Skip to content

Commit

Permalink
Add custom vector-element formatter
Browse files Browse the repository at this point in the history
This allows a very compact notation for serialization of vectors whose
elements are not serialized using their default encoding.
  • Loading branch information
sipa authored and furszy committed Jul 3, 2021
1 parent df4e1ba commit 1dfddce
Showing 1 changed file with 47 additions and 0 deletions.
47 changes: 47 additions & 0 deletions src/serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,53 @@ class LimitedString
template<typename I>
BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); }

/** Formatter to serialize/deserialize vector elements using another formatter
*
* Example:
* struct X {
* std::vector<uint64_t> v;
* SERIALIZE_METHODS(X, obj) { READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); }
* };
* will define a struct that contains a vector of uint64_t, which is serialized
* as a vector of VarInt-encoded integers.
*
* V is not required to be an std::vector type. It works for any class that
* exposes a value_type, size, reserve, push_back, and const iterators.
*/
template<class Formatter>
struct VectorFormatter
{
template<typename Stream, typename V>
void Ser(Stream& s, const V& v)
{
WriteCompactSize(s, v.size());
for (const typename V::value_type& elem : v) {
s << Using<Formatter>(elem);
}
}

template<typename Stream, typename V>
void Unser(Stream& s, V& v)
{
v.clear();
size_t size = ReadCompactSize(s);
size_t allocated = 0;
while (allocated < size) {
// For DoS prevention, do not blindly allocate as much as the stream claims to contain.
// Instead, allocate in 5MiB batches, so that an attacker actually needs to provide
// X MiB of data to make us allocate X+5 Mib.
static_assert(sizeof(typename V::value_type) <= MAX_VECTOR_ALLOCATE, "Vector element size too large");
allocated = std::min(size, allocated + MAX_VECTOR_ALLOCATE / sizeof(typename V::value_type));
v.reserve(allocated);
while (v.size() < allocated) {
typename V::value_type val;
s >> Using<Formatter>(val);
v.push_back(std::move(val));
}
}
};
};

/**
* Forward declarations
*/
Expand Down

0 comments on commit 1dfddce

Please sign in to comment.