Skip to content

Commit abf8624

Browse files
committed
Add custom vector-element formatter
This allows a very compact notation for serialization of vectors whose elements are not serialized using their default encoding.
1 parent 37d800b commit abf8624

File tree

1 file changed

+47
-0
lines changed

1 file changed

+47
-0
lines changed

src/serialize.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,53 @@ class LimitedString
596596
template<typename I>
597597
BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); }
598598

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+
599646
/**
600647
* Forward declarations
601648
*/

0 commit comments

Comments
 (0)