bitcoin 的序列化功能主要实现在 serialize.h 文件,整个代码主要是围绕 stream
和参与序列化反序列化的类型 T 展开。
stream 这个模板形参表达具有 read(char, size_t) 和 write(char, size_t) 方法的对象, 类似 golang 的 io.reader 和 io.writer。
简单的使用例子:
需要在用户的自定义类型内部 添加ADD_SERIALIZE_METHODS;调用, 宏展开后:
这个宏为用户自定义类型添加了两个成员函数:Serialize和Unserialize, 它们内部调用需要用户自定义的模板成员函数SerializationOp, 在SerializationOp函数内部, 主要使用READWRITE和READWRITEMANY宏,完成对自定义类型每个数据成员的序列化与反序列化。
这里SerReadWrite 和 SerReadWriteMany 各自有两个overload 实现, 区别是末尾分别传入了不同的类型CSerActionSerialize和CSerActionUnserialize, 而且 形参 ser_action 根本没有在内部使用, 查阅了相关资料, 这里使用了c++ 泛型编程常用的一种模式:
tag dispatch 技术, 另一个解释,
通过携带不同的类型,在编译时选择不同的overload 实现, CSerActionSerialize 对应序列化的实现, CSerActionUnserialize 对应反序列化的实现。
SerializeMany和SerializeMany是通过变长模板 parameter pack 展开技术来实现, 以SerializeMany为例子:
SerializeMany有三个 overload 实现, 假设从上倒下,分别编号为 1、 2、3; 当我们传入两个以上的实参是, 编译器选择版本 3,版本 3 内部从 parameter pack 弹出一个参数, 然后传给版本 2 调用, 剩下的参数列表,传给版本 3,递归调用, 直到 parameter pack 为空时, 选择版本 1。
迂回这么长, 最终序列化真正使用全局名称空间的Serialize来完成, 反序列化通过调用Unserialize实现。
而Serialize和Unserialize又有一堆的 overload 实现, bitcoin 作者实现一些常见类型的模板特化,比如,std::string, 主要设计表达脚本
的 prevector , std::vector, std::pair, std::map, std::set, std::unique_ptr, std::share_ptr , c++ 的模板匹配根据参数列表的匹配程度选择不同的实现, 优先精准匹配,最后选择类型T的成员函数实现:
在序列化 string, map, set, vector, prevector 等可能包含多元素的集合类型时, 内部会调用ReadCompactSize和WriteCompactSize读取写入紧凑编码的元素个数:
针对位宽 1,2,4,8 的基础类型,Serialize和Unserialize最终调用 ser_writedata, ser_readdata8完成实现。
另外代码开始处的
作为 tag 类型, tag 对象, 主要为多个实现签名有以下形式:
的反序列化构造器做分发, 目前主要是CTransaction, CMutableTransaction 类型:
领取专属 10元无门槛券
私享最新 技术干货