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 ...
随机推荐
- cmd 环境变量设置方法详细解释
cmd设置环境变量可以方便我们bat脚本的运行,但是要注意的是变量只在当前的cmd窗口有作用(局部生效),如果想要设置持久的环境变量需要我们通过两种手段进行设置:一种是直接修改注册表,另一种是通过我的 ...
- NET LOCALGROUP命令详解(将用户添加到管理员组等)
NET LOCALGROUP [groupname [/COMMENT:"text"]] [/DOMAIN] groupname {/ADD [/COMMENT:"tex ...
- JavaUtil_03_图片处理工具类
一.源码 功能:缩放图像.切割图像.图像类型转换.彩色转黑白.文字水印.图片水印等 package com.ray.dingtalk.util; import java.awt.AlphaCompos ...
- perl 语言学习总结
.#!/usr/bin/perl -w 内建警告信息,Perl发出警告 .字符串 . 连接符 .重复次数 .字符串与数字之间的自动转换 .; + += *= .= not and or xor .pr ...
- linux 进程学习笔记-信号semaphore
信号灯(信号量)不是进程通信手段,其是用于控制和协调在进程间通信过程中的共享资源访问,就如同互斥锁(两者的区别可以参考这里) 可以将简单地将信号灯想象成一个计数器,初始时计数器值为n(有n个资源可供使 ...
- 【Lintcode】033.N-Queens
题目: The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two que ...
- xpath技术,用在dom4j中
title: xPath语法应用 tags: xPath,dom4j grammar_cjkRuby: true --- 在dom4j中,会使用到xPath技术. 在项目中导入 jaxen-1.1-b ...
- [poj3450]Corporate Identity(后缀数组)
题意:多个字符串的最长公共子串. 解题关键:字符串的任何一个子串都是这个字符串的某个后缀的前缀.求A和B的最长公共子串等价于求A的后缀和B的后缀的最长公共前缀的最大值. 后缀数组的经典例题,连接在一起 ...
- Programming With Objective-C---- Introduction ---- Objective-C 学习(一)
About Objective-C Objective-C is the primary programming language you use when writing software for ...
- java之Date(日期)、Date格式化、Calendar(日历)
参考http://how2j.cn/k/date/date-date/346.html Date(日期) Date类 注意:是java.util.Date; 而非 java.sql.Date,此类是给 ...