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





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

評論