|
25 | 25 |
|
26 | 26 | static const unsigned int MAX_SIZE = 0x02000000;
|
27 | 27 |
|
| 28 | +/** Maximum amount of memory (in bytes) to allocate at once when deserializing vectors. */ |
| 29 | +static const unsigned int MAX_VECTOR_ALLOCATE = 5000000; |
| 30 | + |
28 | 31 | /**
|
29 | 32 | * Dummy data type to identify deserializing constructors.
|
30 | 33 | *
|
@@ -593,6 +596,53 @@ class LimitedString
|
593 | 596 | template<typename I>
|
594 | 597 | BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); }
|
595 | 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 | + |
596 | 646 | /**
|
597 | 647 | * Forward declarations
|
598 | 648 | */
|
@@ -673,6 +723,20 @@ inline void Unserialize(Stream& is, T&& a)
|
673 | 723 | a.Unserialize(is);
|
674 | 724 | }
|
675 | 725 |
|
| 726 | +/** Default formatter. Serializes objects as themselves. |
| 727 | + * |
| 728 | + * The vector/prevector serialization code passes this to VectorFormatter |
| 729 | + * to enable reusing that logic. It shouldn't be needed elsewhere. |
| 730 | + */ |
| 731 | +struct DefaultFormatter |
| 732 | +{ |
| 733 | + template<typename Stream, typename T> |
| 734 | + static void Ser(Stream& s, const T& t) { Serialize(s, t); } |
| 735 | + |
| 736 | + template<typename Stream, typename T> |
| 737 | + static void Unser(Stream& s, T& t) { Unserialize(s, t); } |
| 738 | +}; |
| 739 | + |
676 | 740 |
|
677 | 741 |
|
678 | 742 |
|
@@ -713,9 +777,7 @@ void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&)
|
713 | 777 | template<typename Stream, unsigned int N, typename T, typename V>
|
714 | 778 | void Serialize_impl(Stream& os, const prevector<N, T>& v, const V&)
|
715 | 779 | {
|
716 |
| - WriteCompactSize(os, v.size()); |
717 |
| - for (typename prevector<N, T>::const_iterator vi = v.begin(); vi != v.end(); ++vi) |
718 |
| - ::Serialize(os, (*vi)); |
| 780 | + Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v)); |
719 | 781 | }
|
720 | 782 |
|
721 | 783 | template<typename Stream, unsigned int N, typename T>
|
@@ -744,19 +806,7 @@ void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&)
|
744 | 806 | template<typename Stream, unsigned int N, typename T, typename V>
|
745 | 807 | void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&)
|
746 | 808 | {
|
747 |
| - v.clear(); |
748 |
| - unsigned int nSize = ReadCompactSize(is); |
749 |
| - unsigned int i = 0; |
750 |
| - unsigned int nMid = 0; |
751 |
| - while (nMid < nSize) |
752 |
| - { |
753 |
| - nMid += 5000000 / sizeof(T); |
754 |
| - if (nMid > nSize) |
755 |
| - nMid = nSize; |
756 |
| - v.resize_uninitialized(nMid); |
757 |
| - for (; i < nMid; ++i) |
758 |
| - Unserialize(is, v[i]); |
759 |
| - } |
| 809 | + Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v)); |
760 | 810 | }
|
761 | 811 |
|
762 | 812 | template<typename Stream, unsigned int N, typename T>
|
@@ -793,9 +843,7 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&)
|
793 | 843 | template<typename Stream, typename T, typename A, typename V>
|
794 | 844 | void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&)
|
795 | 845 | {
|
796 |
| - WriteCompactSize(os, v.size()); |
797 |
| - for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi) |
798 |
| - ::Serialize(os, (*vi)); |
| 846 | + Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v)); |
799 | 847 | }
|
800 | 848 |
|
801 | 849 | template<typename Stream, typename T, typename A>
|
@@ -824,19 +872,7 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&)
|
824 | 872 | template<typename Stream, typename T, typename A, typename V>
|
825 | 873 | void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&)
|
826 | 874 | {
|
827 |
| - v.clear(); |
828 |
| - unsigned int nSize = ReadCompactSize(is); |
829 |
| - unsigned int i = 0; |
830 |
| - unsigned int nMid = 0; |
831 |
| - while (nMid < nSize) |
832 |
| - { |
833 |
| - nMid += 5000000 / sizeof(T); |
834 |
| - if (nMid > nSize) |
835 |
| - nMid = nSize; |
836 |
| - v.resize(nMid); |
837 |
| - for (; i < nMid; i++) |
838 |
| - Unserialize(is, v[i]); |
839 |
| - } |
| 875 | + Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v)); |
840 | 876 | }
|
841 | 877 |
|
842 | 878 | template<typename Stream, typename T, typename A>
|
|
0 commit comments