比特币源码技术分析

比特币客户端所有的序列化函数均在seriliaze.h中实现。其中,cdatastream类是数据序列化的核心结构。
cdatastream
cdatastream拥有一个字符类容器用来存放序列化之后的数据。它结合一个容器类型和一个流(stream)界面以处理数据。它使用6个成员函数实现这一功能:
[cpp]view plaincopy
classcdatastream
{
protected:
typedefvectorvector_type;
vector_typevch;
unsignedintnreadpos;
shortstate;
shortexceptmask;
public:
intntype;
intnversion;
//......
}
vch存有序列化后的数据。它是一个拥有自定义内存分配器的字符容器类型。该内存分配器将由该容器的实现在需要分配/释放内存时调用。该内存分配器会在向操作系统释放内存前清空内存中的数据以防止本机的其他进程访问此数据,从而保证数据存储的安全性。该内存分配器的实现在此不进行讨论,读者可于serialize.h自行查找。
nreadpos是vch读取数据的起始位置。
state是错误标识。该变量用于指示在序列化/反序列化当中可能出现的错误。
exceptmask是错误掩码。它初始化为ios::badbit | ios::failbit。与state类似,它被用于指示错误种类。
ntype的取值为ser_network,ser_disk,ser_gethash,ser_skipsig,ser_blockheaderonly之一,其作用为通知cdatastream进行具体某种序列化操作。这5个符号被定义在一个枚举类型enum里。每个符号均为一个int类型(4字节),并且其值为2的次方。
[cpp]view plaincopy
enum
{
//primaryactions
ser_network=(1<<0),
ser_disk=(1<<1),
ser_gethash=(1<<2),
//modifiers
ser_skipsig=(1<<16),
ser_blockheaderonly=(1<=0);
unsignedintnreadposnext=nreadpos+nsize;
if(nreadposnext>=vch.size())
{
if(nreadposnext>vch.size())
{
setstate(ios::failbit,cdatastream::read():endofdata);
memset(pch,0,nsize);
nsize=vch.size()-nreadpos;
}
memcpy(pch,&vch[nreadpos],nsize);
nreadpos=0;
vch.clear();
return(*this);
}
memcpy(pch,&vch[nreadpos],nsize);
nreadpos=nreadposnext;
return(*this);
}
cdatastream&write(constchar*pch,intnsize)
{
//writetotheendofthebuffer
assert(nsize>=0);
vch.insert(vch.end(),pch,pch+nsize);
return(*this);
}
cdatastream::read()从cdatastream复制nsize个字符到一个由char* pch所指向的内存空间。以下是它的实现过程:
计算将要从vch读取的数据的结束位置,unsigned int nreadposnext = nreadpos + nsize。
如果结束位置比vch的大小更大,则当前没有足够的数据供读取。在这种情况下,通过调用函数setstate()将state设为ios::failbit,并将所有的零复制到pch。
否则,调用memcpy(pch, &vch[nreadpos], nsize)复制nsize个字符,从vch的nreadpos位置开始,到由pch指向的一段预先分配的内存。接着从nreadpos向前移至下一个起始位置nreadposnext(第22行)。
该实现表明1)当一段数据被从流中读取之后,该段数据无法被再次读取;2)nreadpos是第一个有效数据的读取位置。
cdatastream::write()非常简单。它将由pch指向的nsize个字符附加到vch的结尾。
宏readdata()和writedata()
函数cdatastream::read()与cdatastream::write()的作用是序列化/反序列化原始类型(int,bool,unsigned long等)。为了序列化这些数据类型,这些类型的指针将被转换为char*。由于这些类型的大小目前已知,它们可以从cdatastream中读取或者写入至字符缓冲。两个用于引用这些函数的宏被定义为助手。
[cpp]view plaincopy
#definewritedata(s,obj)s.write((char*)&(obj),sizeof(obj))
#definereaddata(s,obj)s.read((char*)&(obj),sizeof(obj))
这里是如何使用这些宏的例子。下面的函数将序列化一个unsigned long类型。
[cpp]view plaincopy
[cpp]view plaincopy
templateinlinevoidserialize(stream&s,unsignedlonga,int,int=0){writedata(s,a);}
把writedata(s, a)用自身的定义取代,以下是展开以后的函数:
[cpp]view plaincopy
templateinlinevoidserialize(stream&s,unsignedlonga,int,int=0){s.write((char*)&(a),sizeof(a));}
该函数接受一个unsigned long参数a,获取它的内存地址,转换指针为char*并调用函数s.write()。
cdatastream中的操作符
cdatastream重载了操作符用于序列化和反序列化。
[cpp]view plaincopy
template
cdatastream&operator<>(t&obj)
{
//unserializefromthisstream
::unserialize(*this,obj,ntype,nversion);
return(*this);
}
头文件serialize.h包含了14个重载后的这两个全局函数给14个原始类型(signed和unsigned版本char,short,int,long和long long,以及char,float,double和bool)以及6个重载版本的6个复合类型(string,vector,pair,map,set和cscript)。因此,对于这些类型,你可以简单地使用以下代码来序列化/反序列化数据:
[cpp]view plaincopy
cdatastreamss(ser_gethash);
ss<obj3>>obj4;//反序列化
如果没有任何实现的类型符合第二个参数obj,则以下泛型t全局函数将会被调用。
[cpp]view plaincopy
template
inlinevoidserialize(stream&os,constt&a,longntype,intnversion=version)
{
a.serialize(os,(int)ntype,nversion);
}
对于该泛型版本,类型t应该用于实现一个成员函数和签名t::serialize(stream, int, int)。它将通过a.serialize()被调用。
怎样实现一个类型的序列化
在之前的介绍当中,泛型t需要实现以下三个成员函数进行序列化。
[cpp]view plaincopy
unsignedintgetserializesize(intntype=0,intnversion=version)const;
voidserialize(stream&s,intntype=0,intnversion=version)const;
voidunserialize(stream&s,intntype=0,intnversion=version);
这三个函数将由它们相对应的带泛型t的全局函数调用。这些全局函数则由cdatastream中重载的操作符调用。
一个宏implement_serialize(statements)用于定义任意类型的这三个函数的实现。
[cpp]view plaincopy
#defineimplement_serialize(statements)\
unsignedintgetserializesize(intntype=0,intnversion=version)const\
{\
cseractiongetserializesizeser_action;\
constboolfgetsize=true;\
constboolfwrite=false;\
constboolfread=false;\
unsignedintnsersize=0;\
ser_streamplaceholders;\
s.ntype=ntype;\
s.nversion=nversion;\
{statements}\
returnnsersize;\
}\
template\
voidserialize(stream&s,intntype=0,intnversion=version)const\
{\
cseractionserializeser_action;\
constboolfgetsize=false;\
constboolfwrite=true;\
constboolfread=false;\
unsignedintnsersize=0;\
{statements}\
}\
template\
voidunserialize(stream&s,intntype=0,intnversion=version)\
{\
cseractionunserializeser_action;\
constboolfgetsize=false;\
constboolfwrite=false;\
constboolfread=true;\
unsignedintnsersize=0;\
{statements}\
}
以下例子示范怎样使用该宏。
[cpp]view plaincopy
#include
#includeserialize.h
usingnamespacestd;
classaclass{
public:
aclass(intxin):x(xin){};
intx;
implement_serialize(readwrite(this->x);)
}
intmain(){
cdatastreamastream2;
aclassaobj(200);//一个x为200的aclass类型对象
cout< asream2<>a2
cout< 宏readwrite()的定义如下
[cpp]view plaincopy
#definereadwrite(obj)(nsersize+=::serreadwrite(s,(obj),ntype,nversion,ser_action))
该宏的展开被放在宏implement_serialize(statements)的全部三个函数里。因此,它一次需要完成三件事情:1)返回序列化后数据的大小,2)序列化(写入)数据至流;3)从流中反序列化(读取)数据。参考宏implement_serialize(statements)中对这三个函数的定义。
想要了解宏readwrite(obj)怎样工作,你首先需要明白它的完整形式当中的nsersize,s,
ntype,nversion和ser_action是怎么来的。它们全部来自宏
implement_serialize(statements)的三个函数主体部分:
nsersize是一个unsigned int,在三个函数当中初始化为0;
ser_action是一个对象在三个函数当中均有声明,但为三种不同类型。它在三个函数当中
分别为cseractiongetserializesize、cseractionserialize和
cseractionunserialize;
s在第一个函数中定义为ser_streamplaceholder类型。它是第一个传入至另外两个函数
的参数,拥有参数类型stream;
ntype和nversion在三个函数中均为传入参数。
因此,一旦宏readwrite()扩展至宏implement_serialize(),所有它的符号都将被计算,
因为它们已经存在于宏implement_serialize()的主体中。readwrite(obj)的扩展调用
一个全局函数::serreadwrite(s, (obj), ntype, nversion, ser_action)。
这里是这个函数的全部三种版本。
[cpp]view plaincopy
template
inlineunsignedintserreadwrite(stream&s,constt&obj,intntype,intnversion,cseractiongetserializesizeser_action)
{
return::getserializesize(obj,ntype,nversion);
}
template
inlineunsignedintserreadwrite(stream&s,constt&obj,intntype,intnversion,cseractionserializeser_action)
{
::serialize(s,obj,ntype,nversion);
return0;
}
template
inlineunsignedintserreadwrite(stream&s,t&obj,intntype,intnversion,cseractionunserializeser_action)
{
::unserialize(s,obj,ntype,nversion);
return0;
}
如你所见,函数::serreadwrite()被重载为三种版本。取决于最后一个参数,它将会调分别用全局函数::getserialize(),::serialize()和::unserialize();这三个函数在前面章节已经介绍。
如果你检查三种不同版本的::serreadwrite()的最后一个参数,你会发现它们全部为空类型。
这三种类型的唯一用途是区别::serreadwrite()的三个版本,
继而被宏implement_serialize()定义的所有函数使用。

OLED屏幕的缺点是什么以及弹片微针模组的作用
再见MP3,你好AAC:MP3专利授权走到终点 相关项目已经终结
谁之过?高通中国合资公司宣告破产!
电路板挂钟的制作教程
Google Assistant帮你打CALL Android P自带系统级人工智能
比特币源码技术分析
锂离子电池放电曲线基础知识详解
机器人移动底盘的功能特点
PCB设计应用中如何抑制电磁干扰
工信部副部长陈肇雄表示提速降费的意义具体体现在三个方面
EtherCAT转Modbus网关的 EtherCAT从站配置案例
如何模拟用于波浪能发电的直线电机或发电机
协同电路保护方案使通信设备免受损害
华为麒麟990系列发布,最强AI+5G手机芯片出炉!
剖析发动机抖动七大原因
市值蒸发将近1.4 万亿港元的腾讯怎么了
浅谈FPC贴片加工产品验收要点
颠覆IoT行业的开发神器!涂鸦智能重磅推出TuyaOS操作系统
台积电在建工厂突发大火
PT-6012D胶带标签高温型持粘性测试仪简介