比特幣客戶端所有的序列化函數(shù)均在seriliaze.h中實現(xiàn)。其中,CDataStream類是數(shù)據(jù)序列化的核心結(jié)構(gòu)。
CDataStream
CDataStream擁有一個字符類容器用來存放序列化之后的數(shù)據(jù)。它結(jié)合一個容器類型和一個流(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)存時調(diào)用。該內(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是錯誤標(biāo)識。該變量用于指示在序列化/反序列化當(dāng)中可能出現(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復(fù)制nSize個字符到一個由char* pch所指向的內(nèi)存空間。以下是它的實現(xiàn)過程:
計算將要從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個字符,從vch的nReadPos位置開始,到由pch指向的一段預(yù)先分配的內(nèi)存。接著從nReadPos向前移至下一個起始位置nReadPosNext(第22行)。
該實現(xiàn)表明1)當(dāng)一段數(shù)據(jù)被從流中讀取之后,該段數(shù)據(jù)無法被再次讀取;2)nReadPos是第一個有效數(shù)據(jù)的讀取位置。
CDataStream::write()非常簡單。它將由pch指向的nSize個字符附加到vch的結(jié)尾。
宏READDATA()和WRITEDATA()
函數(shù)CDataStream::read()與CDataStream::write()的作用是序列化/反序列化原始類型(int,bool,unsigned long等)。為了序列化這些數(shù)據(jù)類型,這些類型的指針將被轉(zhuǎn)換為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)存地址,轉(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個重載后的這兩個全局函數(shù)給14個原始類型(signed和unsigned版本char,short,int,long和long long,以及char,float,double和bool)以及6個重載版本的6個復(fù)合類型(string,vector,pair,map,set和CScript)。因此,對于這些類型,你可以簡單地使用以下代碼來序列化/反序列化數(shù)據(jù):
[cpp]view plaincopy
CDataStreamss(SER_GETHASH);
ss<
ss>>obj3>>obj4;//反序列化
如果沒有任何實現(xiàn)的類型符合第二個參數(shù)obj,則以下泛型T全局函數(shù)將會被調(diào)用。
[cpp]view plaincopy
template
inlinevoidSerialize(Stream&os,constT&a,longnType,intnVersion=VERSION)
{
a.Serialize(os,(int)nType,nVersion);
}
對于該泛型版本,類型T應(yīng)該用于實現(xiàn)一個成員函數(shù)和簽名T::Serialize(Stream, int, int)。它將通過a.Serialize()被調(diào)用。
怎樣實現(xiàn)一個類型的序列化
在之前的介紹當(dāng)中,泛型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ù)將由它們相對應(yīng)的帶泛型T的全局函數(shù)調(diào)用。這些全局函數(shù)則由CDataStream中重載的操作符<<和>>調(diào)用。
一個宏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對象。它將在屏幕上輸出下面的結(jié)果。
[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)怎樣工作,你首先需要明白它的完整形式當(dāng)中的nSerSize,s,
nType,nVersion和ser_action是怎么來的。它們?nèi)縼碜院?/p>
IMPLEMENT_SERIALIZE(statements)的三個函數(shù)主體部分:
nSerSize是一個unsigned int,在三個函數(shù)當(dāng)中初始化為0;
ser_action是一個對象在三個函數(shù)當(dāng)中均有聲明,但為三種不同類型。它在三個函數(shù)當(dāng)中
分別為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)的擴展調(diào)用
一個全局函數(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ù),它將會調(diào)分別用全局函數(shù)::GetSerialize(),::Serialize()和::Unserialize();這三個函數(shù)在前面章節(jié)已經(jīng)介紹。
如果你檢查三種不同版本的::SerReadWrite()的最后一個參數(shù),你會發(fā)現(xiàn)它們?nèi)繛榭疹愋汀?/p>
這三種類型的唯一用途是區(qū)別::SerReadWrite()的三個版本,
繼而被宏IMPLEMENT_SERIALIZE()定義的所有函數(shù)使用。
-
比特幣
+關(guān)注
關(guān)注
57文章
7006瀏覽量
141309
原文標(biāo)題:比特幣源碼技術(shù)分析-2
文章出處:【微信號:C_Expert,微信公眾號:C語言專家集中營】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
![](https://file1.elecfans.com/web2/M00/84/A9/wKgaomRmD2CAeXvcAADKL00MDLY836.png)
![](https://file1.elecfans.com/web2/M00/84/A9/wKgaomRmD2CAal62AAC-H1j-rKc074.png)
![](https://file1.elecfans.com/web2/M00/84/AD/wKgZomRmD9qAWIY9AAB-e6sr8g4463.png)
![](https://file1.elecfans.com/web2/M00/84/AD/wKgaomRmD9qAez-fAAB1qIe0wuw439.png)
![](https://file1.elecfans.com/web2/M00/84/AD/wKgZomRmD9uAAnFHAACAqe8CF8A866.png)
![](https://file1.elecfans.com/web2/M00/84/AD/wKgZomRmD9yAOw-mAABkHeN7bk0410.png)
時代周刊:為什么比特幣是自由的源泉?
關(guān)于比特幣源碼技術(shù)的分析
比特幣現(xiàn)金B(yǎng)CH才是原始的比特幣區(qū)塊鏈
比特幣價格的上漲推動了比特幣礦業(yè)的利潤
![<b class='flag-5'>比特</b><b class='flag-5'>幣</b>價格的上漲推動了<b class='flag-5'>比特</b><b class='flag-5'>幣</b>礦業(yè)的利潤](https://file.elecfans.com/web1/M00/96/F0/pIYBAF0IU7iANrYsAAB4NAtjFqE656.jpg)
評論