在EOS的eosiolib模块中有一个datasteam.hpp文件,它几乎实现了所有类型对字节流的转换,是一个非常强大的工具类,在这里对它的做一个简单的提取,也加强一下自己对它的理解。在下面的工程中有三个头文件与EOS源码对应如下:

  #include"datastream.h"     -->  \eos\contracts\eosiolib\datastream.hpp  //去掉了异常,改为代码处理
  #include"serialize.h"    -->  \eos\contracts\eosiolib\serialize.hpp //未修改
#include"varint.h"      -->  \eos\contracts\eosiolib\varint.hpp   //未修改 示例代码编译环境: ubuntu 16.04 boost 1.67 Qt Creator

下面我们从我的简单实例进行分析,相信理解了这部分原题,eos中的datastream也自己可以进行修改了。

先来看看我们需要实现的功能,main函数如下编写:

 #include <iostream>
#include<vector>
#include<functional>
#include<algorithm>
#include <iterator>
#include<string>
#include<vector>
#include<set>
#include<map>
#include"datastream.h"
#include"serialize.h" class CBase{
public:
std::string m_strBase = "bright";
std::vector<char> m_vcBase; EOSLIB_SERIALIZE( CBase, (m_strBase)(m_vcBase) )
}; class CDerive:public CBase {
public:
std::string m_strDerive = "Derive";
std::set<std::string> m_derSet; EOSLIB_SERIALIZE_DERIVED( CDerive, CBase, (m_strDerive) (m_derSet) )
}; void printderive(const CDerive& derive)
{
std::cout << derive.m_strBase.data() << " " << derive.m_strDerive.data() << std::endl;
copy(derive.m_vcBase.begin(), derive.m_vcBase.end(), std::ostream_iterator<char>(std::cout, " "));
std::cout << "\n";
copy(derive.m_derSet.begin(), derive.m_derSet.end(), std::ostream_iterator<std::string>(std::cout, " "));
} int main()
{
CDerive derive;
derive.m_vcBase = { 'a', 'b' , 'c', 'd', 'e', 'f', 'g', 'h', 'i'};
derive.m_derSet = {"one", "two", "three", "four", "five"}; printderive(derive); bytes packed_derive = pack(derive);
size_t size = packed_derive.size();
int32_t data = ; std::cout << "\n";
for(bytes::iterator iter = packed_derive.begin(); iter !=packed_derive.end(); ++iter)
{
data = *iter;
if(data <'a')
{
std::cout << data << " ";
}
else
{
std::cout << *iter << " ";
}
} std::cout << "\n"; CDerive copy_derive;
copy_derive = unpack<CDerive>(packed_derive);
printderive(copy_derive); return ;
}

  运行后的打印信息如下

bright Derive
a b c d e f g h i
five four one three two
6 b r i g h t 9 a b c d e f g h i 6 68 e r i v e 5 4 f i v e 4 f o u r 3 o n e 5 t h r e e 3 t w o
bright Derive
a b c d e f g h i
five four one three two

  从第四行的打印信息我们可以清楚地看到类对象的数据变成了一种格式:长度+内容。所有的数据从基数开始依次被放入到流中,在前面加入了长度,而对于容器类型std::set<std::string>,首先会记录set的实际数据长度,再记录string的长度。因此,在我里我们就可以联想到,任何一种数据我们都可以按照自己的想法去转化成datastream,如map,tuple,deque及自定义类型。那在上面的功能中最关键的为两个宏:EOSLIB_SERIALIZE与EOSLIB_SERIALIZE_DERIVED。 他们如何实现的呢?请看serialize.h文件的实现:

 #ifndef SERIALIZE_H
