From ca62563df341786d1d1809a037d8b592924e78c4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 8 Jan 2020 08:56:19 -0800 Subject: [PATCH 1/3] Add a generic approach for (de)serialization of objects using code in other classes This adds the (internal) Wrapper class, and the Using function that uses it. Given a class F that implements Ser(stream, const object&) and Unser(stream, object&) functions, this permits writing e.g. READWRITE(Using(object)). --- src/serialize.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/serialize.h b/src/serialize.h index c9e994f8440..fd8626007a0 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -442,6 +442,32 @@ I ReadVarInt(Stream& is) } } +/** Simple wrapper class to serialize objects using a formatter; used by Using(). */ +template +class Wrapper +{ + static_assert(std::is_lvalue_reference::value, "Wrapper needs an lvalue reference type T"); +protected: + T m_object; +public: + explicit Wrapper(T obj) : m_object(obj) {} + template void Serialize(Stream &s) const { Formatter().Ser(s, m_object); } + template void Unserialize(Stream &s) { Formatter().Unser(s, m_object); } +}; + +/** Cause serialization/deserialization of an object to be done using a specified formatter class. + * + * To use this, you need a class Formatter that has public functions Ser(stream, const object&) for + * serialization, and Unser(stream, object&) for deserialization. Serialization routines (inside + * READWRITE, or directly with << and >> operators), can then use Using(object). + * + * This works by constructing a Wrapper-wrapped version of object, where T is + * const during serialization, and non-const during deserialization, which maintains const + * correctness. + */ +template +static inline Wrapper Using(T&& t) { return Wrapper(t); } + #define VARINT(obj, ...) WrapVarInt<__VA_ARGS__>(REF(obj)) #define COMPACTSIZE(obj) CCompactSize(REF(obj)) #define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj)) From 2f1b2f4ed044fe005e5a6c1b55e95822e83c16df Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 8 Jan 2020 09:05:44 -0800 Subject: [PATCH 2/3] Convert VARINT to the formatter/Using approach --- src/serialize.h | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/serialize.h b/src/serialize.h index fd8626007a0..56c324c5277 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -468,26 +468,22 @@ public: template static inline Wrapper Using(T&& t) { return Wrapper(t); } -#define VARINT(obj, ...) WrapVarInt<__VA_ARGS__>(REF(obj)) +#define VARINT(obj, ...) Using>(obj) #define COMPACTSIZE(obj) CCompactSize(REF(obj)) #define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj)) -template -class CVarInt +/** Serialization wrapper class for integers in VarInt format. */ +template +struct VarIntFormatter { -protected: - I &n; -public: - explicit CVarInt(I& nIn) : n(nIn) { } - - template - void Serialize(Stream &s) const { - WriteVarInt(s, n); + template void Ser(Stream &s, I v) + { + WriteVarInt::type>(s, v); } - template - void Unserialize(Stream& s) { - n = ReadVarInt(s); + template void Unser(Stream& s, I& v) + { + v = ReadVarInt::type>(s); } }; @@ -572,9 +568,6 @@ public: } }; -template -CVarInt WrapVarInt(I& n) { return CVarInt{n}; } - template BigEndian WrapBigEndian(I& n) { return BigEndian(n); } From 9b66083788581c264a097e26795561cb3eac455d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 7 Jul 2017 20:27:39 -0700 Subject: [PATCH 3/3] Convert chain to new serialization --- src/chain.h | 56 +++++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/chain.h b/src/chain.h index f28071ff2e0..48bcb8bfddd 100644 --- a/src/chain.h +++ b/src/chain.h @@ -48,17 +48,15 @@ public: uint64_t nTimeFirst; //!< earliest time of block in file uint64_t nTimeLast; //!< latest time of block in file - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(VARINT(nBlocks)); - READWRITE(VARINT(nSize)); - READWRITE(VARINT(nUndoSize)); - READWRITE(VARINT(nHeightFirst)); - READWRITE(VARINT(nHeightLast)); - READWRITE(VARINT(nTimeFirst)); - READWRITE(VARINT(nTimeLast)); + SERIALIZE_METHODS(CBlockFileInfo, obj) + { + READWRITE(VARINT(obj.nBlocks)); + READWRITE(VARINT(obj.nSize)); + READWRITE(VARINT(obj.nUndoSize)); + READWRITE(VARINT(obj.nHeightFirst)); + READWRITE(VARINT(obj.nHeightLast)); + READWRITE(VARINT(obj.nTimeFirst)); + READWRITE(VARINT(obj.nTimeLast)); } void SetNull() { @@ -332,31 +330,25 @@ public: hashPrev = (pprev ? pprev->GetBlockHash() : uint256()); } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { + SERIALIZE_METHODS(CDiskBlockIndex, obj) + { int _nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) - READWRITE(VARINT(_nVersion, VarIntMode::NONNEGATIVE_SIGNED)); + if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT(_nVersion, VarIntMode::NONNEGATIVE_SIGNED)); - READWRITE(VARINT(nHeight, VarIntMode::NONNEGATIVE_SIGNED)); - READWRITE(VARINT(nStatus)); - READWRITE(VARINT(nTx)); - if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) - READWRITE(VARINT(nFile, VarIntMode::NONNEGATIVE_SIGNED)); - if (nStatus & BLOCK_HAVE_DATA) - READWRITE(VARINT(nDataPos)); - if (nStatus & BLOCK_HAVE_UNDO) - READWRITE(VARINT(nUndoPos)); + READWRITE(VARINT(obj.nHeight, VarIntMode::NONNEGATIVE_SIGNED)); + READWRITE(VARINT(obj.nStatus)); + READWRITE(VARINT(obj.nTx)); + if (obj.nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) READWRITE(VARINT(obj.nFile, VarIntMode::NONNEGATIVE_SIGNED)); + if (obj.nStatus & BLOCK_HAVE_DATA) READWRITE(VARINT(obj.nDataPos)); + if (obj.nStatus & BLOCK_HAVE_UNDO) READWRITE(VARINT(obj.nUndoPos)); // block header - READWRITE(this->nVersion); - READWRITE(hashPrev); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); + READWRITE(obj.nVersion); + READWRITE(obj.hashPrev); + READWRITE(obj.hashMerkleRoot); + READWRITE(obj.nTime); + READWRITE(obj.nBits); + READWRITE(obj.nNonce); } uint256 GetBlockHash() const