datastream解析
在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解析的更多相关文章
- Asp.net Json数据解析的一种思路
在日常的编码中,经常会遇到JSON类型的数据,有简单的,也有复杂的.对于简单的,我们可以用正则等匹配,但是一旦遇到复杂的,就比较难办了. 数据分析 目前手头上需要制作一个天气预报功能,现成的接口已经有 ...
- c#网络通信框架networkcomms内核解析之八 数据包的核心处理器
NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本 gplv3协议 我们先回顾一个 c#网络通信框架networkcomms内核解析之六 处理接收到的二进制 ...
- Asp.net Json 解析 与 直接用ip访问返回josn
数据分析 目前手头上需要制作一个天气预报功能,现成的接口已经有了.我随便输入一个城市,然后出现了如下的信息: {"wdata":{"cityName":&quo ...
- Hadoop学习总结之二:HDFS读写过程解析
一.文件的打开 1.1.客户端 HDFS打开一个文件,需要在客户端调用DistributedFileSystem.open(Path f, int bufferSize),其实现为: public F ...
- Flink解析kafka canal未压平数据为message报错
canal使用非flatmessage方式获取mysql bin log日志发至kafka比直接发送json效率要高很多,数据发到kafka后需要实时解析为json,这里可以使用strom或者flin ...
- Hadoop源码分析(1):HDFS读写过程解析
一.文件的打开 1.1.客户端 HDFS打开一个文件,需要在客户端调用DistributedFileSystem.open(Path f, int bufferSize),其实现为: public F ...
- Apache-Flink深度解析-概述
摘要: Apache Flink 的命脉 "命脉" 即生命与血脉,常喻极为重要的事物.系列的首篇,首篇的首段不聊Apache Flink的历史,不聊Apache Flink的架构, ...
- Apache-Flink深度解析-DataStream-Connectors之Kafka
Kafka 简介 Apache Kafka是一个分布式发布-订阅消息传递系统. 它最初由LinkedIn公司开发,LinkedIn于2010年贡献给了Apache基金会并成为顶级开源项目.Kafka用 ...
- EOS行为核心:解析插件chain_plugin
EOS提供了大量的rpc接口,其中功能性最强,使用最频繁的一部分接口是EOS的行为核心,由chain_api_plugin提供,具体实现是在chain_plugin. 关键字:EOS,区块链,chai ...
随机推荐
- webpack不同devtools打包对比
测试所用的配置文件: const path = require('path'); const HtmlWebpackPlugin= require('html-webpack-plugin'); co ...
- sudo执行提示Command not found
运行一命令在普通用户下可行,切换到root用户依然可行,但在普通用户下使用sudo执行时,提示Command not found. 修改/etc/sudoers文件,找到类似下面的一行: Defaul ...
- ubuntu mysql 配置(远程访问&&字符集设置&&忽略大小写)
1.安装 参考http://www.cnblogs.com/wuhou/archive/2008/09/28/1301071.html sudo apt-get install mysql-serve ...
- The Contiki build system
The Contiki build system http://contiki.sourceforge.net/docs/2.6/a01796.html 先看官方文档的说明,对contiki的构建系统 ...
- C++之封装
希望暴露public 希望隐藏private 对象实例化有两种方式,从栈实例化,从堆(new出来的)实例化. 以谁做什么作为核心. public 放前面,private放后面(属性可以定义为priva ...
- mac下配置java运行环境
1. oracle官网下载java se jdk地址 http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-38 ...
- Listen81
Nut-Cracking Chimps Demonstrate Cultural Differences One family generally dines on Chinese takeout w ...
- hdu-2874 Connections between cities(lca+tarjan+并查集)
题目链接: Connections between cities Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/327 ...
- visual studio code使用MSVC编译C++
环境 OS::Microsoft Windows [Version 10.0.17134.285] x64 VSC:Version:1.27.2 (system setup) VS:2017 心血来潮 ...
- 使用google浏览器模拟手机终端的方法
谷歌Chrome浏览器,可以很方便地用来当移动终端模拟器.在Windows的[开始]-->[运行]中输入以下命令,启动谷歌浏览器,即可模拟相应手机的浏览器去访问3G手机网页,前提:将先前开启的谷 ...