最新消息:

比特币源码剖析(五)之序列化

Bitcoin justnode 718浏览

比特币序列化功能的实现都在streams.h和serialize.h这两个文件中。

首先我们通过一个demo来演示在比特币代码中如何进行序列化/反序列化操作

CDataStream ss(SER_GETHASH,0);  //实例化CDataStream对象
ss<<obj1; //序列化 ss>>obj2; //反序列化  

接下来我们来看看streams.h文件中的CDataStream类的实现

class CDataStream
{
protected:
    typedef CSerializeData vector_type;
    vector_type vch;
    unsigned int nReadPos;
public:
    ...
    CDataStream& read(char* pch, size_t nSize)
    CDataStream& write(const char* pch, size_t nSize)
    {
        // Write to the end of the buffer
        vch.insert(vch.end(), pch, pch + nSize);
        return (*this);
    }
    template<typename T>
    CDataStream& operator<<(const T& obj)
    {
        // Serialize to this stream
        ::Serialize(*this, obj, nType, nVersion);
        return (*this);
    }

    template<typename T>
    CDataStream& operator>>(T& obj)
    {
        // Unserialize from this stream
        ::Unserialize(*this, obj, nType, nVersion);
        return (*this);
    }
    ...
}

CDataStream类中的vch对象是一个vector容器,用来存放序列化后的数据。write/read函数分别是向vch中写入数据和读数据。CDataStream类中重载了”<<“运算符,在该函数中调用了全局的Serialize方法。Serialize定义在serialize.h文件中。

serialize.h包含了一系列的Serialize的重载函数,包括signed和unsigned char,short,int,long,long long,char,float,double,bool以及string,vector,pair,map,set和CScript。根据传参的类型选择不同的Serialize函数。

下面以int32_t为例,分析调用的过程

template<typename Stream> inline void Serialize(Stream& s, int32_t a,      int, int=0) { ser_writedata32(s, a); }

template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
{
    obj = htole32(obj);
    s.write((char*)&obj, 4);
}

::Serialize()-> Serialize()->s.write(),其中s为CDataStream对象,也就是说最后调用CDataStream中的write,把需要序列化的数据写入CDataStream的vch容器中。

以上的分析是对已知的数据类型进行序列化的过程。那么如果我们要序列化自定义类型的数据,该数据的类型和Serialize的重载函数中的类型都不匹配,那么该如何序列化呢?在serialize.h中有下面一种Serialize函数模板:

template<typename Stream, typename T>
inline void Serialize(Stream& os, const T& a, long nType, int nVersion)
{
    a.Serialize(os, (int)nType, nVersion);
}

当要序列化自定义类型的数据和给定的类型不匹配时,就会调用该Serialize函数。在函数内部会调用对象a自身的Serialize函数。需要注意,对象a就是我们要序列化的对象,所以对于自定义类型的对象要想实现序列化,必须在类内部实现Serialize函数。