serialize.h raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-2022 The Limenka developers
   3  // Distributed under the MIT software license, see the accompanying
   4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  #ifndef LIMENKA_SERIALIZE_H
   7  #define LIMENKA_SERIALIZE_H
   8  
   9  #include <attributes.h>
  10  #include <compat/assumptions.h> // IWYU pragma: keep
  11  #include <compat/endian.h>
  12  #include <prevector.h>
  13  #include <span.h>
  14  
  15  #include <algorithm>
  16  #include <concepts>
  17  #include <cstdint>
  18  #include <cstring>
  19  #include <ios>
  20  #include <limits>
  21  #include <map>
  22  #include <memory>
  23  #include <set>
  24  #include <string>
  25  #include <utility>
  26  #include <vector>
  27  
  28  /**
  29   * The maximum size of a serialized object in bytes or number of elements
  30   * (for eg vectors) when the size is encoded as CompactSize.
  31   */
  32  static constexpr uint64_t MAX_SIZE = 0x02000000;
  33  
  34  /** Maximum amount of memory (in bytes) to allocate at once when deserializing vectors. */
  35  static const unsigned int MAX_VECTOR_ALLOCATE = 5000000;
  36  
  37  /**
  38   * Dummy data type to identify deserializing constructors.
  39   *
  40   * By convention, a constructor of a type T with signature
  41   *
  42   *   template <typename Stream> T::T(deserialize_type, Stream& s)
  43   *
  44   * is a deserializing constructor, which builds the type by
  45   * deserializing it from s. If T contains const fields, this
  46   * is likely the only way to do so.
  47   */
  48  struct deserialize_type {};
  49  constexpr deserialize_type deserialize {};
  50  
  51  /*
  52   * Lowest-level serialization and conversion.
  53   */
  54  template<typename Stream> inline void ser_writedata8(Stream &s, uint8_t obj)
  55  {
  56      s.write(AsBytes(Span{&obj, 1}));
  57  }
  58  template<typename Stream> inline void ser_writedata16(Stream &s, uint16_t obj)
  59  {
  60      obj = htole16_internal(obj);
  61      s.write(AsBytes(Span{&obj, 1}));
  62  }
  63  template<typename Stream> inline void ser_writedata16be(Stream &s, uint16_t obj)
  64  {
  65      obj = htobe16_internal(obj);
  66      s.write(AsBytes(Span{&obj, 1}));
  67  }
  68  template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
  69  {
  70      obj = htole32_internal(obj);
  71      s.write(AsBytes(Span{&obj, 1}));
  72  }
  73  template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
  74  {
  75      obj = htobe32_internal(obj);
  76      s.write(AsBytes(Span{&obj, 1}));
  77  }
  78  template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
  79  {
  80      obj = htole64_internal(obj);
  81      s.write(AsBytes(Span{&obj, 1}));
  82  }
  83  template<typename Stream> inline uint8_t ser_readdata8(Stream &s)
  84  {
  85      uint8_t obj;
  86      s.read(AsWritableBytes(Span{&obj, 1}));
  87      return obj;
  88  }
  89  template<typename Stream> inline uint16_t ser_readdata16(Stream &s)
  90  {
  91      uint16_t obj;
  92      s.read(AsWritableBytes(Span{&obj, 1}));
  93      return le16toh_internal(obj);
  94  }
  95  template<typename Stream> inline uint16_t ser_readdata16be(Stream &s)
  96  {
  97      uint16_t obj;
  98      s.read(AsWritableBytes(Span{&obj, 1}));
  99      return be16toh_internal(obj);
 100  }
 101  template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
 102  {
 103      uint32_t obj;
 104      s.read(AsWritableBytes(Span{&obj, 1}));
 105      return le32toh_internal(obj);
 106  }
 107  template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
 108  {
 109      uint32_t obj;
 110      s.read(AsWritableBytes(Span{&obj, 1}));
 111      return be32toh_internal(obj);
 112  }
 113  template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
 114  {
 115      uint64_t obj;
 116      s.read(AsWritableBytes(Span{&obj, 1}));
 117      return le64toh_internal(obj);
 118  }
 119  
 120  
 121  class SizeComputer;
 122  
 123  /**
 124   * Convert any argument to a reference to X, maintaining constness.
 125   *
 126   * This can be used in serialization code to invoke a base class's
 127   * serialization routines.
 128   *
 129   * Example use:
 130   *   class Base { ... };
 131   *   class Child : public Base {
 132   *     int m_data;
 133   *   public:
 134   *     SERIALIZE_METHODS(Child, obj) {
 135   *       READWRITE(AsBase<Base>(obj), obj.m_data);
 136   *     }
 137   *   };
 138   *
 139   * static_cast cannot easily be used here, as the type of Obj will be const Child&
 140   * during serialization and Child& during deserialization. AsBase will convert to
 141   * const Base& and Base& appropriately.
 142   */
 143  template <class Out, class In>
 144  Out& AsBase(In& x)
 145  {
 146      static_assert(std::is_base_of_v<Out, In>);
 147      return x;
 148  }
 149  template <class Out, class In>
 150  const Out& AsBase(const In& x)
 151  {
 152      static_assert(std::is_base_of_v<Out, In>);
 153      return x;
 154  }
 155  
 156  #define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__))
 157  #define SER_READ(obj, code) ser_action.SerRead(s, obj, [&](Stream& s, typename std::remove_const<Type>::type& obj) { code; })
 158  #define SER_WRITE(obj, code) ser_action.SerWrite(s, obj, [&](Stream& s, const Type& obj) { code; })
 159  
 160  /**
 161   * Implement the Ser and Unser methods needed for implementing a formatter (see Using below).
 162   *
 163   * Both Ser and Unser are delegated to a single static method SerializationOps, which is polymorphic
 164   * in the serialized/deserialized type (allowing it to be const when serializing, and non-const when
 165   * deserializing).
 166   *
 167   * Example use:
 168   *   struct FooFormatter {
 169   *     FORMATTER_METHODS(Class, obj) { READWRITE(obj.val1, VARINT(obj.val2)); }
 170   *   }
 171   *   would define a class FooFormatter that defines a serialization of Class objects consisting
 172   *   of serializing its val1 member using the default serialization, and its val2 member using
 173   *   VARINT serialization. That FooFormatter can then be used in statements like
 174   *   READWRITE(Using<FooFormatter>(obj.bla)).
 175   */
 176  #define FORMATTER_METHODS(cls, obj) \
 177      template<typename Stream> \
 178      static void Ser(Stream& s, const cls& obj) { SerializationOps(obj, s, ActionSerialize{}); } \
 179      template<typename Stream> \
 180      static void Unser(Stream& s, cls& obj) { SerializationOps(obj, s, ActionUnserialize{}); } \
 181      template<typename Stream, typename Type, typename Operation> \
 182      static void SerializationOps(Type& obj, Stream& s, Operation ser_action)
 183  
 184  /**
 185   * Formatter methods can retrieve parameters attached to a stream using the
 186   * SER_PARAMS(type) macro as long as the stream is created directly or
 187   * indirectly with a parameter of that type. This permits making serialization
 188   * depend on run-time context in a type-safe way.
 189   *
 190   * Example use:
 191   *   struct BarParameter { bool fancy; ... };
 192   *   struct Bar { ... };
 193   *   struct FooFormatter {
 194   *     FORMATTER_METHODS(Bar, obj) {
 195   *       auto& param = SER_PARAMS(BarParameter);
 196   *       if (param.fancy) {
 197   *         READWRITE(VARINT(obj.value));
 198   *       } else {
 199   *         READWRITE(obj.value);
 200   *       }
 201   *     }
 202   *   };
 203   * which would then be invoked as
 204   *   READWRITE(BarParameter{...}(Using<FooFormatter>(obj.foo)))
 205   *
 206   * parameter(obj) can be invoked anywhere in the call stack; it is
 207   * passed down recursively into all serialization code, until another
 208   * serialization parameter overrides it.
 209   *
 210   * Parameters will be implicitly converted where appropriate. This means that
 211   * "parent" serialization code can use a parameter that derives from, or is
 212   * convertible to, a "child" formatter's parameter type.
 213   *
 214   * Compilation will fail in any context where serialization is invoked but
 215   * no parameter of a type convertible to BarParameter is provided.
 216   */
 217  #define SER_PARAMS(type) (s.template GetParams<type>())
 218  
 219  #define BASE_SERIALIZE_METHODS(cls)                                                                 \
 220      template <typename Stream>                                                                      \
 221      void Serialize(Stream& s) const                                                                 \
 222      {                                                                                               \
 223          static_assert(std::is_same<const cls&, decltype(*this)>::value, "Serialize type mismatch"); \
 224          Ser(s, *this);                                                                              \
 225      }                                                                                               \
 226      template <typename Stream>                                                                      \
 227      void Unserialize(Stream& s)                                                                     \
 228      {                                                                                               \
 229          static_assert(std::is_same<cls&, decltype(*this)>::value, "Unserialize type mismatch");     \
 230          Unser(s, *this);                                                                            \
 231      }
 232  
 233  /**
 234   * Implement the Serialize and Unserialize methods by delegating to a single templated
 235   * static method that takes the to-be-(de)serialized object as a parameter. This approach
 236   * has the advantage that the constness of the object becomes a template parameter, and
 237   * thus allows a single implementation that sees the object as const for serializing
 238   * and non-const for deserializing, without casts.
 239   */
 240  #define SERIALIZE_METHODS(cls, obj) \
 241      BASE_SERIALIZE_METHODS(cls)     \
 242      FORMATTER_METHODS(cls, obj)
 243  
 244  // Templates for serializing to anything that looks like a stream,
 245  // i.e. anything that supports .read(Span<std::byte>) and .write(Span<const std::byte>)
 246  //
 247  // clang-format off
 248  
 249  // Typically int8_t and char are distinct types, but some systems may define int8_t
 250  // in terms of char. Forbid serialization of char in the typical case, but allow it if
 251  // it's the only way to describe an int8_t.
 252  template<class T>
 253  concept CharNotInt8 = std::same_as<T, char> && !std::same_as<T, int8_t>;
 254  
 255  template <typename Stream, CharNotInt8 V> void Serialize(Stream&, V) = delete; // char serialization forbidden. Use uint8_t or int8_t
 256  template <typename Stream> void Serialize(Stream& s, std::byte a) { ser_writedata8(s, uint8_t(a)); }
 257  template<typename Stream> inline void Serialize(Stream& s, int8_t a  ) { ser_writedata8(s, a); }
 258  template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
 259  template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); }
 260  template<typename Stream> inline void Serialize(Stream& s, uint16_t a) { ser_writedata16(s, a); }
 261  template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_writedata32(s, a); }
 262  template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
 263  template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
 264  template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
 265  // CAmount (__int128) truncates to int64_t for wire format (Bitcoin protocol is 8-byte amounts).
 266  // Any value outside the int64 range would serialize identically to a small
 267  // value (and sign-extend on read), silently corrupting hashes and disk
 268  // records - reject it instead.  Field formats that must carry 128-bit
 269  // values (e.g. the CT kernel fee) serialize their halves explicitly.
 270  template<typename Stream> inline void Serialize(Stream& s, __int128 a)
 271  {
 272      assert(a == static_cast<__int128>(static_cast<int64_t>(a)));
 273      ser_writedata64(s, static_cast<int64_t>(a));
 274  }
 275  template <typename Stream, BasicByte B, int N> void Serialize(Stream& s, const B (&a)[N]) { s.write(MakeByteSpan(a)); }
 276  template <typename Stream, BasicByte B, std::size_t N> void Serialize(Stream& s, const std::array<B, N>& a) { s.write(MakeByteSpan(a)); }
 277  template <typename Stream, BasicByte B, std::size_t N> void Serialize(Stream& s, std::span<B, N> span) { s.write(std::as_bytes(span)); }
 278  template <typename Stream, BasicByte B> void Serialize(Stream& s, Span<B> span) { s.write(AsBytes(span)); }
 279  
 280  template <typename Stream, CharNotInt8 V> void Unserialize(Stream&, V) = delete; // char serialization forbidden. Use uint8_t or int8_t
 281  template <typename Stream> void Unserialize(Stream& s, std::byte& a) { a = std::byte{ser_readdata8(s)}; }
 282  template<typename Stream> inline void Unserialize(Stream& s, int8_t& a  ) { a = ser_readdata8(s); }
 283  template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
 284  template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); }
 285  template<typename Stream> inline void Unserialize(Stream& s, uint16_t& a) { a = ser_readdata16(s); }
 286  template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a = ser_readdata32(s); }
 287  template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
 288  template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
 289  template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
 290  // CAmount (__int128) reads 8 bytes from wire (Bitcoin protocol), sign-extends.
 291  template<typename Stream> inline void Unserialize(Stream& s, __int128& a) { a = static_cast<int64_t>(ser_readdata64(s)); }
 292  template <typename Stream, BasicByte B, int N> void Unserialize(Stream& s, B (&a)[N]) { s.read(MakeWritableByteSpan(a)); }
 293  template <typename Stream, BasicByte B, std::size_t N> void Unserialize(Stream& s, std::array<B, N>& a) { s.read(MakeWritableByteSpan(a)); }
 294  template <typename Stream, BasicByte B, std::size_t N> void Unserialize(Stream& s, std::span<B, N> span) { s.read(std::as_writable_bytes(span)); }
 295  template <typename Stream, BasicByte B> void Unserialize(Stream& s, Span<B> span) { s.read(AsWritableBytes(span)); }
 296  
 297  template <typename Stream> inline void Serialize(Stream& s, bool a) { uint8_t f = a; ser_writedata8(s, f); }
 298  template <typename Stream> inline void Unserialize(Stream& s, bool& a) { uint8_t f = ser_readdata8(s); a = f; }
 299  // clang-format on
 300  
 301  
 302  /**
 303   * Compact Size
 304   * size <  253        -- 1 byte
 305   * size <= USHRT_MAX  -- 3 bytes  (253 + 2 bytes)
 306   * size <= UINT_MAX   -- 5 bytes  (254 + 4 bytes)
 307   * size >  UINT_MAX   -- 9 bytes  (255 + 8 bytes)
 308   */
 309  constexpr inline unsigned int GetSizeOfCompactSize(uint64_t nSize)
 310  {
 311      if (nSize < 253)             return sizeof(unsigned char);
 312      else if (nSize <= std::numeric_limits<uint16_t>::max()) return sizeof(unsigned char) + sizeof(uint16_t);
 313      else if (nSize <= std::numeric_limits<unsigned int>::max())  return sizeof(unsigned char) + sizeof(unsigned int);
 314      else                         return sizeof(unsigned char) + sizeof(uint64_t);
 315  }
 316  
 317  inline void WriteCompactSize(SizeComputer& os, uint64_t nSize);
 318  
 319  template<typename Stream>
 320  void WriteCompactSize(Stream& os, uint64_t nSize)
 321  {
 322      if (nSize < 253)
 323      {
 324          ser_writedata8(os, nSize);
 325      }
 326      else if (nSize <= std::numeric_limits<uint16_t>::max())
 327      {
 328          ser_writedata8(os, 253);
 329          ser_writedata16(os, nSize);
 330      }
 331      else if (nSize <= std::numeric_limits<unsigned int>::max())
 332      {
 333          ser_writedata8(os, 254);
 334          ser_writedata32(os, nSize);
 335      }
 336      else
 337      {
 338          ser_writedata8(os, 255);
 339          ser_writedata64(os, nSize);
 340      }
 341      return;
 342  }
 343  
 344  /**
 345   * Decode a CompactSize-encoded variable-length integer.
 346   *
 347   * As these are primarily used to encode the size of vector-like serializations, by default a range
 348   * check is performed. When used as a generic number encoding, range_check should be set to false.
 349   */
 350  template<typename Stream>
 351  uint64_t ReadCompactSize(Stream& is, bool range_check = true)
 352  {
 353      uint8_t chSize = ser_readdata8(is);
 354      uint64_t nSizeRet = 0;
 355      if (chSize < 253)
 356      {
 357          nSizeRet = chSize;
 358      }
 359      else if (chSize == 253)
 360      {
 361          nSizeRet = ser_readdata16(is);
 362          if (nSizeRet < 253)
 363              throw std::ios_base::failure("non-canonical ReadCompactSize()");
 364      }
 365      else if (chSize == 254)
 366      {
 367          nSizeRet = ser_readdata32(is);
 368          if (nSizeRet < 0x10000u)
 369              throw std::ios_base::failure("non-canonical ReadCompactSize()");
 370      }
 371      else
 372      {
 373          nSizeRet = ser_readdata64(is);
 374          if (nSizeRet < 0x100000000ULL)
 375              throw std::ios_base::failure("non-canonical ReadCompactSize()");
 376      }
 377      if (range_check && nSizeRet > MAX_SIZE) {
 378          throw std::ios_base::failure("ReadCompactSize(): size too large");
 379      }
 380      return nSizeRet;
 381  }
 382  
 383  /**
 384   * Variable-length integers: bytes are a MSB base-128 encoding of the number.
 385   * The high bit in each byte signifies whether another digit follows. To make
 386   * sure the encoding is one-to-one, one is subtracted from all but the last digit.
 387   * Thus, the byte sequence a[] with length len, where all but the last byte
 388   * has bit 128 set, encodes the number:
 389   *
 390   *  (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1))
 391   *
 392   * Properties:
 393   * * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes)
 394   * * Every integer has exactly one encoding
 395   * * Encoding does not depend on size of original integer type
 396   * * No redundancy: every (infinite) byte sequence corresponds to a list
 397   *   of encoded integers.
 398   *
 399   * 0:         [0x00]  256:        [0x81 0x00]
 400   * 1:         [0x01]  16383:      [0xFE 0x7F]
 401   * 127:       [0x7F]  16384:      [0xFF 0x00]
 402   * 128:  [0x80 0x00]  16511:      [0xFF 0x7F]
 403   * 255:  [0x80 0x7F]  65535: [0x82 0xFE 0x7F]
 404   * 2^32:           [0x8E 0xFE 0xFE 0xFF 0x00]
 405   */
 406  
 407  /**
 408   * Mode for encoding VarInts.
 409   *
 410   * Currently there is no support for signed encodings. The default mode will not
 411   * compile with signed values, and the legacy "nonnegative signed" mode will
 412   * accept signed values, but improperly encode and decode them if they are
 413   * negative. In the future, the DEFAULT mode could be extended to support
 414   * negative numbers in a backwards compatible way, and additional modes could be
 415   * added to support different varint formats (e.g. zigzag encoding).
 416   */
 417  enum class VarIntMode { DEFAULT, NONNEGATIVE_SIGNED };
 418  
 419  template <VarIntMode Mode, typename I>
 420  struct CheckVarIntMode {
 421      constexpr CheckVarIntMode()
 422      {
 423          static_assert(Mode != VarIntMode::DEFAULT || std::is_unsigned<I>::value, "Unsigned type required with mode DEFAULT.");
 424          static_assert(Mode != VarIntMode::NONNEGATIVE_SIGNED || std::is_signed<I>::value, "Signed type required with mode NONNEGATIVE_SIGNED.");
 425      }
 426  };
 427  
 428  template<VarIntMode Mode, typename I>
 429  inline unsigned int GetSizeOfVarInt(I n)
 430  {
 431      CheckVarIntMode<Mode, I>();
 432      int nRet = 0;
 433      while(true) {
 434          nRet++;
 435          if (n <= 0x7F)
 436              break;
 437          n = (n >> 7) - 1;
 438      }
 439      return nRet;
 440  }
 441  
 442  template<typename I>
 443  inline void WriteVarInt(SizeComputer& os, I n);
 444  
 445  template<typename Stream, VarIntMode Mode, typename I>
 446  void WriteVarInt(Stream& os, I n)
 447  {
 448      CheckVarIntMode<Mode, I>();
 449      unsigned char tmp[(sizeof(n)*8+6)/7];
 450      int len=0;
 451      while(true) {
 452          tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00);
 453          if (n <= 0x7F)
 454              break;
 455          n = (n >> 7) - 1;
 456          len++;
 457      }
 458      do {
 459          ser_writedata8(os, tmp[len]);
 460      } while(len--);
 461  }
 462  
 463  template<typename Stream, VarIntMode Mode, typename I>
 464  I ReadVarInt(Stream& is)
 465  {
 466      CheckVarIntMode<Mode, I>();
 467      I n = 0;
 468      while(true) {
 469          unsigned char chData = ser_readdata8(is);
 470          if (n > (std::numeric_limits<I>::max() >> 7)) {
 471             throw std::ios_base::failure("ReadVarInt(): size too large");
 472          }
 473          n = (n << 7) | (chData & 0x7F);
 474          if (chData & 0x80) {
 475              if (n == std::numeric_limits<I>::max()) {
 476                  throw std::ios_base::failure("ReadVarInt(): size too large");
 477              }
 478              n++;
 479          } else {
 480              return n;
 481          }
 482      }
 483  }
 484  
 485  /** Simple wrapper class to serialize objects using a formatter; used by Using(). */
 486  template<typename Formatter, typename T>
 487  class Wrapper
 488  {
 489      static_assert(std::is_lvalue_reference<T>::value, "Wrapper needs an lvalue reference type T");
 490  protected:
 491      T m_object;
 492  public:
 493      explicit Wrapper(T obj) : m_object(obj) {}
 494      template<typename Stream> void Serialize(Stream &s) const { Formatter().Ser(s, m_object); }
 495      template<typename Stream> void Unserialize(Stream &s) { Formatter().Unser(s, m_object); }
 496  };
 497  
 498  /** Cause serialization/deserialization of an object to be done using a specified formatter class.
 499   *
 500   * To use this, you need a class Formatter that has public functions Ser(stream, const object&) for
 501   * serialization, and Unser(stream, object&) for deserialization. Serialization routines (inside
 502   * READWRITE, or directly with << and >> operators), can then use Using<Formatter>(object).
 503   *
 504   * This works by constructing a Wrapper<Formatter, T>-wrapped version of object, where T is
 505   * const during serialization, and non-const during deserialization, which maintains const
 506   * correctness.
 507   */
 508  template<typename Formatter, typename T>
 509  static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&>(t); }
 510  
 511  #define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj)
 512  #define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
 513  #define COMPACTSIZE(obj) Using<CompactSizeFormatter<true>>(obj)
 514  #define LIMITED_STRING(obj,n) Using<LimitedStringFormatter<n>>(obj)
 515  
 516  /** Serialization wrapper class for integers in VarInt format. */
 517  template<VarIntMode Mode>
 518  struct VarIntFormatter
 519  {
 520      template<typename Stream, typename I> void Ser(Stream &s, I v)
 521      {
 522          WriteVarInt<Stream,Mode,typename std::remove_cv<I>::type>(s, v);
 523      }
 524  
 525      template<typename Stream, typename I> void Unser(Stream& s, I& v)
 526      {
 527          v = ReadVarInt<Stream,Mode,typename std::remove_cv<I>::type>(s);
 528      }
 529  };
 530  
 531  /** Serialization wrapper class for custom integers and enums.
 532   *
 533   * It permits specifying the serialized size (1 to 8 bytes) and endianness.
 534   *
 535   * Use the big endian mode for values that are stored in memory in native
 536   * byte order, but serialized in big endian notation. This is only intended
 537   * to implement serializers that are compatible with existing formats, and
 538   * its use is not recommended for new data structures.
 539   */
 540  template<int Bytes, bool BigEndian = false>
 541  struct CustomUintFormatter
 542  {
 543      static_assert(Bytes > 0 && Bytes <= 8, "CustomUintFormatter Bytes out of range");
 544      static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes));
 545  
 546      template <typename Stream, typename I> void Ser(Stream& s, I v)
 547      {
 548          if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
 549          if (BigEndian) {
 550              uint64_t raw = htobe64_internal(v);
 551              s.write(AsBytes(Span{&raw, 1}).last(Bytes));
 552          } else {
 553              uint64_t raw = htole64_internal(v);
 554              s.write(AsBytes(Span{&raw, 1}).first(Bytes));
 555          }
 556      }
 557  
 558      template <typename Stream, typename I> void Unser(Stream& s, I& v)
 559      {
 560          using U = typename std::conditional<std::is_enum<I>::value, std::underlying_type<I>, std::common_type<I>>::type::type;
 561          static_assert(std::numeric_limits<U>::max() >= MAX && std::numeric_limits<U>::min() <= 0, "Assigned type too small");
 562          uint64_t raw = 0;
 563          if (BigEndian) {
 564              s.read(AsWritableBytes(Span{&raw, 1}).last(Bytes));
 565              v = static_cast<I>(be64toh_internal(raw));
 566          } else {
 567              s.read(AsWritableBytes(Span{&raw, 1}).first(Bytes));
 568              v = static_cast<I>(le64toh_internal(raw));
 569          }
 570      }
 571  };
 572  
 573  template<int Bytes> using BigEndianFormatter = CustomUintFormatter<Bytes, true>;
 574  
 575  /** Formatter for integers in CompactSize format. */
 576  template<bool RangeCheck>
 577  struct CompactSizeFormatter
 578  {
 579      template<typename Stream, typename I>
 580      void Unser(Stream& s, I& v)
 581      {
 582          uint64_t n = ReadCompactSize<Stream>(s, RangeCheck);
 583          if (n < std::numeric_limits<I>::min() || n > std::numeric_limits<I>::max()) {
 584              throw std::ios_base::failure("CompactSize exceeds limit of type");
 585          }
 586          v = n;
 587      }
 588  
 589      template<typename Stream, typename I>
 590      void Ser(Stream& s, I v)
 591      {
 592          static_assert(std::is_unsigned<I>::value, "CompactSize only supported for unsigned integers");
 593          static_assert(std::numeric_limits<I>::max() <= std::numeric_limits<uint64_t>::max(), "CompactSize only supports 64-bit integers and below");
 594  
 595          WriteCompactSize<Stream>(s, v);
 596      }
 597  };
 598  
 599  template <typename U, bool LOSSY = false>
 600  struct ChronoFormatter {
 601      template <typename Stream, typename Tp>
 602      void Unser(Stream& s, Tp& tp)
 603      {
 604          U u;
 605          s >> u;
 606          // Lossy deserialization does not make sense, so force Wnarrowing
 607          tp = Tp{typename Tp::duration{typename Tp::duration::rep{u}}};
 608      }
 609      template <typename Stream, typename Tp>
 610      void Ser(Stream& s, Tp tp)
 611      {
 612          if constexpr (LOSSY) {
 613              s << U(tp.time_since_epoch().count());
 614          } else {
 615              s << U{tp.time_since_epoch().count()};
 616          }
 617      }
 618  };
 619  template <typename U>
 620  using LossyChronoFormatter = ChronoFormatter<U, true>;
 621  
 622  class CompactSizeWriter
 623  {
 624  protected:
 625      uint64_t n;
 626  public:
 627      explicit CompactSizeWriter(uint64_t n_in) : n(n_in) { }
 628  
 629      template<typename Stream>
 630      void Serialize(Stream &s) const {
 631          WriteCompactSize<Stream>(s, n);
 632      }
 633  };
 634  
 635  template<size_t Limit>
 636  struct LimitedStringFormatter
 637  {
 638      template<typename Stream>
 639      void Unser(Stream& s, std::string& v)
 640      {
 641          size_t size = ReadCompactSize(s);
 642          if (size > Limit) {
 643              throw std::ios_base::failure("String length limit exceeded");
 644          }
 645          v.resize(size);
 646          if (size != 0) s.read(MakeWritableByteSpan(v));
 647      }
 648  
 649      template<typename Stream>
 650      void Ser(Stream& s, const std::string& v)
 651      {
 652          s << v;
 653      }
 654  };
 655  
 656  /** Formatter to serialize/deserialize vector elements using another formatter
 657   *
 658   * Example:
 659   *   struct X {
 660   *     std::vector<uint64_t> v;
 661   *     SERIALIZE_METHODS(X, obj) { READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); }
 662   *   };
 663   * will define a struct that contains a vector of uint64_t, which is serialized
 664   * as a vector of VarInt-encoded integers.
 665   *
 666   * V is not required to be an std::vector type. It works for any class that
 667   * exposes a value_type, size, reserve, emplace_back, back, and const iterators.
 668   */
 669  template<class Formatter>
 670  struct VectorFormatter
 671  {
 672      template<typename Stream, typename V>
 673      void Ser(Stream& s, const V& v)
 674      {
 675          Formatter formatter;
 676          WriteCompactSize(s, v.size());
 677          for (const typename V::value_type& elem : v) {
 678              formatter.Ser(s, elem);
 679          }
 680      }
 681  
 682      template<typename Stream, typename V>
 683      void Unser(Stream& s, V& v)
 684      {
 685          Formatter formatter;
 686          v.clear();
 687          size_t size = ReadCompactSize(s);
 688          size_t allocated = 0;
 689          while (allocated < size) {
 690              // For DoS prevention, do not blindly allocate as much as the stream claims to contain.
 691              // Instead, allocate in 5MiB batches, so that an attacker actually needs to provide
 692              // X MiB of data to make us allocate X+5 Mib.
 693              static_assert(sizeof(typename V::value_type) <= MAX_VECTOR_ALLOCATE, "Vector element size too large");
 694              allocated = std::min(size, allocated + MAX_VECTOR_ALLOCATE / sizeof(typename V::value_type));
 695              v.reserve(allocated);
 696              while (v.size() < allocated) {
 697                  v.emplace_back();
 698                  formatter.Unser(s, v.back());
 699              }
 700          }
 701      };
 702  };
 703  
 704  /**
 705   * Forward declarations
 706   */
 707  
 708  /**
 709   *  string
 710   */
 711  template<typename Stream, typename C> void Serialize(Stream& os, const std::basic_string<C>& str);
 712  template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str);
 713  
 714  /**
 715   * prevector
 716   */
 717  template<typename Stream, unsigned int N, typename T> inline void Serialize(Stream& os, const prevector<N, T>& v);
 718  template<typename Stream, unsigned int N, typename T> inline void Unserialize(Stream& is, prevector<N, T>& v);
 719  
 720  /**
 721   * vector
 722   */
 723  template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v);
 724  template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v);
 725  
 726  /**
 727   * pair
 728   */
 729  template<typename Stream, typename K, typename T> void Serialize(Stream& os, const std::pair<K, T>& item);
 730  template<typename Stream, typename K, typename T> void Unserialize(Stream& is, std::pair<K, T>& item);
 731  
 732  /**
 733   * map
 734   */
 735  template<typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream& os, const std::map<K, T, Pred, A>& m);
 736  template<typename Stream, typename K, typename T, typename Pred, typename A> void Unserialize(Stream& is, std::map<K, T, Pred, A>& m);
 737  
 738  /**
 739   * set
 740   */
 741  template<typename Stream, typename K, typename Pred, typename A> void Serialize(Stream& os, const std::set<K, Pred, A>& m);
 742  template<typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream& is, std::set<K, Pred, A>& m);
 743  
 744  /**
 745   * shared_ptr
 746   */
 747  template<typename Stream, typename T> void Serialize(Stream& os, const std::shared_ptr<const T>& p);
 748  template<typename Stream, typename T> void Unserialize(Stream& os, std::shared_ptr<const T>& p);
 749  
 750  /**
 751   * unique_ptr
 752   */
 753  template<typename Stream, typename T> void Serialize(Stream& os, const std::unique_ptr<const T>& p);
 754  template<typename Stream, typename T> void Unserialize(Stream& os, std::unique_ptr<const T>& p);
 755  
 756  
 757  /**
 758   * If none of the specialized versions above matched, default to calling member function.
 759   */
 760  template <class T, class Stream>
 761  concept Serializable = requires(T a, Stream s) { a.Serialize(s); };
 762  template <typename Stream, typename T>
 763      requires Serializable<T, Stream>
 764  void Serialize(Stream& os, const T& a)
 765  {
 766      a.Serialize(os);
 767  }
 768  
 769  template <class T, class Stream>
 770  concept Unserializable = requires(T a, Stream s) { a.Unserialize(s); };
 771  template <typename Stream, typename T>
 772      requires Unserializable<T, Stream>
 773  void Unserialize(Stream& is, T&& a)
 774  {
 775      a.Unserialize(is);
 776  }
 777  
 778  /** Default formatter. Serializes objects as themselves.
 779   *
 780   * The vector/prevector serialization code passes this to VectorFormatter
 781   * to enable reusing that logic. It shouldn't be needed elsewhere.
 782   */
 783  struct DefaultFormatter
 784  {
 785      template<typename Stream, typename T>
 786      static void Ser(Stream& s, const T& t) { Serialize(s, t); }
 787  
 788      template<typename Stream, typename T>
 789      static void Unser(Stream& s, T& t) { Unserialize(s, t); }
 790  };
 791  
 792  
 793  
 794  
 795  
 796  /**
 797   * string
 798   */
 799  template<typename Stream, typename C>
 800  void Serialize(Stream& os, const std::basic_string<C>& str)
 801  {
 802      WriteCompactSize(os, str.size());
 803      if (!str.empty())
 804          os.write(MakeByteSpan(str));
 805  }
 806  
 807  template<typename Stream, typename C>
 808  void Unserialize(Stream& is, std::basic_string<C>& str)
 809  {
 810      unsigned int nSize = ReadCompactSize(is);
 811      str.resize(nSize);
 812      if (nSize != 0)
 813          is.read(MakeWritableByteSpan(str));
 814  }
 815  
 816  
 817  
 818  /**
 819   * prevector
 820   */
 821  template <typename Stream, unsigned int N, typename T>
 822  void Serialize(Stream& os, const prevector<N, T>& v)
 823  {
 824      if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
 825          WriteCompactSize(os, v.size());
 826          if (!v.empty()) os.write(MakeByteSpan(v));
 827      } else {
 828          Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v));
 829      }
 830  }
 831  
 832  
 833  template <typename Stream, unsigned int N, typename T>
 834  void Unserialize(Stream& is, prevector<N, T>& v)
 835  {
 836      if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
 837          // Limit size per read so bogus size value won't cause out of memory
 838          v.clear();
 839          unsigned int nSize = ReadCompactSize(is);
 840          unsigned int i = 0;
 841          while (i < nSize) {
 842              unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
 843              v.resize_uninitialized(i + blk);
 844              is.read(AsWritableBytes(Span{&v[i], blk}));
 845              i += blk;
 846          }
 847      } else {
 848          Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v));
 849      }
 850  }
 851  
 852  
 853  /**
 854   * vector
 855   */
 856  template <typename Stream, typename T, typename A>
 857  void Serialize(Stream& os, const std::vector<T, A>& v)
 858  {
 859      if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
 860          WriteCompactSize(os, v.size());
 861          if (!v.empty()) os.write(MakeByteSpan(v));
 862      } else if constexpr (std::is_same_v<T, bool>) {
 863          // A special case for std::vector<bool>, as dereferencing
 864          // std::vector<bool>::const_iterator does not result in a const bool&
 865          // due to std::vector's special casing for bool arguments.
 866          WriteCompactSize(os, v.size());
 867          for (bool elem : v) {
 868              ::Serialize(os, elem);
 869          }
 870      } else {
 871          Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v));
 872      }
 873  }
 874  
 875  
 876  template <typename Stream, typename T, typename A>
 877  void Unserialize(Stream& is, std::vector<T, A>& v)
 878  {
 879      if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
 880          // Limit size per read so bogus size value won't cause out of memory
 881          v.clear();
 882          unsigned int nSize = ReadCompactSize(is);
 883          unsigned int i = 0;
 884          while (i < nSize) {
 885              unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
 886              v.resize(i + blk);
 887              is.read(AsWritableBytes(Span{&v[i], blk}));
 888              i += blk;
 889          }
 890      } else {
 891          Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v));
 892      }
 893  }
 894  
 895  
 896  /**
 897   * pair
 898   */
 899  template<typename Stream, typename K, typename T>
 900  void Serialize(Stream& os, const std::pair<K, T>& item)
 901  {
 902      Serialize(os, item.first);
 903      Serialize(os, item.second);
 904  }
 905  
 906  template<typename Stream, typename K, typename T>
 907  void Unserialize(Stream& is, std::pair<K, T>& item)
 908  {
 909      Unserialize(is, item.first);
 910      Unserialize(is, item.second);
 911  }
 912  
 913  
 914  
 915  /**
 916   * map
 917   */
 918  template<typename Stream, typename K, typename T, typename Pred, typename A>
 919  void Serialize(Stream& os, const std::map<K, T, Pred, A>& m)
 920  {
 921      WriteCompactSize(os, m.size());
 922      for (const auto& entry : m)
 923          Serialize(os, entry);
 924  }
 925  
 926  template<typename Stream, typename K, typename T, typename Pred, typename A>
 927  void Unserialize(Stream& is, std::map<K, T, Pred, A>& m)
 928  {
 929      m.clear();
 930      unsigned int nSize = ReadCompactSize(is);
 931      typename std::map<K, T, Pred, A>::iterator mi = m.begin();
 932      for (unsigned int i = 0; i < nSize; i++)
 933      {
 934          std::pair<K, T> item;
 935          Unserialize(is, item);
 936          mi = m.insert(mi, item);
 937      }
 938  }
 939  
 940  
 941  
 942  /**
 943   * set
 944   */
 945  template<typename Stream, typename K, typename Pred, typename A>
 946  void Serialize(Stream& os, const std::set<K, Pred, A>& m)
 947  {
 948      WriteCompactSize(os, m.size());
 949      for (typename std::set<K, Pred, A>::const_iterator it = m.begin(); it != m.end(); ++it)
 950          Serialize(os, (*it));
 951  }
 952  
 953  template<typename Stream, typename K, typename Pred, typename A>
 954  void Unserialize(Stream& is, std::set<K, Pred, A>& m)
 955  {
 956      m.clear();
 957      unsigned int nSize = ReadCompactSize(is);
 958      typename std::set<K, Pred, A>::iterator it = m.begin();
 959      for (unsigned int i = 0; i < nSize; i++)
 960      {
 961          K key;
 962          Unserialize(is, key);
 963          it = m.insert(it, key);
 964      }
 965  }
 966  
 967  
 968  
 969  /**
 970   * unique_ptr
 971   */
 972  template<typename Stream, typename T> void
 973  Serialize(Stream& os, const std::unique_ptr<const T>& p)
 974  {
 975      assert(p != nullptr);
 976      Serialize(os, *p);
 977  }
 978  
 979  template<typename Stream, typename T>
 980  void Unserialize(Stream& is, std::unique_ptr<const T>& p)
 981  {
 982      p.reset(new T(deserialize, is));
 983  }
 984  
 985  
 986  
 987  /**
 988   * shared_ptr
 989   */
 990  template<typename Stream, typename T> void
 991  Serialize(Stream& os, const std::shared_ptr<const T>& p)
 992  {
 993      Serialize(os, *p);
 994  }
 995  
 996  template<typename Stream, typename T>
 997  void Unserialize(Stream& is, std::shared_ptr<const T>& p)
 998  {
 999      p = std::make_shared<const T>(deserialize, is);
1000  }
1001  
1002  /**
1003   * Support for (un)serializing many things at once
1004   */
1005  
1006  template <typename Stream, typename... Args>
1007  void SerializeMany(Stream& s, const Args&... args)
1008  {
1009      (::Serialize(s, args), ...);
1010  }
1011  
1012  template <typename Stream, typename... Args>
1013  inline void UnserializeMany(Stream& s, Args&&... args)
1014  {
1015      (::Unserialize(s, args), ...);
1016  }
1017  
1018  /**
1019   * Support for all macros providing or using the ser_action parameter of the SerializationOps method.
1020   */
1021  struct ActionSerialize {
1022      static constexpr bool ForRead() { return false; }
1023  
1024      template<typename Stream, typename... Args>
1025      static void SerReadWriteMany(Stream& s, const Args&... args)
1026      {
1027          ::SerializeMany(s, args...);
1028      }
1029  
1030      template<typename Stream, typename Type, typename Fn>
1031      static void SerRead(Stream& s, Type&&, Fn&&)
1032      {
1033      }
1034  
1035      template<typename Stream, typename Type, typename Fn>
1036      static void SerWrite(Stream& s, Type&& obj, Fn&& fn)
1037      {
1038          fn(s, std::forward<Type>(obj));
1039      }
1040  };
1041  struct ActionUnserialize {
1042      static constexpr bool ForRead() { return true; }
1043  
1044      template<typename Stream, typename... Args>
1045      static void SerReadWriteMany(Stream& s, Args&&... args)
1046      {
1047          ::UnserializeMany(s, args...);
1048      }
1049  
1050      template<typename Stream, typename Type, typename Fn>
1051      static void SerRead(Stream& s, Type&& obj, Fn&& fn)
1052      {
1053          fn(s, std::forward<Type>(obj));
1054      }
1055  
1056      template<typename Stream, typename Type, typename Fn>
1057      static void SerWrite(Stream& s, Type&&, Fn&&)
1058      {
1059      }
1060  };
1061  
1062  /* ::GetSerializeSize implementations
1063   *
1064   * Computing the serialized size of objects is done through a special stream
1065   * object of type SizeComputer, which only records the number of bytes written
1066   * to it.
1067   *
1068   * If your Serialize or SerializationOp method has non-trivial overhead for
1069   * serialization, it may be worthwhile to implement a specialized version for
1070   * SizeComputer, which uses the s.seek() method to record bytes that would
1071   * be written instead.
1072   */
1073  class SizeComputer
1074  {
1075  protected:
1076      size_t nSize{0};
1077  
1078  public:
1079      SizeComputer() = default;
1080  
1081      void write(Span<const std::byte> src)
1082      {
1083          this->nSize += src.size();
1084      }
1085  
1086      /** Pretend _nSize bytes are written, without specifying them. */
1087      void seek(size_t _nSize)
1088      {
1089          this->nSize += _nSize;
1090      }
1091  
1092      template<typename T>
1093      SizeComputer& operator<<(const T& obj)
1094      {
1095          ::Serialize(*this, obj);
1096          return (*this);
1097      }
1098  
1099      size_t size() const {
1100          return nSize;
1101      }
1102  };
1103  
1104  template<typename I>
1105  inline void WriteVarInt(SizeComputer &s, I n)
1106  {
1107      s.seek(GetSizeOfVarInt<I>(n));
1108  }
1109  
1110  inline void WriteCompactSize(SizeComputer &s, uint64_t nSize)
1111  {
1112      s.seek(GetSizeOfCompactSize(nSize));
1113  }
1114  
1115  template <typename T>
1116  size_t GetSerializeSize(const T& t)
1117  {
1118      return (SizeComputer() << t).size();
1119  }
1120  
1121  //! Check if type contains a stream by seeing if has a GetStream() method.
1122  template<typename T>
1123  concept ContainsStream = requires(T t) { t.GetStream(); };
1124  
1125  /** Wrapper that overrides the GetParams() function of a stream. */
1126  template <typename SubStream, typename Params>
1127  class ParamsStream
1128  {
1129      const Params& m_params;
1130      // If ParamsStream constructor is passed an lvalue argument, Substream will
1131      // be a reference type, and m_substream will reference that argument.
1132      // Otherwise m_substream will be a substream instance and move from the
1133      // argument. Letting ParamsStream contain a substream instance instead of
1134      // just a reference is useful to make the ParamsStream object self contained
1135      // and let it do cleanup when destroyed, for example by closing files if
1136      // SubStream is a file stream.
1137      SubStream m_substream;
1138  
1139  public:
1140      ParamsStream(SubStream&& substream, const Params& params LIFETIMEBOUND) : m_params{params}, m_substream{std::forward<SubStream>(substream)} {}
1141  
1142      template <typename NestedSubstream, typename Params1, typename Params2, typename... NestedParams>
1143      ParamsStream(NestedSubstream&& s, const Params1& params1 LIFETIMEBOUND, const Params2& params2 LIFETIMEBOUND, const NestedParams&... params LIFETIMEBOUND)
1144          : ParamsStream{::ParamsStream{std::forward<NestedSubstream>(s), params2, params...}, params1} {}
1145  
1146      template <typename U> ParamsStream& operator<<(const U& obj) { ::Serialize(*this, obj); return *this; }
1147      template <typename U> ParamsStream& operator>>(U&& obj) { ::Unserialize(*this, obj); return *this; }
1148      void write(Span<const std::byte> src) { GetStream().write(src); }
1149      void read(Span<std::byte> dst) { GetStream().read(dst); }
1150      void ignore(size_t num) { GetStream().ignore(num); }
1151      bool eof() const { return GetStream().eof(); }
1152      size_t size() const { return GetStream().size(); }
1153  
1154      //! Get reference to stream parameters.
1155      template <typename P>
1156      const auto& GetParams() const
1157      {
1158          if constexpr (std::is_convertible_v<Params, P>) {
1159              return m_params;
1160          } else {
1161              return m_substream.template GetParams<P>();
1162          }
1163      }
1164  
1165      //! Get reference to underlying stream.
1166      auto& GetStream()
1167      {
1168          if constexpr (ContainsStream<SubStream>) {
1169              return m_substream.GetStream();
1170          } else {
1171              return m_substream;
1172          }
1173      }
1174      const auto& GetStream() const
1175      {
1176          if constexpr (ContainsStream<SubStream>) {
1177              return m_substream.GetStream();
1178          } else {
1179              return m_substream;
1180          }
1181      }
1182  };
1183  
1184  /**
1185   * Explicit template deduction guide is required for single-parameter
1186   * constructor so Substream&& is treated as a forwarding reference, and
1187   * SubStream is deduced as reference type for lvalue arguments.
1188   */
1189  template <typename Substream, typename Params>
1190  ParamsStream(Substream&&, const Params&) -> ParamsStream<Substream, Params>;
1191  
1192  /**
1193   * Template deduction guide for multiple params arguments that creates a nested
1194   * ParamsStream.
1195   */
1196  template <typename Substream, typename Params1, typename Params2, typename... Params>
1197  ParamsStream(Substream&& s, const Params1& params1, const Params2& params2, const Params&... params) ->
1198      ParamsStream<decltype(ParamsStream{std::forward<Substream>(s), params2, params...}), Params1>;
1199  
1200  /** Wrapper that serializes objects with the specified parameters. */
1201  template <typename Params, typename T>
1202  class ParamsWrapper
1203  {
1204      const Params& m_params;
1205      T& m_object;
1206  
1207  public:
1208      explicit ParamsWrapper(const Params& params, T& obj) : m_params{params}, m_object{obj} {}
1209  
1210      template <typename Stream>
1211      void Serialize(Stream& s) const
1212      {
1213          ParamsStream ss{s, m_params};
1214          ::Serialize(ss, m_object);
1215      }
1216      template <typename Stream>
1217      void Unserialize(Stream& s)
1218      {
1219          ParamsStream ss{s, m_params};
1220          ::Unserialize(ss, m_object);
1221      }
1222  };
1223  
1224  /**
1225   * Helper macro for SerParams structs
1226   *
1227   * Allows you define SerParams instances and then apply them directly
1228   * to an object via function call syntax, eg:
1229   *
1230   *   constexpr SerParams FOO{....};
1231   *   ss << FOO(obj);
1232   */
1233  #define SER_PARAMS_OPFUNC                                                                \
1234      /**                                                                                  \
1235       * Return a wrapper around t that (de)serializes it with specified parameter params. \
1236       *                                                                                   \
1237       * See SER_PARAMS for more information on serialization parameters.                  \
1238       */                                                                                  \
1239      template <typename T>                                                                \
1240      auto operator()(T&& t) const                                                         \
1241      {                                                                                    \
1242          return ParamsWrapper{*this, t};                                                  \
1243      }
1244  
1245  #endif // LIMENKA_SERIALIZE_H
1246