Changeset View
Changeset View
Standalone View
Standalone View
src/serialize.h
Show First 20 Lines • Show All 185 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
template <typename X> const X &ReadWriteAsHelper(const X &x) { | template <typename X> const X &ReadWriteAsHelper(const X &x) { | ||||
return x; | return x; | ||||
} | } | ||||
#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) | #define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) | ||||
#define READWRITEAS(type, obj) \ | #define READWRITEAS(type, obj) \ | ||||
(::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj))) | (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj))) | ||||
#define SER_READ(obj, code) \ | |||||
::SerRead( \ | |||||
s, ser_action, obj, \ | |||||
[&](Stream &s, typename std::remove_const<Type>::type &obj) { code; }) | |||||
#define SER_WRITE(obj, code) \ | |||||
::SerWrite(s, ser_action, obj, [&](Stream &s, const Type &obj) { code; }) | |||||
/** | /** | ||||
* Implement three methods for serializable objects. These are actually wrappers | * Implement three methods for serializable objects. These are actually wrappers | ||||
* over "SerializationOp" template, which implements the body of each class' | * over "SerializationOp" template, which implements the body of each class' | ||||
* serialization code. Adding "ADD_SERIALIZE_METHODS" in the body of the class | * serialization code. Adding "ADD_SERIALIZE_METHODS" in the body of the class | ||||
* causes these wrappers to be added as members. | * causes these wrappers to be added as members. | ||||
*/ | */ | ||||
#define ADD_SERIALIZE_METHODS \ | #define ADD_SERIALIZE_METHODS \ | ||||
▲ Show 20 Lines • Show All 415 Lines • ▼ Show 20 Lines | template <typename Stream, typename I> void Ser(Stream &s, I v) { | ||||
WriteVarInt<Stream, Mode, typename std::remove_cv<I>::type>(s, v); | WriteVarInt<Stream, Mode, typename std::remove_cv<I>::type>(s, v); | ||||
} | } | ||||
template <typename Stream, typename I> void Unser(Stream &s, I &v) { | template <typename Stream, typename I> void Unser(Stream &s, I &v) { | ||||
v = ReadVarInt<Stream, Mode, typename std::remove_cv<I>::type>(s); | v = ReadVarInt<Stream, Mode, typename std::remove_cv<I>::type>(s); | ||||
} | } | ||||
}; | }; | ||||
template <int Bytes> struct CustomUintFormatter { | /** | ||||
* Serialization wrapper class for custom integers and enums. | |||||
* | |||||
* It permits specifying the serialized size (1 to 8 bytes) and endianness. | |||||
* | |||||
* Use the big endian mode for values 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. | |||||
*/ | |||||
template <int Bytes, bool BigEndian = false> struct CustomUintFormatter { | |||||
static_assert(Bytes > 0 && Bytes <= 8, | static_assert(Bytes > 0 && Bytes <= 8, | ||||
"CustomUintFormatter Bytes out of range"); | "CustomUintFormatter Bytes out of range"); | ||||
static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes)); | static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes)); | ||||
template <typename Stream, typename I> void Ser(Stream &s, I v) { | template <typename Stream, typename I> void Ser(Stream &s, I v) { | ||||
if (v < 0 || v > MAX) { | if (v < 0 || v > MAX) { | ||||
throw std::ios_base::failure( | throw std::ios_base::failure( | ||||
"CustomUintFormatter value out of range"); | "CustomUintFormatter value out of range"); | ||||
} | } | ||||
if (BigEndian) { | |||||
uint64_t raw = htobe64(v); | |||||
s.write(((const char *)&raw) + 8 - Bytes, Bytes); | |||||
} else { | |||||
uint64_t raw = htole64(v); | uint64_t raw = htole64(v); | ||||
s.write((const char *)&raw, Bytes); | s.write((const char *)&raw, Bytes); | ||||
} | } | ||||
} | |||||
template <typename Stream, typename I> void Unser(Stream &s, I &v) { | template <typename Stream, typename I> void Unser(Stream &s, I &v) { | ||||
static_assert(std::numeric_limits<I>::max() >= MAX && | using U = typename std::conditional<std::is_enum<I>::value, | ||||
std::numeric_limits<I>::min() <= 0, | std::underlying_type<I>, | ||||
"CustomUintFormatter type too small"); | std::common_type<I>>::type::type; | ||||
static_assert(std::numeric_limits<U>::max() >= MAX && | |||||
std::numeric_limits<U>::min() <= 0, | |||||
"Assigned type too small"); | |||||
uint64_t raw = 0; | uint64_t raw = 0; | ||||
if (BigEndian) { | |||||
s.read(((char *)&raw) + 8 - Bytes, Bytes); | |||||
v = static_cast<I>(be64toh(raw)); | |||||
} else { | |||||
s.read((char *)&raw, Bytes); | s.read((char *)&raw, Bytes); | ||||
v = le64toh(raw); | v = static_cast<I>(le64toh(raw)); | ||||
} | |||||
}; | |||||
/** 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 BigEndian { | |||||
protected: | |||||
I &m_val; | |||||
public: | |||||
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 { | |||||
ser_writedata16be(s, m_val); | |||||
} | } | ||||
template <typename Stream> void Unserialize(Stream &s) { | |||||
m_val = ser_readdata16be(s); | |||||
} | } | ||||
}; | }; | ||||
template <int Bytes> | |||||
using BigEndianFormatter = CustomUintFormatter<Bytes, true>; | |||||
/** Formatter for integers in CompactSize format. */ | /** Formatter for integers in CompactSize format. */ | ||||
struct CompactSizeFormatter { | struct CompactSizeFormatter { | ||||
template <typename Stream, typename I> void Unser(Stream &s, I &v) { | template <typename Stream, typename I> void Unser(Stream &s, I &v) { | ||||
uint64_t n = ReadCompactSize<Stream>(s); | uint64_t n = ReadCompactSize<Stream>(s); | ||||
if (n < std::numeric_limits<I>::min() || | if (n < std::numeric_limits<I>::min() || | ||||
n > std::numeric_limits<I>::max()) { | n > std::numeric_limits<I>::max()) { | ||||
throw std::ios_base::failure("CompactSize exceeds limit of type"); | throw std::ios_base::failure("CompactSize exceeds limit of type"); | ||||
} | } | ||||
Show All 32 Lines | public: | ||||
template <typename Stream> void Serialize(Stream &s) const { | template <typename Stream> void Serialize(Stream &s) const { | ||||
WriteCompactSize(s, string.size()); | WriteCompactSize(s, string.size()); | ||||
if (!string.empty()) { | if (!string.empty()) { | ||||
s.write((char *)string.data(), string.size()); | s.write((char *)string.data(), string.size()); | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
template <typename I> BigEndian<I> WrapBigEndian(I &n) { | |||||
return BigEndian<I>(n); | |||||
} | |||||
/** | /** | ||||
* Formatter to serialize/deserialize vector elements using another formatter | * Formatter to serialize/deserialize vector elements using another formatter | ||||
* | * | ||||
* Example: | * Example: | ||||
* struct X { | * struct X { | ||||
* std::vector<uint64_t> v; | * std::vector<uint64_t> v; | ||||
* SERIALIZE_METHODS(X, obj) { | * SERIALIZE_METHODS(X, obj) { | ||||
* READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); | * READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); | ||||
▲ Show 20 Lines • Show All 436 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
template <typename Stream, typename... Args> | template <typename Stream, typename... Args> | ||||
inline void SerReadWriteMany(Stream &s, CSerActionUnserialize ser_action, | inline void SerReadWriteMany(Stream &s, CSerActionUnserialize ser_action, | ||||
Args &&... args) { | Args &&... args) { | ||||
::UnserializeMany(s, args...); | ::UnserializeMany(s, args...); | ||||
} | } | ||||
template <typename Stream, typename Type, typename Fn> | |||||
inline void SerRead(Stream &s, CSerActionSerialize ser_action, Type &&, Fn &&) { | |||||
} | |||||
template <typename Stream, typename Type, typename Fn> | |||||
inline void SerRead(Stream &s, CSerActionUnserialize ser_action, Type &&obj, | |||||
Fn &&fn) { | |||||
fn(s, std::forward<Type>(obj)); | |||||
} | |||||
template <typename Stream, typename Type, typename Fn> | |||||
inline void SerWrite(Stream &s, CSerActionSerialize ser_action, Type &&obj, | |||||
Fn &&fn) { | |||||
fn(s, std::forward<Type>(obj)); | |||||
} | |||||
template <typename Stream, typename Type, typename Fn> | |||||
inline void SerWrite(Stream &s, CSerActionUnserialize ser_action, Type &&, | |||||
Fn &&) {} | |||||
template <typename I> inline void WriteVarInt(CSizeComputer &s, I n) { | template <typename I> inline void WriteVarInt(CSizeComputer &s, I n) { | ||||
s.seek(GetSizeOfVarInt<I>(n)); | s.seek(GetSizeOfVarInt<I>(n)); | ||||
} | } | ||||
inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize) { | inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize) { | ||||
s.seek(GetSizeOfCompactSize(nSize)); | s.seek(GetSizeOfCompactSize(nSize)); | ||||
} | } | ||||
Show All 12 Lines |