Bitcoin序列化功能主要實現在serialize.h
檔案,整個程式碼主要是圍繞stream
和參與序列化反序列化的型別T展開。
stream這個模板形參表達具有read(char**, size_t)
和write(char**, size_t)
方法的物件, 類似Golang 的io.reader ,io.writer。
簡單的使用例子:
#include <serialize.h>
#include <streams.h>
#include <hash.h>
#include <test/test_bitcoin.h>
#include <stdint.h>
#include <memory>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
struct student
{
std::string name;
double midterm, final;
std::vector<double> homework;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(name);
READWRITE(midterm);
READWRITE(final);
READWRITE(homework);
}
};
bool operator==(student const& lhs, student const& rhs){
return lhs.name == rhs.name &&
lhs.midterm == rhs.midterm &&
lhs.final == rhs.final &&
lhs.homework == rhs.homework;
}
std::ostream& operator<<(std::ostream& os, student const& st){
os << "name: " << st.name << `
`
<< "midterm: " << st.midterm << `
`
<< "final: " << st.final << `
`
<< "homework: " ;
for (auto e : st.homework) {
os << e << ` `;
}
return os;
}
BOOST_AUTO_TEST_CASE(normal)
{
student s, t;
s.name = "john";
s.midterm = 77;
s.final = 82;
auto v = std::vector<double> {83, 50, 10, 88, 65};
s.homework = v;
CDataStream ss(SER_DISK, 0);
ss << s;
ss >> t;
BOOST_CHECK(t.name == "john");
BOOST_CHECK(t.midterm == 77);
BOOST_CHECK(t.final == 82);
BOOST_TEST(t.homework == v, boost::test_tools::per_element());
CDataStream sd(SER_DISK, 0);
CDataStream sn(SER_NETWORK, PROTOCOL_VERSION);
sd << s;
sn << s;
BOOST_CHECK(Hash(sd.begin(), sd.end()) == Hash(sn.begin(), sn.end()));
}
BOOST_AUTO_TEST_CASE(vector)
{
auto vs = std::vector<student>(3);
vs[0].name = "bob";
vs[0].midterm = 90;
vs[0].final = 76;
vs[0].homework = std::vector<double> {85, 53, 12, 75, 55};
vs[1].name = "jim";
vs[1].midterm = 96;
vs[1].final = 72;
vs[1].homework = std::vector<double> {91, 46, 19, 70, 59};
vs[2].name = "tom";
vs[2].midterm = 85;
vs[2].final = 57;
vs[2].homework = std::vector<double> {91, 77, 45, 50, 35};
CDataStream ss(SER_DISK, 0);
auto vt = std::vector<student>(3);
ss << vs;
ss >> vt;
BOOST_TEST(vs == vt, boost::test_tools::per_element());
}
BOOST_AUTO_TEST_CASE(unique_ptr){
auto hex = "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000";
CDataStream stream(ParseHex(hex), SER_NETWORK, PROTOCOL_VERSION);
//CTransaction tx(deserialize, stream);
auto utx = std::unique_ptr<const CTransaction>(nullptr);
::Unserialize(stream, utx);
BOOST_TEST(utx->vin.size() == std::size_t(1));
BOOST_TEST(utx->vout[0].nValue == 1000000);
}
BOOST_AUTO_TEST_SUITE_END()
複製程式碼
需要在使用者的自定義型別內部 新增 ADD_SERIALIZE_METHODS 呼叫, 巨集展開後:
template<typename Stream>
void Serialize(Stream& s) const {
NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize());
}
template<typename Stream>
void Unserialize(Stream& s) {
SerializationOp(s, CSerActionUnserialize());
}
複製程式碼
這個巨集為使用者自定義型別新增了兩個成員函式: Serialize 和 Unserialize, 它們內部呼叫需要使用者自定義的模板成員函式SerializationOp , 在 SerializationOp 函式內部, 主要使用 READWRITE 和 READWRITEMANY 巨集,完成對自定義型別每個資料成員的序列化與反序列化。
#define READWRITE(obj) (::SerReadWrite(s, (obj), ser_action))
#define READWRITEMANY(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
struct CSerActionSerialize
{
constexpr bool ForRead() const { return false; }
};
struct CSerActionUnserialize
{
constexpr bool ForRead() const { return true; }
};
template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
{
::Serialize(s, obj);
}
template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
{
::Unserialize(s, obj);
}
template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, Args&&... args)
{
::SerializeMany(s, std::forward<Args>(args)...);
}
template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&... args)
{
::UnserializeMany(s, args...);
}
複製程式碼
需要在使用者的自定義型別內部 新增 ADD_SERIALIZE_METHODS 呼叫, 巨集展開後:
template<typename Stream>
void Serialize(Stream& s) const {
NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize());
}
template<typename Stream>
void Unserialize(Stream& s) {
SerializationOp(s, CSerActionUnserialize());
}
複製程式碼
這個巨集為使用者自定義型別新增了兩個成員函式: Serialize
和 Unserialize
, 它們內部呼叫需要使用者自定義的模板成員函式SerializationOp
, 在 SerializationOp
函式內部, 主要使用 READWRITE
和 READWRITEMANY
巨集,完成對自定義型別每個資料成員的序列化與反序列化。
#define READWRITE(obj) (::SerReadWrite(s, (obj), ser_action))
#define READWRITEMANY(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
struct CSerActionSerialize
{
constexpr bool ForRead() const { return false; }
};
struct CSerActionUnserialize
{
constexpr bool ForRead() const { return true; }
};
template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
{
::Serialize(s, obj);
}
template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
{
::Unserialize(s, obj);
}
template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, Args&&... args)
{
::SerializeMany(s, std::forward<Args>(args)...);
}
template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&... args)
{
::UnserializeMany(s, args...);
}
複製程式碼
這裡SerReadWrite 和 SerReadWriteMany 各自有兩個overload 實現, 區別是末尾分別傳入了不同的型別CSerActionSerialize
和 CSerActionUnserialize
, 而且 形參 ser_action 根本沒有在內部使用, 查閱了相關資料, 這裡使用了c++ 泛型程式設計常用的一種模式:
tag dispatch 技術](https://akrzemi1.wordpress.com/examples/overloading-tag-dispatch/), 另一個解釋:[https://arne-mertz.de/2016/10/tag-dispatch/)(https://arne-mertz.de/2016/10/tag-dispatch/),
通過攜帶不同的型別,在編譯時選擇不同的overload 實現, CSerActionSerialize 對應序列化的實現, CSerActionUnserialize 對應反序列化的實現。
SerializeMany
和 SerializeMany
是通過變長模板parameter pack 展開技術來實現, 以 SerializeMany
為例子:
template<typename Stream>
void SerializeMany(Stream& s)
{
}
template<typename Stream, typename Arg>
void SerializeMany(Stream& s, Arg&& arg)
{
::Serialize(s, std::forward<Arg>(arg));
}
template<typename Stream, typename Arg, typename... Args>
void SerializeMany(Stream& s, Arg&& arg, Args&&... args)
{
::Serialize(s, std::forward<Arg>(arg));
::SerializeMany(s, std::forward<Args>(args)...);
}
複製程式碼
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的成員函式實現:
template<typename Stream, typename T>
inline void Serialize(Stream& os, const T& a)
{
a.Serialize(os);
}
template<typename Stream, typename T>
inline void Unserialize(Stream& is, T& a)
{
a.Unserialize(is);
}
複製程式碼
在序列化string, map, set, vector, prevector 等可能包含多元素的集合型別時, 內部會呼叫 ReadCompactSize
和 WriteCompactSize
讀取寫入緊湊編碼的元素個數:
template<typename Stream>
void WriteCompactSize(Stream& os, uint64_t nSize)
{
if (nSize < 253)
{
ser_writedata8(os, nSize);
}
else if (nSize <= std::numeric_limits<unsigned short>::max())
{
ser_writedata8(os, 253);
ser_writedata16(os, nSize);
}
else if (nSize <= std::numeric_limits<unsigned int>::max())
{
ser_writedata8(os, 254);
ser_writedata32(os, nSize);
}
else
{
ser_writedata8(os, 255);
ser_writedata64(os, nSize);
}
return;
}
template<typename Stream>
uint64_t ReadCompactSize(Stream& is)
{
uint8_t chSize = ser_readdata8(is);
uint64_t nSizeRet = 0;
if (chSize < 253)
{
nSizeRet = chSize;
}
else if (chSize == 253)
{
nSizeRet = ser_readdata16(is);
if (nSizeRet < 253)
throw std::ios_base::failure("non-canonical ReadCompactSize()");
}
else if (chSize == 254)
{
nSizeRet = ser_readdata32(is);
if (nSizeRet < 0x10000u)
throw std::ios_base::failure("non-canonical ReadCompactSize()");
}
else
{
nSizeRet = ser_readdata64(is);
if (nSizeRet < 0x100000000ULL)
throw std::ios_base::failure("non-canonical ReadCompactSize()");
}
if (nSizeRet > (uint64_t)MAX_SIZE)
throw std::ios_base::failure("ReadCompactSize(): size too large");
return nSizeRet;
}
複製程式碼
針對位寬1,2,4,8的基礎型別,Serialize
和 Unserialize
最終呼叫ser_writedata*, ser_readdata8* 完成實現。
template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint16_t a) { ser_writedata16(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, float a ) { ser_writedata32(s, ser_float_to_uint32(a)); }
template<typename Stream> inline void Serialize(Stream& s, double a ) { ser_writedata64(s, ser_double_to_uint64(a)); }
template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char
template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint16_t& a) { a = ser_readdata16(s); }
template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, float& a ) { a = ser_uint32_to_float(ser_readdata32(s)); }
template<typename Stream> inline void Unserialize(Stream& s, double& a ) { a = ser_uint64_to_double(ser_readdata64(s)); }
template<typename Stream> inline void Serialize(Stream& s, bool a) { char f=a; ser_writedata8(s, f); }
template<typename Stream> inline void Unserialize(Stream& s, bool& a) { char f=ser_readdata8(s); a=f; }
複製程式碼
另外程式碼開始處的
struct deserialize_type {};
constexpr deserialize_type deserialize {};
複製程式碼
作為tag 型別, tag 物件, 主要為多個實現簽名有以下形式:
template <typename Stream>
T::T(deserialize_type, Stream& s)
複製程式碼
的反序列化構造器做分發, 目前主要是CTransaction, CMutableTransaction 型別:
template <typename Stream>
CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
template <typename Stream>
CMutableTransaction(deserialize_type, Stream& s) {
Unserialize(s);
}
複製程式碼
原文地址: mp.weixin.qq.com/s/_fhGCfkI0…
本文由 Copernicus團隊 喻建
寫作,轉載無需授權。