Skip to main content

libraries/eosiolib/core/eosio/datastream.hpp

More...

Namespaces

Name
eosio
eosio::_datastream_detail

Classes

Name
classeosio::datastream
classeosio::datastream< size_t >
structeosio::_datastream_detail::is_datastream
structeosio::_datastream_detail::is_datastream< datastream< T > >

Detailed Description

Copyright: defined in eos/LICENSE

Source code


#pragma once
#include "check.hpp"
#include "varint.hpp"

#include <list>
#include <queue>
#include <vector>
#include <array>
#include <set>
#include <map>
#include <string>
#include <optional>
#include <variant>

#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/include/std_tuple.hpp>

#include <boost/mp11/tuple.hpp>
#include <boost/pfr.hpp>

namespace eosio {

template<typename T>
class datastream {
public:
datastream( T start, size_t s )
:_start(start),_pos(start),_end(start+s){}

inline void skip( size_t s ){ _pos += s; }

inline bool read( char* d, size_t s ) {
eosio::check( size_t(_end - _pos) >= (size_t)s, "datastream attempted to read past the end" );
memcpy( d, _pos, s );
_pos += s;
return true;
}

inline bool write( const char* d, size_t s ) {
eosio::check( _end - _pos >= (int32_t)s, "datastream attempted to write past the end" );
memcpy( (void*)_pos, d, s );
_pos += s;
return true;
}

inline bool write( char d ) {
eosio::check( _end - _pos >= 1, "datastream attempted to write past the end" );
*_pos++ = d;
return true;
}

inline bool write( const void* d, size_t s ) {
eosio::check( _end - _pos >= (int32_t)s, "datastream attempted to write past the end" );
memcpy( (void*)_pos, d, s );
_pos += s;
return true;
}

inline bool put(char c) {
eosio::check( _pos < _end, "put" );
*_pos = c;
++_pos;
return true;
}

inline bool get( unsigned char& c ) { return get( *(char*)&c ); }

inline bool get( char& c )
{
eosio::check( _pos < _end, "get" );
c = *_pos;
++_pos;
return true;
}

T pos()const { return _pos; }
inline bool valid()const { return _pos <= _end && _pos >= _start; }

inline bool seekp(size_t p) { _pos = _start + p; return _pos <= _end; }

inline size_t tellp()const { return size_t(_pos - _start); }

inline size_t remaining()const { return _end - _pos; }
private:
T _start;
T _pos;
T _end;
};

template<>
class datastream<size_t> {
public:
datastream( size_t init_size = 0):_size(init_size){}

inline bool skip( size_t s ) { _size += s; return true; }

inline bool write( const char* ,size_t s ) { _size += s; return true; }

inline bool write( char ) { _size++; return true; }

inline bool write( const void* ,size_t s ) { _size += s; return true; }

inline bool put(char ) { ++_size; return true; }

inline bool valid()const { return true; }

inline bool seekp(size_t p) { _size = p; return true; }

inline size_t tellp()const { return _size; }

inline size_t remaining()const { return 0; }
private:
size_t _size;
};

template<typename Stream, typename T>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const std::list<T>& l) {
ds << unsigned_int( l.size() );
for ( const auto& elem : l )
ds << elem;
return ds;
}

template<typename Stream, typename T>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, std::list<T>& l) {
unsigned_int s;
ds >> s;
l.resize(s.value);
for( auto& i : l )
ds >> i;
return ds;
}

template<typename Stream, typename T>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const std::deque<T>& d) {
ds << unsigned_int( d.size() );
for ( const auto& elem : d )
ds << elem;
return ds;
}

template<typename Stream, typename T>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, std::deque<T>& d) {
unsigned_int s;
ds >> s;
d.resize(s.value);
for( auto& i : d )
ds >> i;
return ds;
}

template<typename Stream, typename... Ts>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const std::variant<Ts...>& var) {
unsigned_int index = var.index();
ds << index;
std::visit([&ds](auto& val){ ds << val; }, var);
return ds;
}

template<int I, typename Stream, typename... Ts>
void deserialize(datastream<Stream>& ds, std::variant<Ts...>& var, int i) {
if constexpr (I < std::variant_size_v<std::variant<Ts...>>) {
if (i == I) {
std::variant_alternative_t<I, std::variant<Ts...>> tmp;
ds >> tmp;
var.template emplace<I>(std::move(tmp));
} else {
deserialize<I+1>(ds,var,i);
}
} else {
eosio::check(false, "invalid variant index");
}
}

template<typename Stream, typename... Ts>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, std::variant<Ts...>& var) {
unsigned_int index;
ds >> index;
deserialize<0>(ds,var,index);
return ds;
}

