Changeset View
Changeset View
Standalone View
Standalone View
src/serialize.h
Show First 20 Lines • Show All 714 Lines • ▼ Show 20 Lines | |||||
* SERIALIZE_METHODS(X, obj) { | * SERIALIZE_METHODS(X, obj) { | ||||
* READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); | * READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); | ||||
* } | * } | ||||
* }; | * }; | ||||
* will define a struct that contains a vector of uint64_t, which is serialized | * will define a struct that contains a vector of uint64_t, which is serialized | ||||
* as a vector of VarInt-encoded integers. | * as a vector of VarInt-encoded integers. | ||||
* | * | ||||
* V is not required to be an std::vector type. It works for any class that | * 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. | * exposes a value_type, size, reserve, emplace_back, back, and const iterators. | ||||
*/ | */ | ||||
template <class Formatter> struct VectorFormatter { | template <class Formatter> struct VectorFormatter { | ||||
template <typename Stream, typename V> void Ser(Stream &s, const V &v) { | template <typename Stream, typename V> void Ser(Stream &s, const V &v) { | ||||
Formatter formatter; | |||||
WriteCompactSize(s, v.size()); | WriteCompactSize(s, v.size()); | ||||
for (const typename V::value_type &elem : v) { | for (const typename V::value_type &elem : v) { | ||||
s << Using<Formatter>(elem); | formatter.Ser(s, elem); | ||||
} | } | ||||
} | } | ||||
template <typename Stream, typename V> void Unser(Stream &s, V &v) { | template <typename Stream, typename V> void Unser(Stream &s, V &v) { | ||||
Formatter formatter; | |||||
v.clear(); | v.clear(); | ||||
size_t size = ReadCompactSize(s); | size_t size = ReadCompactSize(s); | ||||
size_t allocated = 0; | size_t allocated = 0; | ||||
while (allocated < size) { | while (allocated < size) { | ||||
// For DoS prevention, do not blindly allocate as much as the stream | // For DoS prevention, do not blindly allocate as much as the stream | ||||
// claims to contain. Instead, allocate in 5MiB batches, so that an | // claims to contain. Instead, allocate in 5MiB batches, so that an | ||||
// attacker actually needs to provide X MiB of data to make us | // attacker actually needs to provide X MiB of data to make us | ||||
// allocate X+5 Mib. | // allocate X+5 Mib. | ||||
static_assert(sizeof(typename V::value_type) <= MAX_VECTOR_ALLOCATE, | static_assert(sizeof(typename V::value_type) <= MAX_VECTOR_ALLOCATE, | ||||
"Vector element size too large"); | "Vector element size too large"); | ||||
allocated = | allocated = | ||||
std::min(size, allocated + MAX_VECTOR_ALLOCATE / | std::min(size, allocated + MAX_VECTOR_ALLOCATE / | ||||
sizeof(typename V::value_type)); | sizeof(typename V::value_type)); | ||||
v.reserve(allocated); | v.reserve(allocated); | ||||
while (v.size() < allocated) { | while (v.size() < allocated) { | ||||
typename V::value_type val; | v.emplace_back(); | ||||
s >> Using<Formatter>(val); | formatter.Unser(s, v.back()); | ||||
v.push_back(std::move(val)); | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
}; | }; | ||||
/** | /** | ||||
* Forward declarations | * Forward declarations | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 420 Lines • Show Last 20 Lines |