#define SERIALIZE_H
#include<iostream>
#include<string>
#include"datastream.h"
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#include<boost/preprocessor/seq/for_each.hpp> #define EOSLIB_REFLECT_MEMBER_OP( r, OP, elem ) \
OP t.elem #define EOSLIB_SERIALIZE( TYPE, MEMBERS ) \
template<typename DataStream> \
friend DataStream& operator << ( DataStream& ds, const TYPE& t ){ \
return ds BOOST_PP_SEQ_FOR_EACH( EOSLIB_REFLECT_MEMBER_OP, <<, MEMBERS );\
}\
template<typename DataStream> \
friend DataStream& operator >> ( DataStream& ds, TYPE& t ){ \
return ds BOOST_PP_SEQ_FOR_EACH( EOSLIB_REFLECT_MEMBER_OP, >>, MEMBERS );\
} #define EOSLIB_SERIALIZE_DERIVED( TYPE, BASE, MEMBERS ) \
template<typename DataStream> \
friend DataStream& operator << ( DataStream& ds, const TYPE& t ){ \
ds << static_cast<const BASE&>(t); \
return ds BOOST_PP_SEQ_FOR_EACH( EOSLIB_REFLECT_MEMBER_OP, <<, MEMBERS );\
}\
template<typename DataStream> \
friend DataStream& operator >> ( DataStream& ds, TYPE& t ){ \
ds >> static_cast<BASE&>(t); \
return ds BOOST_PP_SEQ_FOR_EACH( EOSLIB_REFLECT_MEMBER_OP, >>, MEMBERS );\
} #endif // SERIALIZE_H

把main.cpp文件用上面的宏替换,可以看出其实就是在每个类中实现了自己的输入输出流。其中 BOOST_PP_SEQ_FOR_EACH( EOSLIB_REFLECT_MEMBER_OP, >>, MEMBERS ); 的意思是把对象的所需要的多个成员变量按宏依次展开。比如在CBase中的此句

  ds BOOST_PP_SEQ_FOR_EACH( EOSLIB_REFLECT_MEMBER_OP, <<, MEMBERS );

会替换成:

  ds << CBase.m_strBase << CBase.m_vcBase ;

因为返回值为datastream,故会循环调用直至无成员变量。另外,还有以下两个特点:

1.派生类的会调用基类的,直到最底层的基类(多重继承我没有测试过);

2.在ds << CBase.m_strBase << CBase.m_vcBase ; 中,类中的成员变量使用<<和 >>时是需要自己定义类型的转换的,即类中的成员变量和datastream是如何相互转换的,这也是我们接下来讨论的问题。

datastream的实现如下:

 #ifndef DATASTREAM_H
