@ -1,3 +1,26 @@
/*
This file is part of cpp - ethereum .
cpp - ethereum is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Foobar is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Foobar . If not , see < http : //www.gnu.org/licenses/>.
*/
/** @file RLP.h
* @ author Gav Wood < i @ gavwood . com >
* @ date 2014
*
* RLP ( de - ) serialisation .
*/
# pragma once
# include <exception>
@ -55,13 +78,13 @@ public:
/// List value.
bool isList ( ) const { assert ( ! isNull ( ) ) ; return m_data [ 0 ] > = 0x80 & & m_data [ 0 ] < 0xc0 ; }
/// Integer value. Either isSlimInt() or isBigInt().
/// Integer value. Either isSlimInt(), isFatInt() 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 toHuge Int() ).
/// Fits into eth::uint type. Can use toSlimInt() to read (as well as toFatInt() or toBig Int() ).
bool isSlimInt ( ) const { assert ( ! isNull ( ) ) ; return m_data [ 0 ] < 0x20 ; }
/// Fits only into eth::u256 type. Use only toFatInt() or toBigInt() to read.
/// Fits into eth::u256 or eth::bigint type. Use only toFatInt() or toBigInt() to read.
bool isFatInt ( ) const { assert ( ! isNull ( ) ) ; return m_data [ 0 ] > = 0x20 & & m_data [ 0 ] < 0x38 ; }
/// Fits into eth::u256 type, though might fit into eth::uint type.
@ -77,32 +100,26 @@ public:
/// @returns the number of characters in the string, or zero if it isn't a string.
uint stringSize ( ) const { return isString ( ) ? items ( ) : 0 ; }
/// Equality operators; does best-effort conversion and checks for equality.
bool operator = = ( char const * _s ) const { return isString ( ) & & toString ( ) = = _s ; }
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 ( ) ;
if ( _i < m_lastIndex )
{
m_lastEnd = RLP ( payload ( ) ) . actualSize ( ) ;
m_lastItem = payload ( ) . cropped ( 0 , m_lastEnd ) ;
m_lastIndex = 0 ;
}
for ( ; m_lastIndex < _i ; + + m_lastIndex )
{
m_lastItem = payload ( ) . cropped ( m_lastEnd ) ;
m_lastItem = m_lastItem . cropped ( 0 , RLP ( m_lastItem ) . actualSize ( ) ) ;
m_lastEnd + = m_lastItem . size ( ) ;
}
return RLP ( m_lastItem ) ;
}
bool operator ! = ( std : : string const & _s ) const { return isString ( ) & & toString ( ) ! = _s ; }
bool operator = = ( uint const & _i ) const { return ( isInt ( ) | | isString ( ) ) & & toSlimInt ( ) = = _i ; }
bool operator ! = ( uint const & _i ) const { return ( isInt ( ) | | isString ( ) ) & & toSlimInt ( ) ! = _i ; }
bool operator = = ( u256 const & _i ) const { return ( isInt ( ) | | isString ( ) ) & & toFatInt ( ) = = _i ; }
bool operator ! = ( u256 const & _i ) const { return ( isInt ( ) | | isString ( ) ) & & toFatInt ( ) ! = _i ; }
bool operator = = ( bigint const & _i ) const { return ( isInt ( ) | | isString ( ) ) & & toBigInt ( ) = = _i ; }
bool operator ! = ( bigint const & _i ) const { return ( isInt ( ) | | isString ( ) ) & & toBigInt ( ) ! = _i ; }
/// Subscript operator.
/// @returns the list item @a _i if isList() and @a _i < listItems(), or RLP() otherwise.
/// @note if used to access items in ascending order, this is efficient.
RLP operator [ ] ( uint _i ) const ;
typedef RLP element_type ;
/// @brief Iterator class for iterating through items of RLP list.
class iterator
{
friend class RLP ;
@ -111,18 +128,7 @@ public:
typedef RLP value_type ;
typedef RLP element_type ;
iterator & operator + + ( )
{
if ( m_remaining )
{
m_lastItem . retarget ( m_lastItem . next ( ) . data ( ) , m_remaining ) ;
m_lastItem = m_lastItem . cropped ( 0 , RLP ( m_lastItem ) . actualSize ( ) ) ;
m_remaining - = std : : min < uint > ( m_remaining , m_lastItem . size ( ) ) ;
}
else
m_lastItem . retarget ( m_lastItem . next ( ) . data ( ) , 0 ) ;
return * this ;
}
iterator & operator + + ( ) ;
iterator operator + + ( int ) { auto ret = * this ; operator + + ( ) ; return ret ; }
RLP operator * ( ) const { return RLP ( m_lastItem ) ; }
bool operator = = ( iterator const & _cmp ) const { return m_lastItem = = _cmp . m_lastItem ; }
@ -130,41 +136,31 @@ public:
private :
iterator ( ) { }
iterator ( RLP const & _parent , bool _begin )
{
if ( _begin )
{
auto pl = _parent . payload ( ) ;
m_lastItem = pl . cropped ( 0 , RLP ( pl ) . actualSize ( ) ) ;
m_remaining = pl . size ( ) - m_lastItem . size ( ) ;
}
else
{
m_lastItem = _parent . data ( ) . cropped ( _parent . data ( ) . size ( ) ) ;
m_remaining = 0 ;
}
}
iterator ( RLP const & _parent , bool _begin ) ;
uint m_remaining = 0 ;
bytesConstRef m_lastItem ;
} ;
friend class iterator ;
/// @brief Iterator into beginning of sub-item list (valid only if we are a list).
iterator begin ( ) const { return iterator ( * this , true ) ; }
/// @brief Iterator into end of sub-item list (valid only if we are a list).
iterator end ( ) const { return iterator ( * this , false ) ; }
/// Best-effort conversion operators.
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 ( ) ;
}
/// Converts to string. @returns the empty string if not a string.
std : : string toString ( ) const { if ( ! isString ( ) ) return std : : string ( ) ; return payload ( ) . cropped ( 0 , items ( ) ) . toString ( ) ; }
/// Converts to string. @throws BadCast if not a string.
std : : string toStringStrict ( ) const { if ( ! isString ( ) ) throw BadCast ( ) ; return payload ( ) . cropped ( 0 , items ( ) ) . toString ( ) ; }
/// Converts to int of type given; if isString(), decodes as big-endian bytestream. @returns 0 if not an int or string.
template < class _T = uint > _T toInt ( ) const
{
if ( ! isString ( ) & & ! isInt ( ) )
@ -179,26 +175,29 @@ public:
return ret ;
}
/// Converts to eth::uint. @see toInt()
uint toSlimInt ( ) const { return toInt < uint > ( ) ; }
/// Converts to eth::u256. @see toInt()
u256 toFatInt ( ) const { return toInt < u256 > ( ) ; }
/// Converts to eth::bigint. @see toInt()
bigint toBigInt ( ) const { return toInt < bigint > ( ) ; }
/// Converts to eth::uint. @throws BadCast if not isInt(). @see toInt()
uint toSlimIntStrict ( ) const { if ( ! isSlimInt ( ) ) throw BadCast ( ) ; return toInt < uint > ( ) ; }
/// Converts to eth::u256. @throws BadCast if not isInt(). @see toInt()
u256 toFatIntStrict ( ) const { if ( ! isFatInt ( ) & & ! isSlimInt ( ) ) throw BadCast ( ) ; return toInt < u256 > ( ) ; }
/// Converts to eth::bigint. @throws BadCast if not isInt(). @see toInt()
bigint toBigIntStrict ( ) const { if ( ! isInt ( ) ) throw BadCast ( ) ; return toInt < bigint > ( ) ; }
/// Converts to eth::uint using the toString() as a big-endian bytestream. @throws BadCast if not isString(). @see toInt()
uint toSlimIntFromString ( ) const { if ( ! isString ( ) ) throw BadCast ( ) ; return toInt < uint > ( ) ; }
/// Converts to eth::u256 using the toString() as a big-endian bytestream. @throws BadCast if not isString(). @see toInt()
u256 toFatIntFromString ( ) const { if ( ! isString ( ) ) throw BadCast ( ) ; return toInt < u256 > ( ) ; }
/// Converts to eth::bigint using the toString() as a big-endian bytestream. @throws BadCast if not isString(). @see toInt()
bigint toBigIntFromString ( ) const { if ( ! isString ( ) ) throw BadCast ( ) ; return toInt < bigint > ( ) ; }
RLPs toList ( ) const
{
RLPs ret ;
if ( ! isList ( ) )
return ret ;
uint64_t c = items ( ) ;
for ( uint64_t i = 0 ; i < c ; + + i )
ret . push_back ( operator [ ] ( i ) ) ;
return ret ;
}
/// Converts to RLPs collection object. Useful if you need random access to sub items or will iterate over multiple times.
RLPs toList ( ) const ;
private :
/// Direct value integer.
@ -216,136 +215,67 @@ private:
/// Direct-length list.
bool isSmallList ( ) const { assert ( ! isNull ( ) ) ; return m_data [ 0 ] > = 0x80 & & m_data [ 0 ] < 0xb8 ; }
uint actualSize ( ) const
{
if ( isNull ( ) )
return 0 ;
if ( isInt ( ) )
return 1 + intSize ( ) ;
if ( isString ( ) )
return payload ( ) . data ( ) - m_data . data ( ) + items ( ) ;
if ( isList ( ) )
{
bytesConstRef d = payload ( ) ;
uint64_t c = items ( ) ;
for ( uint64_t i = 0 ; i < c ; + + i , d = d . cropped ( RLP ( d ) . actualSize ( ) ) ) { }
return d . data ( ) - m_data . data ( ) ;
}
return 0 ;
}
/// @returns the theoretical size of this item; if it's a list, will require a deep traversal which could take a while.
/// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work.
uint actualSize ( ) const ;
/// @returns the total additional bytes used to encode the integer. Includes the data-size and potentially the length-size. Returns 0 if not isInt().
uint intSize ( ) const { return ( ! isInt ( ) | | isDirectValueInt ( ) ) ? 0 : isIndirectAddressedInt ( ) ? lengthSize ( ) + items ( ) : ( m_data [ 0 ] - 0x17 ) ; }
/// @returns the bytes used to encode the length of the data. Valid for all types.
uint lengthSize ( ) const { auto n = ( m_data [ 0 ] & 0x3f ) ; return n > 0x37 ? n - 0x37 : 0 ; }
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 ;
}
bytesConstRef payload ( ) const
{
assert ( isString ( ) | | isList ( ) ) ;
auto n = ( m_data [ 0 ] & 0x3f ) ;
return m_data . cropped ( 1 + ( n < 0x38 ? 0 : ( n - 0x37 ) ) ) ;
}
/// @returns the number of data items (bytes in the case of strings & ints, items in the case of lists). Valid for all types.
uint items ( ) const ;
/// @returns the data payload. Valid for all types.
bytesConstRef payload ( ) const { auto n = ( m_data [ 0 ] & 0x3f ) ; return m_data . cropped ( 1 + ( n < 0x38 ? 0 : ( n - 0x37 ) ) ) ; }
/// Our byte data.
bytesConstRef m_data ;
/// The list-indexing cache.
mutable uint m_lastIndex = ( uint ) - 1 ;
mutable uint m_lastEnd ;
mutable uint m_lastEnd = 0 ;
mutable bytesConstRef m_lastItem ;
} ;
struct RLPList { RLPList ( uint _count ) : count ( _count ) { } uint count ; } ;
/**
* @ brief Class for writing to an RLP bytestream .
*/
class RLPStream
{
public :
/// Initializes empty RLPStream.
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 ; }
/// Initializes the RLPStream as a list of @a _listItems items.
explicit RLPStream ( uint _listItems ) { appendList ( _listItems ) ; }
/// Append given data to the byte stream.
RLPStream & append ( uint _s ) ;
RLPStream & append ( u256 _s ) ;
RLPStream & append ( bigint _s ) ;
RLPStream & append ( std : : string const & _s ) ;
RLPStream & appendList ( uint _count ) ;
/// Shift operators for appending data items.
RLPStream & operator < < ( uint _i ) { return append ( _i ) ; }
RLPStream & operator < < ( u256 _i ) { return append ( _i ) ; }
RLPStream & operator < < ( bigint _i ) { return append ( _i ) ; }
RLPStream & operator < < ( char const * _s ) { return append ( std : : string ( _s ) ) ; }
RLPStream & operator < < ( std : : string const & _s ) { return append ( _s ) ; }
template < class _T > RLPStream & operator < < ( std : : vector < _T > const & _s ) { appendList ( _s . size ( ) ) ; for ( auto const & i : _s ) append ( i ) ; return * this ; }
/// Read the byte stream.
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 ( ( byte ) _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 ) ;
}
}
/// Push the node-type byte (using @a _base) along with the item count @a _count.
/// @arg _count is number of characters for strings, data-bytes for ints, or items for lists.
void pushCount ( uint _count , byte _base ) ;
/// Push an integer as a raw big-endian byte-stream.
template < class _T > void pushInt ( _T _i , uint _br )
{
m_out . resize ( m_out . size ( ) + _br ) ;
@ -354,13 +284,7 @@ private:
* ( 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 ) ;
}
/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
template < class _T > static uint bytesRequired ( _T _i )
{
_i > > = 8 ;
@ -369,93 +293,29 @@ private:
return i ;
}
/// Our output byte stream.
bytes m_out ;
} ;
template < class _T > void rlpListAux ( RLPStream & _out , _T _t )
{
_out < < _t ;
}
template < class _T > void rlpListAux ( RLPStream & _out , _T _t ) { _out < < _t ; }
template < class _T , class . . . _Ts > void rlpListAux ( RLPStream & _out , _T _t , _Ts . . . _ts ) { rlpListAux ( _out < < _t , _ts . . . ) ; }
template < class _T , class . . . _Ts > void rlpListAux ( RLPStream & _out , _T _t , _Ts . . . _ts )
{
_out < < _t ;
rlpListAux ( _out , _ts . . . ) ;
}
template < class _T > std : : string rlp ( _T _t )
{
RLPStream out ;
out < < _t ;
return out . str ( ) ;
}
template < class _T > bytes rlpBytes ( _T _t )
{
RLPStream out ;
out < < _t ;
return out . out ( ) ;
}
template < class . . . _Ts > std : : string rlpList ( _Ts . . . _ts )
{
RLPStream out ;
out < < RLPList ( sizeof . . . ( _Ts ) ) ;
rlpListAux ( out , _ts . . . ) ;
return out . str ( ) ;
}
/// Export a single item in RLP format, returning a byte array.
template < class _T > bytes rlp ( _T _t ) { return ( RLPStream ( ) < < _t ) . out ( ) ; }
template < class . . . _Ts > bytes rlpListBytes ( _Ts . . . _ts )
/// Export a list of items in RLP format, returning a byte array.
inline bytes rlpList ( ) { return RLPStream ( 0 ) . out ( ) ; }
template < class . . . _Ts > bytes rlpList ( _Ts . . . _ts )
{
RLPStream out ;
out < < RLPList ( sizeof . . . ( _Ts ) ) ;
RLPStream out ( sizeof . . . ( _Ts ) ) ;
rlpListAux ( out , _ts . . . ) ;
return out . out ( ) ;
}
/// The empty string in RLP format.
extern bytes RLPNull ;
}
inline std : : string escaped ( std : : string const & _s , bool _all = true )
{
std : : string ret ;
ret . reserve ( _s . size ( ) ) ;
ret . push_back ( ' " ' ) ;
for ( auto i : _s )
if ( i = = ' " ' & & ! _all )
ret + = " \\ \" " ;
else if ( i = = ' \\ ' & & ! _all )
ret + = " \\ \\ " ;
else if ( i < ' ' | | i > 127 | | _all )
{
ret + = " \\ x " ;
ret . push_back ( " 0123456789abcdef " [ ( uint8_t ) i / 16 ] ) ;
ret . push_back ( " 0123456789abcdef " [ ( uint8_t ) i % 16 ] ) ;
}
else
ret . push_back ( i ) ;
ret . push_back ( ' " ' ) ;
return ret ;
}
inline std : : ostream & operator < < ( std : : ostream & _out , eth : : RLP _d )
{
if ( _d . isNull ( ) )
_out < < " null " ;
else if ( _d . isInt ( ) )
_out < < std : : showbase < < std : : hex < < std : : nouppercase < < _d . toBigInt ( ) ;
else if ( _d . isString ( ) )
_out < < escaped ( _d . toString ( ) , true ) ;
else if ( _d . isList ( ) )
{
_out < < " [ " ;
int j = 0 ;
for ( auto i : _d )
_out < < ( j + + ? " , " : " " ) < < i ;
_out < < " ] " ;
}
return _out ;
}
/// Human readable version of RLP.
std : : ostream & operator < < ( std : : ostream & _out , eth : : RLP _d ) ;