template<typename Stream, typename T1, typename T2>
datastream<Stream>& operator<<( datastream<Stream>& ds, const std::pair<T1, T2>& t ) {
ds << std::get<0>(t);
ds << std::get<1>(t);
return ds;
}

template<typename Stream, typename T1, typename T2>
datastream<Stream>& operator>>( datastream<Stream>& ds, std::pair<T1, T2>& t ) {
T1 t1;
T2 t2;
ds >> t1;
ds >> t2;
t = std::pair<T1, T2>{t1, t2};
return ds;
}

template<typename Stream, typename T>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const std::optional<T>& opt) {
char valid = opt.has_value();
ds << valid;
if (valid)
ds << *opt;
return ds;
}

template<typename Stream, typename T>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, std::optional<T>& opt) {
char valid = 0;
ds >> valid;
if (valid) {
T val;
ds >> val;
opt = val;
}
return ds;
}


template<typename Stream>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const bool& d) {
return ds << uint8_t(d);
}

template<typename Stream>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, bool& d) {
uint8_t t;
ds >> t;
d = t;
return ds;
}

template<typename Stream>
datastream<Stream>& operator << ( datastream<Stream>& ds, const std::string& v ) {
ds << unsigned_int( v.size() );
if (v.size())
ds.write(v.data(), v.size());
return ds;
}

template<typename Stream>
datastream<Stream>& operator >> ( datastream<Stream>& ds, std::string& v ) {
std::vector<char> tmp;
ds >> tmp;
if( tmp.size() )
v = std::string(tmp.data(),tmp.data()+tmp.size());
else
v = std::string();
return ds;
}

template<typename Stream, typename T, std::size_t N>
datastream<Stream>& operator << ( datastream<Stream>& ds, const std::array<T,N>& v ) {
for( const auto& i : v )
ds << i;
return ds;
}


template<typename Stream, typename T, std::size_t N>
datastream<Stream>& operator >> ( datastream<Stream>& ds, std::array<T,N>& v ) {
for( auto& i : v )
ds >> i;
return ds;
}

namespace _datastream_detail {
template<typename T>
constexpr bool is_pointer() {
return std::is_pointer<T>::value ||
std::is_null_pointer<T>::value ||
std::is_member_pointer<T>::value;
}

template<typename T>
constexpr bool is_primitive() {
return std::is_arithmetic<T>::value ||
std::is_enum<T>::value;
}

/*
* Check if type T is a specialization of datastream
*
* @tparam T - The type to be checked
*/
template<typename T>
struct is_datastream { static constexpr bool value = false; };
template<typename T>
struct is_datastream<datastream<T>> { static constexpr bool value = true; };
}

template<typename Stream, typename T, std::enable_if_t<_datastream_detail::is_pointer<T>()>* = nullptr>
datastream<Stream>& operator >> ( datastream<Stream>& ds, T ) {
static_assert(!_datastream_detail::is_pointer<T>(), "Pointers should not be serialized" );
return ds;
}

template<typename Stream, typename T, std::size_t N,
std::enable_if_t<!_datastream_detail::is_primitive<T>() &&
!_datastream_detail::is_pointer<T>()>* = nullptr>
datastream<Stream>& operator << ( datastream<Stream>& ds, const T (&v)[N] ) {
ds << unsigned_int( N );
for( uint32_t i = 0; i < N; ++i )
ds << v[i];
return ds;
}

template<typename Stream, typename T, std::size_t N,
std::enable_if_t<_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator << ( datastream<Stream>& ds, const T (&v)[N] ) {
ds << unsigned_int( N );
ds.write((char*)&v[0], sizeof(v));
return ds;
}

template<typename Stream, typename T, std::size_t N,
std::enable_if_t<!_datastream_detail::is_primitive<T>() &&
!_datastream_detail::is_pointer<T>()>* = nullptr>
datastream<Stream>& operator >> ( datastream<Stream>& ds, T (&v)[N] ) {
unsigned_int s;
ds >> s;
eosio::check( N == s.value, "T[] size and unpacked size don't match");
for( uint32_t i = 0; i < N; ++i )
ds >> v[i];
return ds;
}

template<typename Stream, typename T, std::size_t N,
std::enable_if_t<_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator >> ( datastream<Stream>& ds, T (&v)[N] ) {
unsigned_int s;
ds >> s;
eosio::check( N == s.value, "T[] size and unpacked size don't match");
ds.read((char*)&v[0], sizeof(v));
return ds;
}

