diff --git a/src/addrdb.h b/src/addrdb.h index 6cb36dfac4e..c47b30b5b9b 100644 --- a/src/addrdb.h +++ b/src/addrdb.h @@ -43,15 +43,7 @@ class CBanEntry nCreateTime = nCreateTimeIn; } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); - READWRITE(nCreateTime); - READWRITE(nBanUntil); - READWRITE(banReason); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.nVersion, obj.nCreateTime, obj.nBanUntil, obj.banReason); } void SetNull() { diff --git a/src/addrman.h b/src/addrman.h index 547088aedf4..66c4c15ca40 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -55,14 +55,10 @@ class CAddrInfo : public CAddress public: - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*(CAddress*)this); - READWRITE(source); - READWRITE(nLastSuccess); - READWRITE(nAttempts); + SERIALIZE_METHODS(obj) + { + READWRITEAS(obj, CAddress); + READWRITE(obj.source, obj.nLastSuccess, obj.nAttempts); } void Init() diff --git a/src/blockencodings.h b/src/blockencodings.h index 5a1d80d4212..4a3a46be08e 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -12,58 +12,70 @@ class CTxMemPool; // Dumb helper to handle CTransaction compression at serialize-time -struct TransactionCompressor { +template +struct TransactionCompressWrapper { private: - CTransactionRef& tx; + T& tx; public: - TransactionCompressor(CTransactionRef& txIn) : tx(txIn) {} - - ADD_SERIALIZE_METHODS; + TransactionCompressWrapper(T& txIn) : tx(txIn) {} + SERIALIZE_METHODS(obj) { READWRITE(obj.tx); } +}; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(tx); //TODO: Compress tx encoding +template +struct Uint48Wrapper { +private: + I& m_int; +public: + Uint48Wrapper(I& i) : m_int(i) {} + template void Serialize(Stream& s) const + { + uint32_t lsb = m_int & 0xffffffff; + uint16_t msb = (m_int >> 32) & 0xffff; + s << lsb << msb; + } + template void Unserialize(Stream& s) + { + uint32_t lsb; + uint16_t msb; + s >> lsb >> msb; + m_int = (uint64_t(msb) << 32) | uint64_t(lsb); } }; +template +static inline TransactionCompressWrapper TransactionCompressor(T& tx) { return TransactionCompressWrapper(tx); } + class BlockTransactionsRequest { public: // A BlockTransactionsRequest message uint256 blockhash; std::vector indexes; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(blockhash); + template + void Serialize(Stream& s) const + { + s << blockhash; uint64_t indexes_size = (uint64_t)indexes.size(); - READWRITE(COMPACTSIZE(indexes_size)); - if (ser_action.ForRead()) { - size_t i = 0; - while (indexes.size() < indexes_size) { - indexes.resize(std::min((uint64_t)(1000 + indexes.size()), indexes_size)); - for (; i < indexes.size(); i++) { - uint64_t index = 0; - READWRITE(COMPACTSIZE(index)); - if (index > std::numeric_limits::max()) - throw std::ios_base::failure("index overflowed 16 bits"); - indexes[i] = index; - } - } - - uint16_t offset = 0; - for (size_t j = 0; j < indexes.size(); j++) { - if (uint64_t(indexes[j]) + uint64_t(offset) > std::numeric_limits::max()) - throw std::ios_base::failure("indexes overflowed 16 bits"); - indexes[j] = indexes[j] + offset; - offset = indexes[j] + 1; - } - } else { - for (size_t i = 0; i < indexes.size(); i++) { - uint64_t index = indexes[i] - (i == 0 ? 0 : (indexes[i - 1] + 1)); - READWRITE(COMPACTSIZE(index)); - } + s << COMPACTSIZE(indexes_size); + for (size_t i = 0; i < indexes.size(); i++) { + const uint64_t index = indexes[i] - (i == 0 ? 0 : (indexes[i - 1] + 1)); + s << COMPACTSIZE(index); + } + } + + template + void Unserialize(Stream& s) + { + std::vector tmp; + s >> blockhash >> VectorApply(tmp); + + indexes.resize(tmp.size()); + uint16_t offset = 0; + for (size_t j = 0; j < tmp.size(); j++) { + if (tmp[j] + uint64_t(offset) > std::numeric_limits::max()) + throw std::ios_base::failure("indexes overflowed 16 bits"); + indexes[j] = tmp[j] + offset; + offset = indexes[j] + 1; } } }; @@ -78,24 +90,9 @@ class BlockTransactions { BlockTransactions(const BlockTransactionsRequest& req) : blockhash(req.blockhash), txn(req.indexes.size()) {} - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(blockhash); - uint64_t txn_size = (uint64_t)txn.size(); - READWRITE(COMPACTSIZE(txn_size)); - if (ser_action.ForRead()) { - size_t i = 0; - while (txn.size() < txn_size) { - txn.resize(std::min((uint64_t)(1000 + txn.size()), txn_size)); - for (; i < txn.size(); i++) - READWRITE(REF(TransactionCompressor(txn[i]))); - } - } else { - for (size_t i = 0; i < txn.size(); i++) - READWRITE(REF(TransactionCompressor(txn[i]))); - } + SERIALIZE_METHODS(obj) + { + READWRITE(obj.blockhash, VectorApply(obj.txn)); } }; @@ -106,16 +103,21 @@ struct PrefilledTransaction { uint16_t index; CTransactionRef tx; - ADD_SERIALIZE_METHODS; + template + void Serialize(Stream& s) const + { + s << COMPACTSIZE(index) << TransactionCompressor(tx); + } - template - inline void SerializationOp(Stream& s, Operation ser_action) { - uint64_t idx = index; - READWRITE(COMPACTSIZE(idx)); + template + void Unserialize(Stream& s) + { + uint64_t idx; + s >> COMPACTSIZE(idx); if (idx > std::numeric_limits::max()) throw std::ios_base::failure("index overflowed 16-bits"); index = idx; - READWRITE(REF(TransactionCompressor(tx))); + s >> TransactionCompressor(tx); } }; @@ -154,40 +156,18 @@ class CBlockHeaderAndShortTxIDs { size_t BlockTxCount() const { return shorttxids.size() + prefilledtxn.size(); } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(header); - READWRITE(nonce); - - uint64_t shorttxids_size = (uint64_t)shorttxids.size(); - READWRITE(COMPACTSIZE(shorttxids_size)); - if (ser_action.ForRead()) { - size_t i = 0; - while (shorttxids.size() < shorttxids_size) { - shorttxids.resize(std::min((uint64_t)(1000 + shorttxids.size()), shorttxids_size)); - for (; i < shorttxids.size(); i++) { - uint32_t lsb = 0; uint16_t msb = 0; - READWRITE(lsb); - READWRITE(msb); - shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb); - static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids serialization assumes 6-byte shorttxids"); - } - } - } else { - for (size_t i = 0; i < shorttxids.size(); i++) { - uint32_t lsb = shorttxids[i] & 0xffffffff; - uint16_t msb = (shorttxids[i] >> 32) & 0xffff; - READWRITE(lsb); - READWRITE(msb); - } - } - - READWRITE(prefilledtxn); + template + void Serialize(Stream& s) const + { + s << header << nonce << VectorApply(shorttxids) << prefilledtxn; + } - if (ser_action.ForRead()) - FillShortTxIDSelector(); + template + inline void Unserialize(Stream& s) + { + static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids serialization assumes 6-byte shorttxids"); + s >> header >> nonce >> VectorApply(shorttxids) >> prefilledtxn; + FillShortTxIDSelector(); } }; diff --git a/src/bloom.h b/src/bloom.h index 7ca96822396..c98458e263b 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -70,15 +70,7 @@ class CBloomFilter CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweak, unsigned char nFlagsIn); CBloomFilter() : isFull(true), isEmpty(false), nHashFuncs(0), nTweak(0), nFlags(0) {} - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(vData); - READWRITE(nHashFuncs); - READWRITE(nTweak); - READWRITE(nFlags); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.vData, obj.nHashFuncs, obj.nTweak, obj.nFlags); } void insert(const std::vector& vKey); void insert(const COutPoint& outpoint); diff --git a/src/chain.h b/src/chain.h index 1223539e737..360df302e20 100644 --- a/src/chain.h +++ b/src/chain.h @@ -39,17 +39,15 @@ class CBlockFileInfo 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(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() { @@ -87,13 +85,7 @@ struct CDiskBlockPos int nFile; unsigned int nPos; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(VARINT(nFile)); - READWRITE(VARINT(nPos)); - } + SERIALIZE_METHODS(obj) { READWRITE(VARINT(obj.nFile), VARINT(obj.nPos)); } CDiskBlockPos() { SetNull(); @@ -380,31 +372,29 @@ class CDiskBlockIndex : public CBlockIndex hashPrev = (pprev ? pprev->GetBlockHash() : uint256()); } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { + SERIALIZE_METHODS(obj) + { int _nVersion = s.GetVersion(); if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT(_nVersion)); - READWRITE(VARINT(nHeight)); - READWRITE(VARINT(nStatus)); - READWRITE(VARINT(nTx)); - if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) - READWRITE(VARINT(nFile)); - if (nStatus & BLOCK_HAVE_DATA) - READWRITE(VARINT(nDataPos)); - if (nStatus & BLOCK_HAVE_UNDO) - READWRITE(VARINT(nUndoPos)); + READWRITE(VARINT(obj.nHeight)); + READWRITE(VARINT(obj.nStatus)); + READWRITE(VARINT(obj.nTx)); + if (obj.nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) + READWRITE(VARINT(obj.nFile)); + 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 diff --git a/src/coins.h b/src/coins.h index efb5ce869c3..be264ec6ae1 100644 --- a/src/coins.h +++ b/src/coins.h @@ -24,7 +24,7 @@ * * Serialized format: * - VARINT((coinbase ? 1 : 0) | (height << 1)) - * - the non-spent CTxOut (via CTxOutCompressor) + * - the non-spent CTxOut (via TxOutCompress) */ class Coin { @@ -60,7 +60,7 @@ class Coin assert(!IsSpent()); uint32_t code = nHeight * 2 + fCoinBase; ::Serialize(s, VARINT(code)); - ::Serialize(s, CTxOutCompressor(REF(out))); + ::Serialize(s, TxOutCompress(out)); } template @@ -69,7 +69,7 @@ class Coin ::Unserialize(s, VARINT(code)); nHeight = code >> 1; fCoinBase = code & 1; - ::Unserialize(s, REF(CTxOutCompressor(out))); + ::Unserialize(s, TxOutCompress(out)); } bool IsSpent() const { diff --git a/src/compressor.cpp b/src/compressor.cpp index f4c12f38d2d..52624195495 100644 --- a/src/compressor.cpp +++ b/src/compressor.cpp @@ -9,7 +9,15 @@ #include "pubkey.h" #include "script/standard.h" -bool CScriptCompressor::IsToKeyID(CKeyID &hash) const +/* + * These check for scripts for which a special case with a shorter encoding is defined. + * They are implemented separately from the CScript test, as these test for exact byte + * sequence correspondences, and are more strict. For example, IsToPubKey also verifies + * whether the public key is valid (as invalid ones cannot be represented in compressed + * form). + */ + +static bool IsToKeyID(const CScript& script, CKeyID &hash) { if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY @@ -20,7 +28,7 @@ bool CScriptCompressor::IsToKeyID(CKeyID &hash) const return false; } -bool CScriptCompressor::IsToScriptID(CScriptID &hash) const +static bool IsToScriptID(const CScript& script, CScriptID &hash) { if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20 && script[22] == OP_EQUAL) { @@ -30,7 +38,7 @@ bool CScriptCompressor::IsToScriptID(CScriptID &hash) const return false; } -bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const +static bool IsToPubKey(const CScript& script, CPubKey &pubkey) { if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && (script[1] == 0x02 || script[1] == 0x03)) { @@ -45,24 +53,24 @@ bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const return false; } -bool CScriptCompressor::Compress(std::vector &out) const +bool CompressScript(const CScript& script, std::vector &out) { CKeyID keyID; - if (IsToKeyID(keyID)) { + if (IsToKeyID(script, keyID)) { out.resize(21); out[0] = 0x00; memcpy(&out[1], &keyID, 20); return true; } CScriptID scriptID; - if (IsToScriptID(scriptID)) { + if (IsToScriptID(script, scriptID)) { out.resize(21); out[0] = 0x01; memcpy(&out[1], &scriptID, 20); return true; } CPubKey pubkey; - if (IsToPubKey(pubkey)) { + if (IsToPubKey(script, pubkey)) { out.resize(33); memcpy(&out[1], &pubkey[1], 32); if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { @@ -76,7 +84,7 @@ bool CScriptCompressor::Compress(std::vector &out) const return false; } -unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const +unsigned int GetSpecialScriptSize(unsigned int nSize) { if (nSize == 0 || nSize == 1) return 20; @@ -85,7 +93,7 @@ unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const return 0; } -bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector &in) +bool DecompressScript(CScript& script, unsigned int nSize, const std::vector &in) { switch(nSize) { case 0x00: @@ -139,7 +147,7 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector &out); +unsigned int GetSpecialScriptSize(unsigned int nSize); +bool DecompressScript(CScript& script, unsigned int nSize, const std::vector &out); + +uint64_t CompressAmount(uint64_t nAmount); +uint64_t DecompressAmount(uint64_t nAmount); /** Compact serializer for scripts. * @@ -25,7 +28,8 @@ class CScriptID; * Other scripts up to 121 bytes require 1 byte + script length. Above * that, scripts up to 16505 bytes require 2 bytes + script length. */ -class CScriptCompressor +template +class ScriptCompressionWrapper { private: /** @@ -36,35 +40,20 @@ class CScriptCompressor */ static const unsigned int nSpecialScripts = 6; - CScript &script; -protected: - /** - * These check for scripts for which a special case with a shorter encoding is defined. - * They are implemented separately from the CScript test, as these test for exact byte - * sequence correspondences, and are more strict. For example, IsToPubKey also verifies - * whether the public key is valid (as invalid ones cannot be represented in compressed - * form). - */ - bool IsToKeyID(CKeyID &hash) const; - bool IsToScriptID(CScriptID &hash) const; - bool IsToPubKey(CPubKey &pubkey) const; - - bool Compress(std::vector &out) const; - unsigned int GetSpecialSize(unsigned int nSize) const; - bool Decompress(unsigned int nSize, const std::vector &out); + S &script; public: - CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } + ScriptCompressionWrapper(S &scriptIn) : script(scriptIn) { } template void Serialize(Stream &s) const { std::vector compr; - if (Compress(compr)) { - s << CFlatData(compr); + if (CompressScript(script, compr)) { + s << FlatVector(compr); return; } unsigned int nSize = script.size() + nSpecialScripts; s << VARINT(nSize); - s << CFlatData(script); + s << FlatVector(script); } template @@ -72,9 +61,9 @@ class CScriptCompressor unsigned int nSize = 0; s >> VARINT(nSize); if (nSize < nSpecialScripts) { - std::vector vch(GetSpecialSize(nSize), 0x00); - s >> REF(CFlatData(vch)); - Decompress(nSize, vch); + std::vector vch(GetSpecialScriptSize(nSize), 0x00); + s >> FlatVector(vch); + DecompressScript(script, nSize, vch); return; } nSize -= nSpecialScripts; @@ -84,38 +73,43 @@ class CScriptCompressor s.ignore(nSize); } else { script.resize(nSize); - s >> REF(CFlatData(script)); + s >> FlatVector(script); } } }; -/** wrapper for CTxOut that provides a more compact serialization */ -class CTxOutCompressor +template +class AmountCompressionWrapper { -private: - CTxOut &txout; - + I& m_val; public: - static uint64_t CompressAmount(uint64_t nAmount); - static uint64_t DecompressAmount(uint64_t nAmount); - - CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } + AmountCompressionWrapper(I& val) : m_val(val) {} + template void Serialize(Stream& s) const + { + s << VARINT(CompressAmount(m_val)); + } + template void Unserialize(Stream& s) + { + uint64_t v; + s >> VARINT(v); + m_val = DecompressAmount(v); + } +}; - ADD_SERIALIZE_METHODS; +template ScriptCompressionWrapper ScriptCompress(S& s) { return ScriptCompressionWrapper(s); } +template AmountCompressionWrapper AmountCompress(I& i) { return AmountCompressionWrapper(i); } - template - inline void SerializationOp(Stream& s, Operation ser_action) { - if (!ser_action.ForRead()) { - uint64_t nVal = CompressAmount(txout.nValue); - READWRITE(VARINT(nVal)); - } else { - uint64_t nVal = 0; - READWRITE(VARINT(nVal)); - txout.nValue = DecompressAmount(nVal); - } - CScriptCompressor cscript(REF(txout.scriptPubKey)); - READWRITE(cscript); - } +/** wrapper for CTxOut that provides a more compact serialization */ +template +class TxOutCompressionWrapper +{ +private: + O &txout; +public: + TxOutCompressionWrapper(O &txoutIn) : txout(txoutIn) { } + SERIALIZE_METHODS(obj) { READWRITE(AmountCompress(obj.txout.nValue), ScriptCompress(obj.txout.scriptPubKey)); } }; +template TxOutCompressionWrapper TxOutCompress(O& o) { return TxOutCompressionWrapper(o); } + #endif // BITCOIN_COMPRESSOR_H diff --git a/src/hash.h b/src/hash.h index b9952d39fc9..f9b3fa2ca18 100644 --- a/src/hash.h +++ b/src/hash.h @@ -187,7 +187,7 @@ class CHashVerifier : public CHashWriter } template - CHashVerifier& operator>>(T& obj) + CHashVerifier& operator>>(T&& obj) { // Unserialize from this stream ::Unserialize(*this, obj); diff --git a/src/merkleblock.h b/src/merkleblock.h index f590c487dee..590419a0c64 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -81,27 +81,31 @@ class CPartialMerkleTree public: - /** serialization implementation */ - ADD_SERIALIZE_METHODS; + template + void Serialize(Stream& s) const + { + s << nTransactions; + s << vHash; + std::vector vBytes; + vBytes.resize((vBits.size()+7)/8); + for (unsigned int p = 0; p < vBits.size(); p++) { + vBytes[p / 8] |= vBits[p] << (p % 8); + } + s << vBytes; + } - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(nTransactions); - READWRITE(vHash); + template + void Unserialize(Stream& s) + { + s >> nTransactions; + s >> vHash; std::vector vBytes; - if (ser_action.ForRead()) { - READWRITE(vBytes); - CPartialMerkleTree &us = *(const_cast(this)); - us.vBits.resize(vBytes.size() * 8); - for (unsigned int p = 0; p < us.vBits.size(); p++) - us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0; - us.fBad = false; - } else { - vBytes.resize((vBits.size()+7)/8); - for (unsigned int p = 0; p < vBits.size(); p++) - vBytes[p / 8] |= vBits[p] << (p % 8); - READWRITE(vBytes); + s >> vBytes; + vBits.resize(vBytes.size() * 8); + for (unsigned int p = 0; p < vBits.size(); p++) { + vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0; } + fBad = false; } /** Construct a partial merkle tree from a list of transaction ids, and a mask that selects a subset of them */ @@ -147,13 +151,7 @@ class CMerkleBlock CMerkleBlock() {} - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(header); - READWRITE(txn); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.header, obj.txn); } }; #endif // BITCOIN_MERKLEBLOCK_H diff --git a/src/netaddress.h b/src/netaddress.h index 57ce897db5e..d8bd80b5528 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -89,12 +89,7 @@ class CNetAddr friend bool operator!=(const CNetAddr& a, const CNetAddr& b); friend bool operator<(const CNetAddr& a, const CNetAddr& b); - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(FLATDATA(ip)); - } + SERIALIZE_METHODS(obj) { READWRITE(FLATDATA(obj.ip)); } friend class CSubNet; }; @@ -126,14 +121,7 @@ class CSubNet friend bool operator!=(const CSubNet& a, const CSubNet& b); friend bool operator<(const CSubNet& a, const CSubNet& b); - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(network); - READWRITE(FLATDATA(netmask)); - READWRITE(FLATDATA(valid)); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.network, FLATDATA(obj.netmask), FLATDATA(obj.valid)); } }; /** A combination of a network address (CNetAddr) and a (TCP) port */ @@ -162,16 +150,7 @@ class CService : public CNetAddr CService(const struct in6_addr& ipv6Addr, unsigned short port); CService(const struct sockaddr_in6& addr); - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(FLATDATA(ip)); - unsigned short portN = htons(port); - READWRITE(FLATDATA(portN)); - if (ser_action.ForRead()) - port = ntohs(portN); - } + SERIALIZE_METHODS(obj) { READWRITE(FLATDATA(obj.ip), BigEndian(obj.port)); } }; #endif // BITCOIN_NETADDRESS_H diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 565da6c1541..3953cf47f67 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -44,12 +44,7 @@ class CFeeRate CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; } std::string ToString() const; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(nSatoshisPerK); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.nSatoshiPerK); } }; #endif // BITCOIN_POLICY_FEERATE_H diff --git a/src/primitives/block.h b/src/primitives/block.h index c90a1dfa644..3dcaa840538 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -33,16 +33,9 @@ class CBlockHeader SetNull(); } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); - READWRITE(hashPrevBlock); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); + SERIALIZE_METHODS(obj) + { + READWRITE(obj.nVersion, obj.hashPrevBlock, obj.hashMerkleRoot, obj.nTime, obj.nBits, obj.nNonce); } void SetNull() @@ -89,12 +82,10 @@ class CBlock : public CBlockHeader *((CBlockHeader*)this) = header; } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*(CBlockHeader*)this); - READWRITE(vtx); + SERIALIZE_METHODS(obj) + { + READWRITEAS(obj, CBlockHeader); + READWRITE(obj.vtx); } void SetNull() @@ -131,14 +122,12 @@ struct CBlockLocator CBlockLocator(const std::vector& vHaveIn) : vHave(vHaveIn) {} - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { + SERIALIZE_METHODS(obj) + { int nVersion = s.GetVersion(); if (!(s.GetType() & SER_GETHASH)) READWRITE(nVersion); - READWRITE(vHave); + READWRITE(obj.vHave); } void SetNull() diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 041034bb8b6..59f3ab05ee2 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -24,13 +24,7 @@ class COutPoint COutPoint(): n((uint32_t) -1) { } COutPoint(const uint256& hashIn, uint32_t nIn): hash(hashIn), n(nIn) { } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(hash); - READWRITE(n); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.hash, obj.n); } void SetNull() { hash.SetNull(); n = (uint32_t) -1; } bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); } @@ -101,14 +95,7 @@ class CTxIn explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL); CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL); - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(prevout); - READWRITE(scriptSig); - READWRITE(nSequence); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.prevout, obj.scriptSig, obj.nSequence); } friend bool operator==(const CTxIn& a, const CTxIn& b) { @@ -141,13 +128,7 @@ class CTxOut CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn); - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(nValue); - READWRITE(scriptPubKey); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.nValue, obj.scriptPubKey); } void SetNull() { diff --git a/src/protocol.h b/src/protocol.h index 7890bb627de..f7a1f87f3a9 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -45,15 +45,9 @@ class CMessageHeader std::string GetCommand() const; bool IsValid(const MessageStartChars& messageStart) const; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) + SERIALIZE_METHODS(obj) { - READWRITE(FLATDATA(pchMessageStart)); - READWRITE(FLATDATA(pchCommand)); - READWRITE(nMessageSize); - READWRITE(FLATDATA(pchChecksum)); + READWRITE(FLATDATA(obj.pchMessageStart), FLATDATA(obj.pchCommand), obj.nMessageSize, FLATDATA(obj.pchChecksum)); } char pchMessageStart[MESSAGE_START_SIZE]; @@ -286,23 +280,16 @@ class CAddress : public CService void Init(); - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) + SERIALIZE_METHODS(obj) { - if (ser_action.ForRead()) - Init(); int nVersion = s.GetVersion(); if (s.GetType() & SER_DISK) READWRITE(nVersion); if ((s.GetType() & SER_DISK) || (nVersion >= CADDR_TIME_VERSION && !(s.GetType() & SER_GETHASH))) - READWRITE(nTime); - uint64_t nServicesInt = nServices; - READWRITE(nServicesInt); - nServices = (ServiceFlags)nServicesInt; - READWRITE(*(CService*)this); + READWRITE(obj.nTime); + READWRITEAS(obj.nServices, uint64_t); + READWRITEAS(obj, CService); } // TODO: make private (improves encapsulation) @@ -341,14 +328,7 @@ class CInv CInv(); CInv(int typeIn, const uint256& hashIn); - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(type); - READWRITE(hash); - } + SERIALIZE_METHODS(obj) { READWRITE(obj.type, obj.hash); } friend bool operator<(const CInv& a, const CInv& b); diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 0c02968f928..9bd12d9ac39 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -24,19 +24,19 @@ class RecentRequestEntry QDateTime date; SendCoinsRecipient recipient; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { + template + void Serialize(Stream& s) const + { unsigned int nDate = date.toTime_t(); + s << this->nVersion << id << nDate << recipient; + } - READWRITE(this->nVersion); - READWRITE(id); - READWRITE(nDate); - READWRITE(recipient); - - if (ser_action.ForRead()) - date = QDateTime::fromTime_t(nDate); + template + void Unserialize(Stream& s) + { + unsigned int nDate; + s >> this->nVersion >> id >> nDate >> recipient; + date = QDateTime::fromTime_t(nDate); } }; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6be36a57e25..492b3c8b777 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -62,35 +62,32 @@ class SendCoinsRecipient static const int CURRENT_VERSION = 1; int nVersion; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { + template + void Serialize(Stream& s) const + { std::string sAddress = address.toStdString(); std::string sLabel = label.toStdString(); std::string sMessage = message.toStdString(); std::string sPaymentRequest; - if (!ser_action.ForRead() && paymentRequest.IsInitialized()) + if (paymentRequest.IsInitialized()) { paymentRequest.SerializeToString(&sPaymentRequest); + } std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString(); + s << this->nVersion << sAddress << sLabel << amount << sMessage << sPaymentRequest << sAuthenticatedMerchant; + } - READWRITE(this->nVersion); - READWRITE(sAddress); - READWRITE(sLabel); - READWRITE(amount); - READWRITE(sMessage); - READWRITE(sPaymentRequest); - READWRITE(sAuthenticatedMerchant); - - if (ser_action.ForRead()) - { - address = QString::fromStdString(sAddress); - label = QString::fromStdString(sLabel); - message = QString::fromStdString(sMessage); - if (!sPaymentRequest.empty()) - paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size())); - authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant); + template + void Unserialize(Stream& s) + { + std::string sAddress, sLabel, sMessage, sPaymentRequest, sAuthenticatedMerchant; + s >> this->nVersion >> sAddress >> sLabel >> amount >> sMessage >> sPaymentRequest >> sAuthenticatedMerchant; + address = QString::fromStdString(sAddress); + label = QString::fromStdString(sLabel); + message = QString::fromStdString(sMessage); + if (!sPaymentRequest.empty()) { + paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size())); } + authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant); } }; diff --git a/src/rest.cpp b/src/rest.cpp index 6a4b005f908..9156cf994e8 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -45,18 +45,13 @@ struct CCoin { uint32_t nHeight; CTxOut out; - ADD_SERIALIZE_METHODS; - CCoin() : nHeight(0) {} CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {} - template - inline void SerializationOp(Stream& s, Operation ser_action) + SERIALIZE_METHODS(obj) { uint32_t nTxVerDummy = 0; - READWRITE(nTxVerDummy); - READWRITE(nHeight); - READWRITE(out); + READWRITE(nTxVerDummy, obj.nHeight, obj.out); } }; diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 03128917fd8..003ed5d0df0 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -40,7 +40,7 @@ class TxInputStream } template - TxInputStream& operator>>(T& obj) + TxInputStream& operator>>(T&& obj) { ::Unserialize(*this, obj); return *this; diff --git a/src/script/script.h b/src/script/script.h index 711ffa97f81..e0eb7df2b66 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -405,12 +405,7 @@ class CScript : public CScriptBase CScript(std::vector::const_iterator pbegin, std::vector::const_iterator pend) : CScriptBase(pbegin, pend) { } CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(static_cast(*this)); - } + SERIALIZE_METHODS(obj) { READWRITEAS(obj, CScriptBase); } CScript& operator+=(const CScript& b) { diff --git a/src/serialize.h b/src/serialize.h index 8b86a07a767..9ffd3a243b0 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -39,26 +39,6 @@ static const unsigned int MAX_SIZE = 0x02000000; struct deserialize_type {}; constexpr deserialize_type deserialize {}; -/** - * Used to bypass the rule against non-const reference to temporary - * where it makes sense with wrappers such as CFlatData or CTxDB - */ -template -inline T& REF(const T& val) -{ - return const_cast(val); -} - -/** - * Used to acquire a non-const pointer "this" to generate bodies - * of const serialization operations from a template - */ -template -inline T* NCONST_PTR(const T* val) -{ - return const_cast(val); -} - /* * Lowest-level serialization and conversion. * @note Sizes of these types are verified in the tests @@ -72,6 +52,11 @@ template inline void ser_writedata16(Stream &s, uint16_t obj) obj = htole16(obj); s.write((char*)&obj, 2); } +template inline void ser_writedata16be(Stream &s, uint16_t obj) +{ + obj = htobe16(obj); + s.write((char*)&obj, 2); +} template inline void ser_writedata32(Stream &s, uint32_t obj) { obj = htole32(obj); @@ -94,6 +79,12 @@ template inline uint16_t ser_readdata16(Stream &s) s.read((char*)&obj, 2); return le16toh(obj); } +template inline uint16_t ser_readdata16be(Stream &s) +{ + uint16_t obj; + s.read((char*)&obj, 2); + return be16toh(obj); +} template inline uint32_t ser_readdata32(Stream &s) { uint32_t obj; @@ -148,24 +139,39 @@ enum SER_GETHASH = (1 << 2), }; -#define READWRITE(obj) (::SerReadWrite(s, (obj), ser_action)) -#define READWRITEMANY(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) +// Convert the reference base type to X, without changing constness or reference type. +template X& AsBaseType(X& x) { return x; } +template const X& AsBaseType(const X& x) { return x; } +template X&& AsBaseType(X&& x) { return std::move(x); } +template const X&& AsBaseType(const X&& x) { return std::move(x); } + +#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) -/** - * Implement three methods for serializable objects. These are actually wrappers over - * "SerializationOp" template, which implements the body of each class' serialization - * code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be - * added as members. +/** Serialize/deserialize the given object after converting it to a reference of + * type `typ`, while preserving reference type and constness. Use this instead + * of casting manually, as it will not inadvertently remove constness. */ -#define ADD_SERIALIZE_METHODS \ - template \ - void Serialize(Stream& s) const { \ - NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize()); \ - } \ - template \ - void Unserialize(Stream& s) { \ - SerializationOp(s, CSerActionUnserialize()); \ - } +#define READWRITEAS(obj, typ) (::SerReadWriteMany(s, ser_action, AsBaseType(obj))) + +/** + * Implement the Serialize and Unserialize methods by delegating to a single templated + * static method that takes the to-be-(de)serialized object as a parameter. This approach + * has the advantage that the constness of the object becomes a template parameter, and + * thus allows a single implementation that sees the object as const for serializing + * and non-const for deserializing, without casts. + */ +#define SERIALIZE_METHODS(obj) \ + template \ + void Serialize(Stream& s) const { \ + SerializationOps(*this, s, CSerActionSerialize()); \ + } \ + template \ + void Unserialize(Stream& s) { \ + SerializationOps(*this, s, CSerActionUnserialize()); \ + } \ + template \ + static inline void SerializationOps(Type& obj, Stream& s, Operation ser_action) \ + template inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char template inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); } @@ -351,37 +357,15 @@ I ReadVarInt(Stream& is) } } -#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) -#define VARINT(obj) REF(WrapVarInt(REF(obj))) -#define COMPACTSIZE(obj) REF(CCompactSize(REF(obj))) -#define LIMITED_STRING(obj,n) REF(LimitedString< n >(REF(obj))) - -/** - * Wrapper for serializing arrays and POD. - */ -class CFlatData +/** Wrapper for serializing arrays and POD. */ +template +class FlatRangeWrapper { protected: - char* pbegin; - char* pend; + C* pbegin; + C* pend; public: - CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } - template - explicit CFlatData(std::vector &v) - { - pbegin = (char*)v.data(); - pend = (char*)(v.data() + v.size()); - } - template - explicit CFlatData(prevector &v) - { - pbegin = (char*)v.data(); - pend = (char*)(v.data() + v.size()); - } - char* begin() { return pbegin; } - const char* begin() const { return pbegin; } - char* end() { return pend; } - const char* end() const { return pend; } + FlatRangeWrapper(C* pbeginIn, C* pendIn) : pbegin(pbeginIn), pend(pendIn) { } template void Serialize(Stream& s) const @@ -395,18 +379,29 @@ class CFlatData s.read(pbegin, pend - pbegin); } }; - +//! Construct a FlatRange wrapper around a const vector. +template static inline const FlatRangeWrapper FlatVector(const T& t) { return FlatRangeWrapper((const char*)t.data(), (const char*)(t.data() + t.size())); } +//! Construct a FlatRange wrapper around a non-const vector. +template static inline FlatRangeWrapper FlatVector(T& t) { return FlatRangeWrapper((char*)t.data(), (char*)(t.data() + t.size())); } +//! Construct a FlatRange wrapper around a const POD and array types. +template static inline const FlatRangeWrapper FlatDataInner(const T* t, size_t len) { return FlatRangeWrapper((const char*)t, ((const char*)t) + len); } +//! Construct a FlatRange wrapper around a non-const POD and array types. +template static inline FlatRangeWrapper FlatDataInner(T* t, size_t len) { return FlatRangeWrapper((char*)t, ((char*)t) + len); } +//! Helper macro to easily serialize POD types. +#define FLATDATA(x) FlatDataInner(&(x), sizeof(x)) + +/** Serialization wrapper class for integers in VarInt format. */ template -class CVarInt +class VarIntWrapper { protected: I &n; public: - CVarInt(I& nIn) : n(nIn) { } + VarIntWrapper(I& nIn) : n(nIn) { } template void Serialize(Stream &s) const { - WriteVarInt(s, n); + WriteVarInt(s, n); } template @@ -414,32 +409,39 @@ class CVarInt n = ReadVarInt(s); } }; +//! Automatically construct a VarInt wrapper around the argument. +template static inline VarIntWrapper::type> VARINT(I&& i) { return VarIntWrapper::type>(i); } -class CCompactSize +/** Serialization wrapper class for integers in CompactSize format. */ +template +class CompactSizeWrapper { protected: - uint64_t &n; + I &n; public: - CCompactSize(uint64_t& nIn) : n(nIn) { } + CompactSizeWrapper(I& nIn) : n(nIn) { } template - void Serialize(Stream &s) const { - WriteCompactSize(s, n); + void Unserialize(Stream& s) { + n = ReadCompactSize(s); } template - void Unserialize(Stream& s) { - n = ReadCompactSize(s); + void Serialize(Stream& s) const { + WriteCompactSize(s, n); } }; +//! Automatically construct a CompactSize wrapper around the argument. +template static inline CompactSizeWrapper COMPACTSIZE(I& i) { return CompactSizeWrapper(i); } +/** Serialization wrapper class for strings of limited length. */ template -class LimitedString +class LimitedStringWrapper { protected: std::string& string; public: - LimitedString(std::string& _string) : string(_string) {} + LimitedStringWrapper(std::string& _string) : string(_string) {} template void Unserialize(Stream& s) @@ -456,14 +458,105 @@ class LimitedString template void Serialize(Stream& s) const { - WriteCompactSize(s, string.size()); - if (!string.empty()) - s.write((char*)string.data(), string.size()); + s << string; } }; +//! Add a LimitedString wrapper around a const string (identity) +template static inline const std::string& LimitedString(const std::string& str) { return str; } +//! Add a LimitedString wrapper around a non-const string +template static inline LimitedStringWrapper LimitedString(std::string& str) { return LimitedStringWrapper(str); } +#define LIMITED_STRING(obj, n) LimitedString(obj) + +/** Serialization wrapper class for big-endian integers. + * + * Use this wrapper around integer types that are stored in memory in native + * byte order, but serialized in big endian notation. + * + * Onlyy 16-bit types are supported for now. + */ +template class BigEndianWrapper +{ +protected: + I& m_val; +public: + BigEndianWrapper(I& val) : m_val(val) + { + static_assert(S == 2, "Unsupported BigEndian size"); + } + + template + void Serialize(Stream& s) const + { + if (S == 2) ser_writedata16be(s, m_val); + } + + template + void Unserialize(Stream& s) + { + if (S == 2) m_val = ser_readdata16be(s); + } +}; +//! Automatically construct a BigEndianWrapper around the argument. +template static inline BigEndianWrapper BigEndian(I& i) { return BigEndianWrapper(i); } + +/** Serialization wrapper for custom-element vectors. + * + * This allows (de)serialization of vectors of type V while using a custom + * serializer W for the entries inside. + * + * For const-correctness, the W parameter is a template itself. + * It is instantiated as W when serializing, and as + * W when deserializing. + * + * Example: + * struct X { + * std::vector v; + * SERIALIZE_METHODS(obj) { READWRITE(VectorApply(obj.v)); } + * }; + * will define a struct that contains a vector of uint64_t, which is serialized + * as a vector of VarInt-encoded integers. + * + * V is not required to be an std::vector type. It works for any class that + * exposes a value_type, iteration, and resize method that behave like vectors. + */ +template