#define DATASTREAM_H #include<iostream>
#include<stdint.h>
#include<memory>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#include"serialize.h"
#include"varint.h" 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){
if( size_t(_end - _pos) < (size_t)s )
{
return false;
} memcpy(d, _pos, s);
_pos += s; return true;
} inline bool write(const char* d, size_t s){
if(_end -_pos < (int32_t)s )
{
return false;
}
memcpy((void*)_pos, d, s);
_pos += s; return true;
} inline bool put(char c) {
if(_pos >= _end) {
return false;
}
*_pos = c;
++_pos;
return true;
} inline bool get( unsigned char& c ) { return get( *(char*)&c ); } inline bool get( char& c )
{
if(_pos >= _end)
{
return false;
} 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 = ):_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 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 ; }
private:
size_t _size;
}; typedef std::vector<char> bytes; template<typename DataStream>
DataStream& operator << ( DataStream& ds, const std::string& v ) {
ds << unsigned_int( v.size() );
if (v.size())
ds.write(v.data(), v.size());
return ds;
} template<typename DataStream>
DataStream& operator >> ( DataStream& 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 DataStream, typename T>
DataStream& operator << ( DataStream& ds, const std::vector<T>& v ) {
ds << unsigned_int( v.size() );
for( const auto& i : v )
ds << i;
return ds;
} template<typename DataStream>
DataStream& operator << ( DataStream& ds, const std::vector<char>& v ) {
ds << unsigned_int( v.size() );
ds.write( v.data(), v.size() );
return ds;
} template<typename DataStream, typename T>
DataStream& operator >> ( DataStream& ds, std::vector<T>& v ) {
unsigned_int s;
ds >> s;
v.resize(s.value);
for( auto& i : v )
ds >> i;
return ds;
} template<typename DataStream>
DataStream& operator >> ( DataStream& ds, std::vector<char>& v ) {
unsigned_int s;
ds >> s;
v.resize( s.value );
ds.read( v.data(), v.size() );
return ds;
} template<typename DataStream, typename T>
DataStream& operator << ( DataStream& ds, const std::set<T>& s ) {
ds << unsigned_int( s.size() );
for( const auto& i : s ) {
ds << i;
}
return ds;
} template<typename DataStream, typename T>
DataStream& operator >> ( DataStream& ds, std::set<T>& s ) {
s.clear();
unsigned_int sz; ds >> sz; for( uint32_t i = ; i < sz.value; ++i ) {
T v;
ds >> v;
s.emplace( std::move(v) );
}
return ds;
} template<typename DataStream, typename K, typename V>
DataStream& operator << ( DataStream& 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 DataStream, typename K, typename V>
DataStream& operator >> ( DataStream& ds, std::map<K,V>& m ) {
m.clear();
unsigned_int s; ds >> s; for (uint32_t i = ; i < s.value; ++i) {
K k; V v;
ds >> k >> v;
m.emplace( std::move(k), std::move(v) );
}
return ds;
} template<typename T>
size_t pack_size( const T& value ) {
datastream<size_t> ps;
ps << value;
return ps.tellp();
} template<typename T>
bytes pack( const T& value ) {
bytes result;
result.resize(pack_size(value)); datastream<char*> ds( result.data(), result.size() );
ds << value;
return result;
} 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>
T unpack( const std::vector<char>& bytes ) {
return unpack<T>( bytes.data(), bytes.size() );
} #endif // DATASTREAM_H

上面的datastream文件我只提取了极少的一部分,实际上EOS几乎已经实现了所有类型与datastream的相互转换。datastream用一个泛化版本和特化版本。特化版本中定义了当前使用类型的一个副本并可以使用指针偏移访问任何位置。而我们的CBase,CDerive的成员变量使用了string,vector,set类型,所以我们自己在这里增加了这三个类型与datastream数据流转换函数,如果我们需要使用map类型的成员,那么我们在这个文件里也要增加相应的转换类型,自定义的也是如此。最后,还有一个依赖的头文件内容如下:

 struct unsigned_int {
unsigned_int( uint32_t v = ):value(v){} template<typename T>
unsigned_int( T v ):value(v){} template<typename T>
operator T()const { return static_cast<T>(value); } unsigned_int& operator=( uint32_t v ) { value = v; return *this; } uint32_t value; friend bool operator==( const unsigned_int& i, const uint32_t& v ) { return i.value == v; }
friend bool operator==( const uint32_t& i, const unsigned_int& v ) { return i == v.value; }
friend bool operator==( const unsigned_int& i, const unsigned_int& v ) { return i.value == v.value; } friend bool operator!=( const unsigned_int& i, const uint32_t& v ) { return i.value != v; }
friend bool operator!=( const uint32_t& i, const unsigned_int& v ) { return i != v.value; }
friend bool operator!=( const unsigned_int& i, const unsigned_int& v ) { return i.value != v.value; } friend bool operator<( const unsigned_int& i, const uint32_t& v ) { return i.value < v; }
friend bool operator<( const uint32_t& i, const unsigned_int& v ) { return i < v.value; }
friend bool operator<( const unsigned_int& i, const unsigned_int& v ) { return i.value < v.value; } friend bool operator>=( const unsigned_int& i, const uint32_t& v ) { return i.value >= v; }
friend bool operator>=( const uint32_t& i, const unsigned_int& v ) { return i >= v.value; }
friend bool operator>=( const unsigned_int& i, const unsigned_int& v ) { return i.value >= v.value; }
template<typename DataStream>
friend DataStream& operator << ( DataStream& ds, const unsigned_int& v ){
uint64_t val = v.value;
do {
uint8_t b = uint8_t(val) & 0x7f;
val >>= ;
b |= ((val > ) << );
ds.write((char*)&b,);//.put(b);
} while( val );
return ds;
} template<typename DataStream>
friend DataStream& operator >> ( DataStream& ds, unsigned_int& vi ){
uint64_t v = ; char b = ; uint8_t by = ;
do {
ds.get(b);
v |= uint32_t(uint8_t(b) & 0x7f) << by;
by += ;
} while( uint8_t(b) & 0x80 );
vi.value = static_cast<uint32_t>(v);
return ds;
}
}; struct signed_int {
signed_int( int32_t v = ):value(v){}
operator int32_t()const { return value; }
template<typename T>
signed_int& operator=( const T& v ) { value = v; return *this; }
signed_int operator++(int) { return value++; }
signed_int& operator++(){ ++value; return *this; } int32_t value; friend bool operator==( const signed_int& i, const int32_t& v ) { return i.value == v; }
friend bool operator==( const int32_t& i, const signed_int& v ) { return i == v.value; }
friend bool operator==( const signed_int& i, const signed_int& v ) { return i.value == v.value; } friend bool operator!=( const signed_int& i, const int32_t& v ) { return i.value != v; }
friend bool operator!=( const int32_t& i, const signed_int& v ) { return i != v.value; }
friend bool operator!=( const signed_int& i, const signed_int& v ) { return i.value != v.value; } friend bool operator<( const signed_int& i, const int32_t& v ) { return i.value < v; }
friend bool operator<( const int32_t& i, const signed_int& v ) { return i < v.value; }
friend bool operator<( const signed_int& i, const signed_int& v ) { return i.value < v.value; } friend bool operator>=( const signed_int& i, const int32_t& v ) { return i.value >= v; }
friend bool operator>=( const int32_t& i, const signed_int& v ) { return i >= v.value; }
friend bool operator>=( const signed_int& i, const signed_int& v ) { return i.value >= v.value; } template<typename DataStream>
friend DataStream& operator << ( DataStream& ds, const signed_int& v ){
uint32_t val = uint32_t((v.value<<) ^ (v.value>>));
do {
uint8_t b = uint8_t(val) & 0x7f;
val >>= ;
b |= ((val > ) << );
ds.write((char*)&b,);//.put(b);
} while( val );
return ds;
}
template<typename DataStream>
friend DataStream& operator >> ( DataStream& ds, signed_int& vi ){
uint32_t v = ; char b = ; int by = ;
do {
ds.get(b);
v |= uint32_t(uint8_t(b) & 0x7f) << by;
by += ;
} while( uint8_t(b) & 0x80 );
vi.value = ((v>>) ^ (v>>)) + (v&0x01);
vi.value = v&0x01 ? vi.value : -vi.value;
vi.value = -vi.value;
return ds;
}
};

其中包括一些类型转换以及数据长度的计算,如此简单。

												

datastream解析的更多相关文章

  1. Asp.net Json数据解析的一种思路

    在日常的编码中,经常会遇到JSON类型的数据,有简单的,也有复杂的.对于简单的,我们可以用正则等匹配,但是一旦遇到复杂的,就比较难办了. 数据分析 目前手头上需要制作一个天气预报功能,现成的接口已经有 ...

  2. c#网络通信框架networkcomms内核解析之八 数据包的核心处理器

    NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本  gplv3协议 我们先回顾一个 c#网络通信框架networkcomms内核解析之六 处理接收到的二进制 ...

  3. Asp.net Json 解析 与 直接用ip访问返回josn

    数据分析 目前手头上需要制作一个天气预报功能,现成的接口已经有了.我随便输入一个城市,然后出现了如下的信息: {"wdata":{"cityName":&quo ...

  4. Hadoop学习总结之二:HDFS读写过程解析

    一.文件的打开 1.1.客户端 HDFS打开一个文件,需要在客户端调用DistributedFileSystem.open(Path f, int bufferSize),其实现为: public F ...

  5. Flink解析kafka canal未压平数据为message报错

    canal使用非flatmessage方式获取mysql bin log日志发至kafka比直接发送json效率要高很多,数据发到kafka后需要实时解析为json,这里可以使用strom或者flin ...

  6. Hadoop源码分析(1):HDFS读写过程解析

    一.文件的打开 1.1.客户端 HDFS打开一个文件,需要在客户端调用DistributedFileSystem.open(Path f, int bufferSize),其实现为: public F ...

  7. Apache-Flink深度解析-概述

    摘要: Apache Flink 的命脉 "命脉" 即生命与血脉,常喻极为重要的事物.系列的首篇,首篇的首段不聊Apache Flink的历史,不聊Apache Flink的架构, ...

  8. Apache-Flink深度解析-DataStream-Connectors之Kafka

    Kafka 简介 Apache Kafka是一个分布式发布-订阅消息传递系统. 它最初由LinkedIn公司开发,LinkedIn于2010年贡献给了Apache基金会并成为顶级开源项目.Kafka用 ...

  9. EOS行为核心:解析插件chain_plugin

    EOS提供了大量的rpc接口,其中功能性最强,使用最频繁的一部分接口是EOS的行为核心,由chain_api_plugin提供,具体实现是在chain_plugin. 关键字:EOS,区块链,chai ...

随机推荐

  1. openstack多region介绍与实践

    版权声明:本文为原创文章,转载请注明出处. 概念介绍 所谓openstack多region,就是多套openstack共享一个keystone和horizon.每个区域一套openstack环境,可以 ...

  2. 【css学习整理】浮动,清除

    css内边距属性: padding padding-top right bottom left 如果是两个数字,指的是上下,左右 padding: 10px 20px  上下10  左右20 如果是三 ...

  3. XXL-Job集群

    底层已经实现好了 调度中心集群 调度中心支持集群部署,提升调度系统容灾和可用性. 调度中心集群部署时,几点要求和建议: DB配置保持一致: 登陆账号配置保持一致: 群机器时钟保持一致(单机集群忽视): ...

  4. 国际电话号码的区号mysql数据表

    -- phpMyAdmin SQL Dump-- version 3.5.2-- http://www.phpmyadmin.net---- Host: localhost-- Generation ...

  5. linux 进程学习笔记-运行新进程

    我们知道,当用fork启动一个新进程以后,新进程会复制父进程的大部份内存空间并接着运行父进程中的代码,如果我们使新进程不运行原父进程的代码,转而运行另外一个程序集中的代码,这就相当于启动了一个新程序. ...

  6. 遁入NOIP记

    回归noip啦 给自己定个小目标 500分起步 在这里列一下需要搞的东西OvO 1.算法基础 模拟 贪心 二分 分治 2.搜索 / 记忆化搜索 剪枝 对抗搜索 3.dp 状压 组合数学 树D 单队 D ...

  7. MySQL多个条件以什么表当做主条件表_20161111周五

    前两天有事情 停了2天 数据需求:1.活动日期11.8-11.10订单2.单笔订单购买A类产品 B类产品满足68元.且连续3天下单的用户ID 首先第一个条件很简单,主要是第二个条件 第二个条件是 且 ...

  8. 「LOJ#10068」「一本通 3.1 练习 3」秘密的牛奶运输(次小生成树

    题目描述 Farmer John 要把他的牛奶运输到各个销售点.运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点. 运输的总距离越小,运输的成本也就越低.低成本的运输是 F ...

  9. 【Lintcode】120.Word Ladder

    题目: Given two words (start and end), and a dictionary, find the length of shortest transformation se ...

  10. AIX 7.1上安装Oracle11g

    1. 上传oracle 11g介质到AIX 我下载的介质是aix.ppc64_11gR2_database_1of2.zip和aix.ppc64_11gR2_database_2of2.zip, 执行 ...