精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

比特幣源碼技術分析

C語言專家集中營 ? 來源:未知 ? 作者:李建兵 ? 2018-03-16 17:19 ? 次閱讀

比特幣客戶端所有的序列化函數均在seriliaze.h中實現。其中,CDataStream類是數據序列化的核心結構。

CDataStream

CDataStream擁有一個字符類容器用來存放序列化之后的數據。它結合一個容器類型和一個流(stream)界面以處理數據。它使用6個成員函數實現這一功能:

[cpp]view plaincopy

classCDataStream

{

protected:

typedefvector>vector_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<

SER_DISK=(1<

SER_GETHASH=(1<

//modifiers

SER_SKIPSIG=(1<

SER_BLOCKHEADERONLY=(1<

};

nVersion是版本號。

CDataStream::read()與CDataStream::write()

成員函數CDataStream::read()和CDataStream::write()是用于執行序列化/反序列化CDataStream對象的低級函數。

[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所指向的內存空間。以下是它的實現過程:

計算將要從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<<(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個重載后的這兩個全局函數給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<

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

#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="<>endl;

asream2<

AClassa2(1);//另一個x為1的對象

astream2>>a2

cout<<"a2="<

return0;

}

這段程序序列化/反序列化AClass對象。它將在屏幕上輸出下面的結果。

[cpp]view plaincopy

aObj=200

a2=200

AClass的這三個序列化/反序列化成員函數可以在一行代碼中實現:

IMPLEMENT_SERIALIZE(READWRITE(this->x);)

宏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()定義的所有函數使用。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 比特幣
    +關注

    關注

    57

    文章

    7002

    瀏覽量

    140330

原文標題:比特幣源碼技術分析-2

文章出處:【微信號:C_Expert,微信公眾號:C語言專家集中營】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    #硬聲創作季 區塊鏈:1.1認識比特

    區塊鏈比特
    Mr_haohao
    發布于 :2022年10月16日 23:44:12

    #硬聲創作季 區塊鏈:1.6比特的分叉

    區塊鏈比特
    Mr_haohao
    發布于 :2022年10月16日 23:48:32

    #硬聲創作季 區塊鏈與加:1.2比特的來源

    區塊鏈比特
    Mr_haohao
    發布于 :2022年10月17日 09:42:07

    #硬聲創作季 區塊鏈與加:1.3比特是什么

    區塊鏈比特
    Mr_haohao
    發布于 :2022年10月17日 09:42:44

    #硬聲創作季 區塊鏈與加:1.6比特分叉

    區塊鏈比特
    Mr_haohao
    發布于 :2022年10月17日 09:44:29

    #硬聲創作季 區塊鏈與加:2.4比特回顧

    區塊鏈比特
    Mr_haohao
    發布于 :2022年10月17日 09:47:11

    究竟比特是什么

    。  然而,比特卻在大洋彼岸贏得了肯定,華爾街最大的機構之一——美銀美林宣布其研究范圍正式覆蓋比特,并對比特
    發表于 12-15 11:17

    時代周刊:為什么比特是自由的源泉?

    銀行賬戶,但他無法凍結比特錢包;在難民營,你可能無法找到一家銀行,但只要有網絡,你就可以收到比特,你不需要獲得任何人的批準,也不用證明自己的身份。
    發表于 01-01 23:23

    萊特比特的區別

    本文詳細的介紹了萊特比特的相關概念,其中包括了萊特礦池介紹、比特特征和
    發表于 01-09 11:21 ?6525次閱讀

    比特是不是電子貨幣_比特怎么交易

    本文開始詳細的介紹了比特的特征,其次介紹了比特的幾個易平臺和分析比特
    發表于 01-30 16:25 ?1w次閱讀

    關于比特源碼技術分析

    為了存儲、搜索、讀取在內存與磁盤中的區塊和交易信息,比特引入了一些訪問類。它們包括:CBlockIndex和CDiskBlockIndex用于索引區塊,CDiskTxPos和CTxIndex用于索引交易。
    的頭像 發表于 04-02 16:55 ?5236次閱讀

    比特現金BCH才是原始的比特區塊鏈

    美國國家標準和技術研究所(NIST ):比特是一個分叉,BCH才是最初的區塊鏈。NIST 于2018年1月29日發布了一份詳細的比特和區
    發表于 11-08 14:19 ?1513次閱讀

    比特價格的上漲推動了比特礦業的利潤

    加密貨幣分析師亞歷克斯克魯格(Alex Kruger)表示,有效的比特開采業務目前的盈虧平衡點在3550美元至4350美元之間,而在撰寫本文時比特
    發表于 06-18 10:59 ?671次閱讀
    <b class='flag-5'>比特</b><b class='flag-5'>幣</b>價格的上漲推動了<b class='flag-5'>比特</b><b class='flag-5'>幣</b>礦業的利潤

    比特比特現金的區別是什么

    比特核心(BTC)的支持者說,比特不是用來買咖啡的。他們認為比特的目的是作為一種價值儲備,
    發表于 12-27 09:27 ?2655次閱讀

    Twitter的比特情感分析器怎樣來構建

    我們的比特情緒分析儀功能完善,為進一步分析比特情緒提供了良好的基礎。
    發表于 01-12 10:49 ?1158次閱讀