@@ -596,6 +596,53 @@ class LimitedString
596
596
template <typename I>
597
597
BigEndian<I> WrapBigEndian (I& n) { return BigEndian<I>(n); }
598
598
599
+ /* * Formatter to serialize/deserialize vector elements using another formatter
600
+ *
601
+ * Example:
602
+ * struct X {
603
+ * std::vector<uint64_t> v;
604
+ * SERIALIZE_METHODS(X, obj) { READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); }
605
+ * };
606
+ * will define a struct that contains a vector of uint64_t, which is serialized
607
+ * as a vector of VarInt-encoded integers.
608
+ *
609
+ * V is not required to be an std::vector type. It works for any class that
610
+ * exposes a value_type, size, reserve, push_back, and const iterators.
611
+ */
612
+ template <class Formatter >
613
+ struct VectorFormatter
614
+ {
615
+ template <typename Stream, typename V>
616
+ void Ser (Stream& s, const V& v)
617
+ {
618
+ WriteCompactSize (s, v.size ());
619
+ for (const typename V::value_type& elem : v) {
620
+ s << Using<Formatter>(elem);
621
+ }
622
+ }
623
+
624
+ template <typename Stream, typename V>
625
+ void Unser (Stream& s, V& v)
626
+ {
627
+ v.clear ();
628
+ size_t size = ReadCompactSize (s);
629
+ size_t allocated = 0 ;
630
+ while (allocated < size) {
631
+ // For DoS prevention, do not blindly allocate as much as the stream claims to contain.
632
+ // Instead, allocate in 5MiB batches, so that an attacker actually needs to provide
633
+ // X MiB of data to make us allocate X+5 Mib.
634
+ static_assert (sizeof (typename V::value_type) <= MAX_VECTOR_ALLOCATE, " Vector element size too large" );
635
+ allocated = std::min (size, allocated + MAX_VECTOR_ALLOCATE / sizeof (typename V::value_type));
636
+ v.reserve (allocated);
637
+ while (v.size () < allocated) {
638
+ typename V::value_type val;
639
+ s >> Using<Formatter>(val);
640
+ v.push_back (std::move (val));
641
+ }
642
+ }
643
+ };
644
+ };
645
+
599
646
/* *
600
647
* Forward declarations
601
648
*/
0 commit comments