本章介绍上一章交易创建后,比特币客户端进行数据序列化的过程。
比特币客户端的所有序列化功能都在seriliaze.h中实现,其中,CDataStream类是数据序列化的核心结构。
尚力财经小编2022
class CDATA stream { protected:typedef vectorvector _ type;vector _ type vch无符号int nReadPos短状态;短例外掩码;public:int nType;转换;//.}vch存储序列化数据。它是一个带有自定义内存分配器的字符容器类型。当需要分配/释放内存时,容器的实现将调用内存分配器。内存分配器会在将内存释放给操作系统之前清除内存中的数据,防止机器的其他进程访问这些数据,从而保证数据存储的安全性。这里不讨论这个内存分配器的实现,但是读者可以在serialize上找到,h NReadPos是vch读取数据的起始位置。是状态错误标识符。此变量用于指示序列化/反序列化中可能出现的错误。Exceptmask是一个错误掩码。它被初始化为ios:badbit | ios:failbit。类似于state,它用于指示错误的种类。nType的值为SER_NETWORK、SER_DISK、SER_GETHASH、SER_SKIPSIG、SER_BLOCKHEADERONLY中的一个,其作用是通知CDataStream执行特定的序列化操作。这五个符号在枚举类型中定义。每个符号都是int类型(4字节),其值是2的幂。enum {//primary actions SER _ NETWORK=(1 0),SER_DISK=(1 1),SER_GETHASH=(1 2),//modifiers SER_SKIPSIG=(1 16),SER_BLOCKHEADERONLY=(1 17),};n是转换数。
CDataStream:read()和CDataStream:write()宏READDATA()和WRITEDATA()
函数CDataStream:read()和CDataStream:write()用于序列化/反序列化原始类型(int、bool、unsigned long等。).为了序列化这些数据类型,指向这些类型的指针将被转换为char*。因为这些类型的大小目前是已知的,所以它们可以从CDataStream中读取或写入字符缓冲区。用于引用这些函数的两个宏被定义为帮助器。# DEFINE WRITE DATA (s,obj) S. WRITE ((char *) (obj),sizeof (obj)) # DEFINE READ DATA (s,obj) S. READ ((char *) (obj),sizeof (obj)) template inline void Serialize(Stream s,unsigned long a,int,int=0) { WRITEDATA(s,a);}用自己的定义替换WRITEDATA(s,a)。接下来是扩展的函数:
Template inline void serialize(streams s,unsigned long a,int,int=0) {s.write ((char *) (a),sizeof(a));}这个函数接受一个无符号长整型参数a,获取它的内存地址,将指针转换为char*并调用函数s.write()。
CDataStream中的运算符
CDataStream重载该运算符以进行序列化和反序列化。模板CDataStream运算符头文件serialize.h包含14个重载的全局函数,用于14种原始类型(char、short、int、long和long long以及char、float、double和bool的有符号和无符号版本)和6种复合类型(string、vector、pair、map、set和CScript)的6个重载版本。因此,对于这些类型,可以简单地使用接下来的代码来序列化/反序列化数据:<(const T& obj) { // Serialize to this stream ::Serialize(*this, obj, nType, nVersion); return (*this); } template CDataStream& operator>CDATA stream ss(SER _ get hash);ssobj 4;//deserialize
如果没有实现的类型匹配第二个参数obj,将调用接下来的泛型T全局函数。对于这个泛型版本,应该使用类型T来实现成员函数和签名T:Serialize(Stream,int,int)。它将通过. Serialize()调用。
如何实现一个类型的序列化
在前面的介绍中,泛型T需要实现以下三个成员函数进行序列化。unsigned int GetSerializeSize(int nType=0,int n VERSION=VERSION)const;void Serialize(Stream s,int nType=0,int n VERSION=VERSION)const;void Unserialize(Stream s,int nType=0,int n VERSION=VERSION);宏IMPLEMENT_SERIALIZE(statements)用于定义这三个函数的任何类型的实现。 # define implements _ serialization(statements) signed int get serialize(int type=0,int n version=version)const { cs generationetserialize ser _ action; const bool fgetsize=true const bool fwrite=false const bool fread=false signed int insertsize=0; s。类型=类型: s。版本=版本: {语句} return nser size } template void serialization(stream s,int ntype=0,int n version=version)const { cseractivationsser _ action; const bool fgetsize=false const bool fwrite=true const bool fread=false signed int insertsize=0; {语句} } 模板无效重新初始化(Stream s,int ntype=0,int nversion=version) n重新初始化ser _ action const bool fgetsize=false const bool fwrite=false const bool fread=true signed int insertsize=0; {语句}}以下例子示范怎样使用该宏。
#include #include "serialize.h"using namespace std;class AClass {public: AClass(int xin) : x(xin){}; int x; IMPLEMENT_SERIALIZE(READWRITE(this->x);)}int main() { CDataStream astream2; AClass aObj(200); //一个x为200的AClass类型对象 cout<<"aObj="endl; asream2a2 cout<<"a2="<这段程序序列化/反序列化AClass对象。它将在屏幕上输出接下来的结果。
aObj=200a2=200AClass的这三个序列化/反序列化成员函数可以在一行代码中实现:
IMPLEMENT_SERIALIZE(READWRITE(this->x);)宏READWRITE()的定义如下#define READWRITE(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)。这里是这个函数的全部三种版本。templateinline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action){ return ::GetSerializeSize(obj, nType, nVersion);}templateinline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action){ ::Serialize(s, obj, nType, nVersion); return 0;}templateinline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action){ ::Unserialize(s, obj,尚力财经小编2022 nType, nVersion); return 0;}如你所见,函数::SerReadWrite()被重载为三种版本。取决于最后一个参数,它将会调分别用全局函数::GetSerialize(),::Serialize()和::Unserialize();这三个函数在前面章节已经介绍。
如果你检查三种不同版本的::SerReadWrite()的最后一个参数,你会发现它们全部为空类型。这三种类型的唯一用途是区别::SerReadWrite()的三个版本,继而被宏IMPLEMENT_SERIALIZE()定义的所有函数使用。