template<typename Stream, typename T,
std::enable_if_t<_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator << ( datastream<Stream>& ds, const std::vector<T>& v ) {
ds << unsigned_int( v.size() );
ds.write( (const void*)v.data(), v.size()*sizeof(T) );
return ds;
}

template<typename Stream, typename T,
std::enable_if_t<!_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator << ( datastream<Stream>& ds, const std::vector<T>& v ) {
ds << unsigned_int( v.size() );
for( const auto& i : v )
ds << i;
return ds;
}

template<typename Stream, typename T,
std::enable_if_t<_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator >> ( datastream<Stream>& ds, std::vector<T>& v ) {
unsigned_int s;
ds >> s;
v.resize( s.value );
ds.read( (char*)v.data(), v.size()*sizeof(T) );
return ds;
}

template<typename Stream, typename T,
std::enable_if_t<!_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator >> ( datastream<Stream>& ds, std::vector<T>& v ) {
unsigned_int s;
ds >> s;
v.resize(s.value);
for( auto& i : v )
ds >> i;
return ds;
}

template<typename Stream, typename T>
datastream<Stream>& operator << ( datastream<Stream>& ds, const std::set<T>& s ) {
ds << unsigned_int( s.size() );
for( const auto& i : s ) {
ds << i;
}
return ds;
}


template<typename Stream, typename T>
datastream<Stream>& operator >> ( datastream<Stream>& ds, std::set<T>& s ) {
s.clear();
unsigned_int sz; ds >> sz;

for( uint32_t i = 0; i < sz.value; ++i ) {
T v;
ds >> v;
s.emplace( std::move(v) );
}
return ds;
}

template<typename Stream, typename K, typename V>
datastream<Stream>& operator << ( datastream<Stream>& ds, const std::map<K,V>& m ) {
ds << unsigned_int( m.size() );
for( const auto& i : m ) {
ds << i.first << i.second;
}
return ds;
}

template<typename Stream, typename K, typename V>
datastream<Stream>& operator >> ( datastream<Stream>& ds, std::map<K,V>& m ) {
m.clear();
unsigned_int s; ds >> s;

for (uint32_t i = 0; i < s.value; ++i) {
K k; V v;
ds >> k >> v;
m.emplace( std::move(k), std::move(v) );
}
return ds;
}

template<typename Stream, typename... Args>
datastream<Stream>& operator<<( datastream<Stream>& ds, const std::tuple<Args...>& t ) {
boost::fusion::for_each( t, [&]( const auto& i ) {
ds << i;
});
return ds;
}

template<typename Stream, typename... Args>
datastream<Stream>& operator>>( datastream<Stream>& ds, std::tuple<Args...>& t ) {
boost::fusion::for_each( t, [&]( auto& i ) {
ds >> i;
});
return ds;
}

template<typename DataStream, typename T, std::enable_if_t<std::is_class<T>::value && _datastream_detail::is_datastream<DataStream>::value>* = nullptr>
DataStream& operator<<( DataStream& ds, const T& v ) {
boost::pfr::for_each_field(v, [&](const auto& field) {
ds << field;
});
return ds;
}

template<typename DataStream, typename T, std::enable_if_t<std::is_class<T>::value && _datastream_detail::is_datastream<DataStream>::value>* = nullptr>
DataStream& operator>>( DataStream& ds, T& v ) {
boost::pfr::for_each_field(v, [&](auto& field) {
ds >> field;
});
return ds;
}

template<typename Stream, typename T, std::enable_if_t<_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator<<( datastream<Stream>& ds, const T& v ) {
ds.write( (const char*)&v, sizeof(T) );
return ds;
}

template<typename Stream, typename T, std::enable_if_t<_datastream_detail::is_primitive<T>()>* = nullptr>
datastream<Stream>& operator>>( datastream<Stream>& ds, T& v ) {
ds.read( (char*)&v, sizeof(T) );
return ds;
}

template<typename T>
T unpack( const char* buffer, size_t len ) {
T result;
datastream<const char*> ds(buffer,len);
ds >> result;
return result;
}

template<typename T>
void unpack( T& res, const char* buffer, size_t len ) {
datastream<const char*> ds(buffer,len);
ds >> res;
}

template<typename T>
T unpack( const std::vector<char>& bytes ) {
return unpack<T>( bytes.data(), bytes.size() );
}

template<typename T>
size_t pack_size( const T& value ) {
datastream<size_t> ps;
ps << value;
return ps.tellp();
}

template<typename T>
std::vector<char> pack( const T& value ) {
std::vector<char> result;
result.resize(pack_size(value));

datastream<char*> ds( result.data(), result.size() );
ds << value;
return result;
}
}

Updated on 2022-12-05 at 15:38:08 +0000