You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

315 lines
8.3 KiB

#pragma once
#include <iostream>
#include "foreign.h"
#include "Common.h"
namespace eth
{
class RLP;
typedef std::vector<RLP> RLPs;
11 years ago
/**
* @brief Class for interpreting Recursive Linear-Prefix Data.
11 years ago
* @by Gav Wood, 2013
*
* Class for reading byte arrays of data in RLP format.
*/
class RLP
{
public:
11 years ago
/// Construct a null node.
RLP() {}
11 years ago
/// Construct a node of value given in the bytes.
explicit RLP(fConstBytes _d): m_data(_d) {}
11 years ago
/// Construct a node of value given in the bytes.
explicit RLP(bytes const& _d): m_data(const_cast<bytes*>(&_d)) {} // a bit horrible, but we know we won't be altering the data. TODO: allow vector<T> const* to be passed to foreign<T const>.
11 years ago
/// Construct a node to read RLP data in the bytes given.
RLP(byte const* _b, uint _s): m_data(fConstBytes(_b, _s)) {}
11 years ago
/// Construct a node to read RLP data in the string.
explicit RLP(std::string const& _s): m_data(fConstBytes((byte const*)_s.data(), _s.size())) {}
11 years ago
/// @returns true if the RLP is non-null.
explicit operator bool() const { return !isNull(); }
/// No value.
bool isNull() const { return m_data.size() == 0; }
/// Contains a zero-length string or zero-length list.
bool isEmpty() const { return m_data[0] == 0x40 || m_data[0] == 0x80; }
/// String value.
bool isString() const { assert(!isNull()); return m_data[0] >= 0x40 && m_data[0] < 0x80; }
/// List value.
bool isList() const { assert(!isNull()); return m_data[0] >= 0x80 && m_data[0] < 0xc0; }
/// Integer value. Either isSlimInt() or isBigInt().
bool isInt() const { assert(!isNull()); return m_data[0] < 0x40; }
/// Fits into eth::uint type. Can use toInt() to read (as well as toBigInt() or toHugeInt() ).
bool isSlimInt() const { assert(!isNull()); return m_data[0] < 0x20; }
/// Fits only into eth::u256 type. Use only toFatInt() or toBigInt() to read.
bool isFatInt() const { assert(!isNull()); return m_data[0] >= 0x20 && m_data[0] < 0x38; }
/// Fits only into eth::bigint type. Use only toBigInt() to read.
bool isBigInt() const { assert(!isNull()); return m_data[0] >= 0x38 && m_data[0] < 0x40; }
/// @returns the number of items in the list, or zero if it isn't a list.
uint itemCount() const { return isList() ? items() : 0; }
/// @returns the number of characters in the string, or zero if it isn't a string.
uint stringSize() const { return isString() ? items() : 0; }
bool operator==(char const* _s) const { return isString() && toString() == _s; }
bool operator==(std::string const& _s) const { return isString() && toString() == _s; }
bool operator==(uint const& _i) const { return toSlimInt() == _i; }
bool operator==(u256 const& _i) const { return toFatInt() == _i; }
bool operator==(bigint const& _i) const { return toBigInt() == _i; }
RLP operator[](uint _i) const
{
if (!isList() || itemCount() <= _i)
return RLP();
fConstBytes d = payload();
for (uint64_t i = 0; i < _i; ++i, d = d.cropped(RLP(d).size())) {}
return RLP(d);
}
explicit operator std::string() const { return toString(); }
explicit operator RLPs() const { return toList(); }
explicit operator uint() const { return toSlimInt(); }
explicit operator u256() const { return toFatInt(); }
explicit operator bigint() const { return toBigInt(); }
std::string toString() const
{
if (!isString())
return std::string();
return payload().cropped(0, items()).toString();
}
template <class _T = uint> _T toInt(_T _def = 0) const
{
if (!isInt())
return _def;
if (isDirectValueInt())
return m_data[0];
_T ret = 0;
auto s = intSize() - intLengthSize();
uint o = intLengthSize() + 1;
for (uint i = 0; i < s; ++i)
ret = (ret << 8) | m_data[i + o];
return ret;
}
uint toSlimInt(uint _def = 0) const { return toInt<uint>(_def); }
u256 toFatInt(u256 _def = 0) const { return toInt<u256>(_def); }
bigint toBigInt(bigint _def = 0) const { return toInt<bigint>(_def); }
RLPs toList() const
{
RLPs ret;
if (!isList())
return ret;
uint64_t c = items();
fConstBytes d = payload();
for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).size()))
ret.push_back(RLP(d));
return ret;
}
private:
/// Direct value integer.
bool isDirectValueInt() const { assert(!isNull()); return m_data[0] < 0x18; }
/// Indirect-value integer.
bool isIndirectValueInt() const { assert(!isNull()); return m_data[0] >= 0x18 && m_data[0] < 0x38; }
/// Indirect addressed integer.
bool isIndirectAddressedInt() const { assert(!isNull()); return m_data[0] < 0x40 && m_data[0] >= 0x38; }
/// Direct-length string.
bool isSmallString() const { assert(!isNull()); return m_data[0] >= 0x40 && m_data[0] < 0x78; }
/// Direct-length list.
bool isSmallList() const { assert(!isNull()); return m_data[0] >= 0x80 && m_data[0] < 0xb8; }
uint size() const
{
if (isInt())
return 1 + intSize();
if (isString())
return payload().data() - m_data.data() + items();
if (isList())
{
fConstBytes d = payload();
uint64_t c = items();
for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).size())) {}
return d.data() - m_data.data();
}
return 0;
}
uint intLengthSize() const { return isIndirectAddressedInt() ? m_data[0] - 0x37 : 0; }
uint intSize() const { return (!isInt() || isDirectValueInt()) ? 0 : isIndirectAddressedInt() ? intLengthSize() + items() : (m_data[0] - 0x17); }
uint items() const
{
auto n = (m_data[0] & 0x3f);
if (n < 0x38)
return n;
uint ret = 0;
for (int i = 0; i < n - 0x37; ++i)
ret = (ret << 8) | m_data[i + 1];
return ret;
}
fConstBytes payload() const
{
assert(isString() || isList());
auto n = (m_data[0] & 0x3f);
return m_data.cropped(1 + (n < 0x38 ? 0 : (n - 0x37)));
}
fConstBytes m_data;
};
struct RLPList { RLPList(uint _count): count(_count) {} uint count; };
class RLPStream
{
public:
RLPStream() {}
void append(uint _s) { appendNumeric(_s); }
void append(u256 _s) { appendNumeric(_s); }
void append(bigint _s) { appendNumeric(_s); }
void append(std::string const& _s)
{
if (_s.size() < 0x38)
m_out.push_back(_s.size() | 0x40);
else
pushCount(_s.size(), 0x40);
uint os = m_out.size();
m_out.resize(os + _s.size());
memcpy(m_out.data() + os, _s.data(), _s.size());
}
void appendList(uint _count)
{
if (_count < 0x38)
m_out.push_back(_count | 0x80);
else
pushCount(_count, 0x80);
}
RLPStream operator<<(uint _i) { append(_i); return *this; }
RLPStream operator<<(u256 _i) { append(_i); return *this; }
RLPStream operator<<(bigint _i) { append(_i); return *this; }
RLPStream operator<<(char const* _s) { append(std::string(_s)); return *this; }
RLPStream operator<<(std::string const& _s) { append(_s); return *this; }
RLPStream operator<<(RLPList _l) { appendList(_l.count); return *this; }
bytes const& out() const { return m_out; }
std::string str() const { return std::string((char const*)m_out.data(), (char const*)(m_out.data() + m_out.size())); }
private:
void appendNumeric(uint _i)
{
if (_i < 0x18)
m_out.push_back(_i);
else
{
auto br = bytesRequired(_i);
m_out.push_back(br + 0x17); // max 8 bytes.
pushInt(_i, br);
}
}
void appendNumeric(u256 _i)
{
if (_i < 0x18)
m_out.push_back(_i);
else
{
auto br = bytesRequired(_i);
m_out.push_back(br + 0x17); // max 8 bytes.
pushInt(_i, br);
}
}
void appendNumeric(bigint _i)
{
if (_i < 0x18)
m_out.push_back((byte)_i);
else
{
uint br = bytesRequired(_i);
if (br <= 32)
m_out.push_back(bytesRequired(_i) + 0x17); // max 32 bytes.
else
{
auto brbr = bytesRequired(br);
m_out.push_back(0x37 + brbr);
pushInt(br, brbr);
}
pushInt(_i, br);
}
}
template <class _T> void pushInt(_T _i, uint _br)
{
m_out.resize(m_out.size() + _br);
byte* b = &m_out.back();
for (; _i; _i >>= 8)
*(b--) = (byte)_i;
}
void pushCount(uint _count, byte _base)
{
auto br = bytesRequired(_count);
m_out.push_back(br + 0x37 + _base); // max 8 bytes.
pushInt(_count, br);
}
template <class _T> static uint bytesRequired(_T _i)
{
_i >>= 8;
uint i = 1;
for (; _i != 0; ++i, _i >>= 8) {}
return i;
}
bytes m_out;
};
}
inline std::ostream& operator<<(std::ostream& _out, eth::RLP _d)
{
if (_d.isNull())
_out << "null";
else if (_d.isInt())
_out << _d.toBigInt();
else if (_d.isString())
_out << "\"" << _d.toString() << "\"";
else if (_d.isList())
{
_out << "[";
int j = 0;
for (auto i: _d.toList())
_out << (j++ ? ", " : " ") << i;
_out << " ]";
}
return _out;
}