diff options
Diffstat (limited to 'src/serialize.h')
-rw-r--r-- | src/serialize.h | 184 |
1 files changed, 104 insertions, 80 deletions
diff --git a/src/serialize.h b/src/serialize.h index dcc8d8691e..e54c7483d2 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -22,6 +22,7 @@ #include <vector> #include <prevector.h> +#include <span.h> static const unsigned int MAX_SIZE = 0x02000000; @@ -41,7 +42,7 @@ 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 + * where it makes sense with wrappers. */ template<typename T> inline T& REF(const T& val) @@ -59,6 +60,12 @@ inline T* NCONST_PTR(const T* val) return const_cast<T*>(val); } +//! Safely convert odd char pointer types to standard ones. +inline char* CharCast(char* c) { return c; } +inline char* CharCast(unsigned char* c) { return (char*)c; } +inline const char* CharCast(const char* c) { return c; } +inline const char* CharCast(const unsigned char* c) { return (const char*)c; } + /* * Lowest-level serialization and conversion. * @note Sizes of these types are verified in the tests @@ -72,6 +79,11 @@ template<typename Stream> inline void ser_writedata16(Stream &s, uint16_t obj) obj = htole16(obj); s.write((char*)&obj, 2); } +template<typename Stream> inline void ser_writedata16be(Stream &s, uint16_t obj) +{ + obj = htobe16(obj); + s.write((char*)&obj, 2); +} template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj) { obj = htole32(obj); @@ -94,6 +106,12 @@ template<typename Stream> inline uint16_t ser_readdata16(Stream &s) s.read((char*)&obj, 2); return le16toh(obj); } +template<typename Stream> inline uint16_t ser_readdata16be(Stream &s) +{ + uint16_t obj; + s.read((char*)&obj, 2); + return be16toh(obj); +} template<typename Stream> inline uint32_t ser_readdata32(Stream &s) { uint32_t obj; @@ -148,8 +166,12 @@ 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<typename X> X& ReadWriteAsHelper(X& x) { return x; } +template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; } + +#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) +#define READWRITEAS(type, obj) (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj))) /** * Implement three methods for serializable objects. These are actually wrappers over @@ -178,6 +200,10 @@ template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_wri 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, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); } +template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); } +template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); } +template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(CharCast(span.data()), span.size()); } 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); } @@ -190,6 +216,9 @@ template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = 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, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); } +template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); } +template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(CharCast(span.data()), span.size()); } 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; } @@ -297,9 +326,31 @@ uint64_t ReadCompactSize(Stream& is) * 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] */ -template<typename I> +/** + * Mode for encoding VarInts. + * + * Currently there is no support for signed encodings. The default mode will not + * compile with signed values, and the legacy "nonnegative signed" mode will + * accept signed values, but improperly encode and decode them if they are + * negative. In the future, the DEFAULT mode could be extended to support + * negative numbers in a backwards compatible way, and additional modes could be + * added to support different varint formats (e.g. zigzag encoding). + */ +enum class VarIntMode { DEFAULT, NONNEGATIVE_SIGNED }; + +template <VarIntMode Mode, typename I> +struct CheckVarIntMode { + constexpr CheckVarIntMode() + { + static_assert(Mode != VarIntMode::DEFAULT || std::is_unsigned<I>::value, "Unsigned type required with mode DEFAULT."); + static_assert(Mode != VarIntMode::NONNEGATIVE_SIGNED || std::is_signed<I>::value, "Signed type required with mode NONNEGATIVE_SIGNED."); + } +}; + +template<VarIntMode Mode, typename I> inline unsigned int GetSizeOfVarInt(I n) { + CheckVarIntMode<Mode, I>(); int nRet = 0; while(true) { nRet++; @@ -313,9 +364,10 @@ inline unsigned int GetSizeOfVarInt(I n) template<typename I> inline void WriteVarInt(CSizeComputer& os, I n); -template<typename Stream, typename I> +template<typename Stream, VarIntMode Mode, typename I> void WriteVarInt(Stream& os, I n) { + CheckVarIntMode<Mode, I>(); unsigned char tmp[(sizeof(n)*8+6)/7]; int len=0; while(true) { @@ -330,9 +382,10 @@ void WriteVarInt(Stream& os, I n) } while(len--); } -template<typename Stream, typename I> +template<typename Stream, VarIntMode Mode, typename I> I ReadVarInt(Stream& is) { + CheckVarIntMode<Mode, I>(); I n = 0; while(true) { unsigned char chData = ser_readdata8(is); @@ -351,67 +404,60 @@ 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))) +#define VARINT(obj, ...) WrapVarInt<__VA_ARGS__>(REF(obj)) +#define COMPACTSIZE(obj) CCompactSize(REF(obj)) +#define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj)) -/** - * Wrapper for serializing arrays and POD. - */ -class CFlatData +template<VarIntMode Mode, typename I> +class CVarInt { protected: - char* pbegin; - char* pend; + I &n; public: - CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } - template <class T, class TAl> - explicit CFlatData(std::vector<T,TAl> &v) - { - pbegin = (char*)v.data(); - pend = (char*)(v.data() + v.size()); - } - template <unsigned int N, typename T, typename S, typename D> - explicit CFlatData(prevector<N, T, S, D> &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; } + explicit CVarInt(I& nIn) : n(nIn) { } template<typename Stream> - void Serialize(Stream& s) const - { - s.write(pbegin, pend - pbegin); + void Serialize(Stream &s) const { + WriteVarInt<Stream,Mode,I>(s, n); } template<typename Stream> - void Unserialize(Stream& s) - { - s.read(pbegin, pend - pbegin); + void Unserialize(Stream& s) { + n = ReadVarInt<Stream,Mode,I>(s); } }; +/** 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. This is only intended + * to implement serializers that are compatible with existing formats, and + * its use is not recommended for new data structures. + * + * Only 16-bit types are supported for now. + */ template<typename I> -class CVarInt +class BigEndian { protected: - I &n; + I& m_val; public: - explicit CVarInt(I& nIn) : n(nIn) { } + explicit BigEndian(I& val) : m_val(val) + { + static_assert(std::is_unsigned<I>::value, "BigEndian type must be unsigned integer"); + static_assert(sizeof(I) == 2 && std::numeric_limits<I>::min() == 0 && std::numeric_limits<I>::max() == std::numeric_limits<uint16_t>::max(), "Unsupported BigEndian size"); + } template<typename Stream> - void Serialize(Stream &s) const { - WriteVarInt<Stream,I>(s, n); + void Serialize(Stream& s) const + { + ser_writedata16be(s, m_val); } template<typename Stream> - void Unserialize(Stream& s) { - n = ReadVarInt<Stream,I>(s); + void Unserialize(Stream& s) + { + m_val = ser_readdata16be(s); } }; @@ -462,8 +508,11 @@ public: } }; +template<VarIntMode Mode=VarIntMode::DEFAULT, typename I> +CVarInt<Mode, I> WrapVarInt(I& n) { return CVarInt<Mode, I>{n}; } + template<typename I> -CVarInt<I> WrapVarInt(I& n) { return CVarInt<I>(n); } +BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); } /** * Forward declarations @@ -539,7 +588,7 @@ inline void Serialize(Stream& os, const T& a) } template<typename Stream, typename T> -inline void Unserialize(Stream& is, T& a) +inline void Unserialize(Stream& is, T&& a) { a.Unserialize(is); } @@ -825,19 +874,6 @@ 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); -} - - @@ -897,17 +933,11 @@ 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) +void SerializeMany(Stream& s, const Arg& arg, const Args&... args) { - ::Serialize(s, std::forward<Arg>(arg)); - ::SerializeMany(s, std::forward<Args>(args)...); + ::Serialize(s, arg); + ::SerializeMany(s, args...); } template<typename Stream> @@ -915,27 +945,21 @@ inline void UnserializeMany(Stream& s) { } -template<typename Stream, typename Arg> -inline void UnserializeMany(Stream& s, Arg& arg) -{ - ::Unserialize(s, arg); -} - template<typename Stream, typename Arg, typename... Args> -inline void UnserializeMany(Stream& s, Arg& arg, Args&... args) +inline void UnserializeMany(Stream& s, Arg&& arg, Args&&... args) { ::Unserialize(s, arg); ::UnserializeMany(s, args...); } template<typename Stream, typename... Args> -inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, Args&&... args) +inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, const Args&... args) { - ::SerializeMany(s, std::forward<Args>(args)...); + ::SerializeMany(s, args...); } template<typename Stream, typename... Args> -inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&... args) +inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&&... args) { ::UnserializeMany(s, args...); } |