比特幣客戶端所有的序列化函數(shù)均在seriliaze.h中實(shí)現(xiàn)。其中,CDataStream類是數(shù)據(jù)序列化的核心結(jié)構(gòu)。
CDataStream
CDataStream擁有一個(gè)字符類容器用來存放序列化之后的數(shù)據(jù)。它結(jié)合一個(gè)容器類型和一個(gè)流(stream)界面以處理數(shù)據(jù)。它使用6個(gè)成員函數(shù)實(shí)現(xiàn)這一功能:
[cpp]view plaincopy
classCDataStream
{
protected:
typedefvector
vector_typevch;
unsignedintnReadPos;
shortstate;
shortexceptmask;
public:
intnType;
intnVersion;
//......
}
vch存有序列化后的數(shù)據(jù)。它是一個(gè)擁有自定義內(nèi)存分配器的字符容器類型。該內(nèi)存分配器將由該容器的實(shí)現(xiàn)在需要分配/釋放內(nèi)存時(shí)調(diào)用。該內(nèi)存分配器會(huì)在向操作系統(tǒng)釋放內(nèi)存前清空內(nèi)存中的數(shù)據(jù)以防止本機(jī)的其他進(jìn)程訪問此數(shù)據(jù),從而保證數(shù)據(jù)存儲(chǔ)的安全性。該內(nèi)存分配器的實(shí)現(xiàn)在此不進(jìn)行討論,讀者可于serialize.h自行查找。
nReadPos是vch讀取數(shù)據(jù)的起始位置。
state是錯(cuò)誤標(biāo)識(shí)。該變量用于指示在序列化/反序列化當(dāng)中可能出現(xiàn)的錯(cuò)誤。
exceptmask是錯(cuò)誤掩碼。它初始化為ios::badbit | ios::failbit。與state類似,它被用于指示錯(cuò)誤種類。
nType的取值為SER_NETWORK,SER_DISK,SER_GETHASH,SER_SKIPSIG,SER_BLOCKHEADERONLY之一,其作用為通知CDataStream進(jìn)行具體某種序列化操作。這5個(gè)符號(hào)被定義在一個(gè)枚舉類型enum里。每個(gè)符號(hào)均為一個(gè)int類型(4字節(jié)),并且其值為2的次方。
[cpp]view plaincopy
enum
{
SER_NETWORK=(1<
SER_DISK=(1<
SER_GETHASH=(1<
//modifiers
SER_SKIPSIG=(1<
SER_BLOCKHEADERONLY=(1<
};
nVersion是版本號(hào)。
CDataStream::read()與CDataStream::write()
成員函數(shù)CDataStream::read()和CDataStream::write()是用于執(zhí)行序列化/反序列化CDataStream對(duì)象的低級(jí)函數(shù)。
[cpp]view plaincopy
CDataStream&read(char*pch,intnSize)
{
//Readfromthebeginningofthebuffer
assert(nSize>=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復(fù)制nSize個(gè)字符到一個(gè)由char* pch所指向的內(nèi)存空間。以下是它的實(shí)現(xiàn)過程:
計(jì)算將要從vch讀取的數(shù)據(jù)的結(jié)束位置,unsigned int nReadPosNext = nReadPos + nSize。
如果結(jié)束位置比vch的大小更大,則當(dāng)前沒有足夠的數(shù)據(jù)供讀取。在這種情況下,通過調(diào)用函數(shù)setState()將state設(shè)為ios::failbit,并將所有的零復(fù)制到pch。
否則,調(diào)用memcpy(pch, &vch[nReadPos], nSize)復(fù)制nSize個(gè)字符,從vch的nReadPos位置開始,到由pch指向的一段預(yù)先分配的內(nèi)存。接著從nReadPos向前移至下一個(gè)起始位置nReadPosNext(第22行)。
該實(shí)現(xiàn)表明1)當(dāng)一段數(shù)據(jù)被從流中讀取之后,該段數(shù)據(jù)無法被再次讀??;2)nReadPos是第一個(gè)有效數(shù)據(jù)的讀取位置。
CDataStream::write()非常簡單。它將由pch指向的nSize個(gè)字符附加到vch的結(jié)尾。
宏READDATA()和WRITEDATA()
函數(shù)CDataStream::read()與CDataStream::write()的作用是序列化/反序列化原始類型(int,bool,unsigned long等)。為了序列化這些數(shù)據(jù)類型,這些類型的指針將被轉(zhuǎn)換為char*。由于這些類型的大小目前已知,它們可以從CDataStream中讀取或者寫入至字符緩沖。兩個(gè)用于引用這些函數(shù)的宏被定義為助手。
[cpp]view plaincopy
#defineWRITEDATA(s,obj)s.write((char*)&(obj),sizeof(obj))
#defineREADDATA(s,obj)s.read((char*)&(obj),sizeof(obj))
這里是如何使用這些宏的例子。下面的函數(shù)將序列化一個(gè)unsigned long類型。
[cpp]view plaincopy
[cpp]view plaincopy
template
把WRITEDATA(s, a)用自身的定義取代,以下是展開以后的函數(shù):
[cpp]view plaincopy
template
該函數(shù)接受一個(gè)unsigned long參數(shù)a,獲取它的內(nèi)存地址,轉(zhuǎn)換指針為char*并調(diào)用函數(shù)s.write()。
CDataStream中的操作符 << 和 >>
CDataStream重載了操作符<< 和 >>用于序列化和反序列化。
[cpp]view plaincopy
template
CDataStream&operator<<(const?T&?obj)??
{
//Serializetothisstream
::Serialize(*this,obj,nType,nVersion);
return(*this);
}
template
CDataStream&operator>>(T&obj)
{
//Unserializefromthisstream
::Unserialize(*this,obj,nType,nVersion);
return(*this);
}
頭文件serialize.h包含了14個(gè)重載后的這兩個(gè)全局函數(shù)給14個(gè)原始類型(signed和unsigned版本char,short,int,long和long long,以及char,float,double和bool)以及6個(gè)重載版本的6個(gè)復(fù)合類型(string,vector,pair,map,set和CScript)。因此,對(duì)于這些類型,你可以簡單地使用以下代碼來序列化/反序列化數(shù)據(jù):
[cpp]view plaincopy
CDataStreamss(SER_GETHASH);
ss<
ss>>obj3>>obj4;//反序列化
如果沒有任何實(shí)現(xiàn)的類型符合第二個(gè)參數(shù)obj,則以下泛型T全局函數(shù)將會(huì)被調(diào)用。
[cpp]view plaincopy
template
inlinevoidSerialize(Stream&os,constT&a,longnType,intnVersion=VERSION)
{
a.Serialize(os,(int)nType,nVersion);
}
對(duì)于該泛型版本,類型T應(yīng)該用于實(shí)現(xiàn)一個(gè)成員函數(shù)和簽名T::Serialize(Stream, int, int)。它將通過a.Serialize()被調(diào)用。
怎樣實(shí)現(xiàn)一個(gè)類型的序列化
在之前的介紹當(dāng)中,泛型T需要實(shí)現(xiàn)以下三個(gè)成員函數(shù)進(jìn)行序列化。
[cpp]view plaincopy
unsignedintGetSerializeSize(intnType=0,intnVersion=VERSION)const;
voidSerialize(Stream&s,intnType=0,intnVersion=VERSION)const;
voidUnserialize(Stream&s,intnType=0,intnVersion=VERSION);
這三個(gè)函數(shù)將由它們相對(duì)應(yīng)的帶泛型T的全局函數(shù)調(diào)用。這些全局函數(shù)則由CDataStream中重載的操作符<<和>>調(diào)用。
一個(gè)宏IMPLEMENT_SERIALIZE(statements)用于定義任意類型的這三個(gè)函數(shù)的實(shí)現(xiàn)。
[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
#include"serialize.h"
usingnamespacestd;
classAClass{
public:
AClass(intxin):x(xin){};
intx;
IMPLEMENT_SERIALIZE(READWRITE(this->x);)
}
intmain(){
CDataStreamastream2;
AClassaObj(200);//一個(gè)x為200的AClass類型對(duì)象
cout<<"aObj="<
asream2<
AClassa2(1);//另一個(gè)x為1的對(duì)象
astream2>>a2
cout<<"a2="<
return0;
}
這段程序序列化/反序列化AClass對(duì)象。它將在屏幕上輸出下面的結(jié)果。
[cpp]view plaincopy
aObj=200
a2=200
AClass的這三個(gè)序列化/反序列化成員函數(shù)可以在一行代碼中實(shí)現(xiàn):
IMPLEMENT_SERIALIZE(READWRITE(this->x);)
宏READWRITE()的定義如下
[cpp]view plaincopy
#defineREADWRITE(obj)(nSerSize+=::SerReadWrite(s,(obj),nType,nVersion,ser_action))
該宏的展開被放在宏IMPLEMENT_SERIALIZE(statements)的全部三個(gè)函數(shù)里。因此,它一次需要完成三件事情:1)返回序列化后數(shù)據(jù)的大小,2)序列化(寫入)數(shù)據(jù)至流;3)從流中反序列化(讀?。?shù)據(jù)。參考宏IMPLEMENT_SERIALIZE(statements)中對(duì)這三個(gè)函數(shù)的定義。
想要了解宏READWRITE(obj)怎樣工作,你首先需要明白它的完整形式當(dāng)中的nSerSize,s,
nType,nVersion和ser_action是怎么來的。它們?nèi)縼碜院?/p>
IMPLEMENT_SERIALIZE(statements)的三個(gè)函數(shù)主體部分:
nSerSize是一個(gè)unsigned int,在三個(gè)函數(shù)當(dāng)中初始化為0;
ser_action是一個(gè)對(duì)象在三個(gè)函數(shù)當(dāng)中均有聲明,但為三種不同類型。它在三個(gè)函數(shù)當(dāng)中
分別為CSerActionGetSerializeSize、CSerActionSerialize和
CSerActionUnserialize;
s在第一個(gè)函數(shù)中定義為ser_streamplaceholder類型。它是第一個(gè)傳入至另外兩個(gè)函數(shù)
的參數(shù),擁有參數(shù)類型Stream;
nType和nVersion在三個(gè)函數(shù)中均為傳入?yún)?shù)。
因此,一旦宏READWRITE()擴(kuò)展至宏IMPLEMENT_SERIALIZE(),所有它的符號(hào)都將被計(jì)算,
因?yàn)樗鼈円呀?jīng)存在于宏IMPLEMENT_SERIALIZE()的主體中。READWRITE(obj)的擴(kuò)展調(diào)用
一個(gè)全局函數(shù)::SerReadWrite(s, (obj), nType, nVersion, ser_action)。
這里是這個(gè)函數(shù)的全部三種版本。
[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;
}
如你所見,函數(shù)::SerReadWrite()被重載為三種版本。取決于最后一個(gè)參數(shù),它將會(huì)調(diào)分別用全局函數(shù)::GetSerialize(),::Serialize()和::Unserialize();這三個(gè)函數(shù)在前面章節(jié)已經(jīng)介紹。
如果你檢查三種不同版本的::SerReadWrite()的最后一個(gè)參數(shù),你會(huì)發(fā)現(xiàn)它們?nèi)繛榭疹愋汀?/p>
這三種類型的唯一用途是區(qū)別::SerReadWrite()的三個(gè)版本,
繼而被宏IMPLEMENT_SERIALIZE()定義的所有函數(shù)使用。
-
比特幣
+關(guān)注
關(guān)注
57文章
7006瀏覽量
142140
原文標(biāo)題:比特幣源碼技術(shù)分析-2
文章出處:【微信號(hào):C_Expert,微信公眾號(hào):C語言專家集中營】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦





時(shí)代周刊:為什么比特幣是自由的源泉?
比特幣能炒嗎?比特幣是少數(shù)人的玩具 7%的比特幣在4%的參與者手中
比特幣是不是電子貨幣_(tái)比特幣怎么交易
關(guān)于比特幣源碼技術(shù)的分析
比特幣現(xiàn)金B(yǎng)CH才是原始的比特幣區(qū)塊鏈
比特幣價(jià)格的上漲推動(dòng)了比特幣礦業(yè)的利潤

評(píng)論