diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 4e2747169..fe97dc2a4 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -39,6 +39,35 @@ enum class bjdata_version_t // binary writer // /////////////////// +/*! +@brief conservative capacity hint for binary serialization into a std::vector + +Returns an approximate number of bytes to reserve up front so that serializing +an array/object of many elements does not repeatedly reallocate the output +buffer. Only the top-level element count is consulted (O(1), no walk of the +DOM), and the result is clamped to a fixed ceiling: a large or untrusted DOM can +therefore never trigger an oversized allocation here, and the multiplication +cannot overflow. The buffer still grows geometrically beyond the hint, so a hint +that is too small only costs a few later reallocations. A single scalar, string, +or binary value is written in one shot and needs no hint. +*/ +template +std::size_t binary_reserve_hint(const BasicJsonType& j) +{ + constexpr std::size_t max_hint = static_cast(1) << 20; // 1 MiB + if (j.is_array() || j.is_object()) + { + const std::size_t elements = j.size(); + // guard the multiplication against overflow and cap the reservation + if (elements > max_hint / 4) + { + return max_hint; + } + return (elements * 4) + 2; + } + return 0; +} + /*! @brief serialization to CBOR and MessagePack values */ diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 551bcdee8..1eeac5085 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -4327,6 +4327,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static std::vector to_cbor(const basic_json& j) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_cbor(j); return result; @@ -4351,6 +4352,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static std::vector to_msgpack(const basic_json& j) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_msgpack(j); return result; @@ -4377,6 +4379,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool use_type = false) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_ubjson(j, use_size, use_type); return result; @@ -4406,6 +4409,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bjdata_version_t version = bjdata_version_t::draft2) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_ubjson(j, use_size, use_type, true, true, version); return result; @@ -4434,6 +4438,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static std::vector to_bson(const basic_json& j) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_bson(j); return result; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cfffde13d..6cfdbfc57 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -16853,6 +16853,35 @@ enum class bjdata_version_t // binary writer // /////////////////// +/*! +@brief conservative capacity hint for binary serialization into a std::vector + +Returns an approximate number of bytes to reserve up front so that serializing +an array/object of many elements does not repeatedly reallocate the output +buffer. Only the top-level element count is consulted (O(1), no walk of the +DOM), and the result is clamped to a fixed ceiling: a large or untrusted DOM can +therefore never trigger an oversized allocation here, and the multiplication +cannot overflow. The buffer still grows geometrically beyond the hint, so a hint +that is too small only costs a few later reallocations. A single scalar, string, +or binary value is written in one shot and needs no hint. +*/ +template +std::size_t binary_reserve_hint(const BasicJsonType& j) +{ + constexpr std::size_t max_hint = static_cast(1) << 20; // 1 MiB + if (j.is_array() || j.is_object()) + { + const std::size_t elements = j.size(); + // guard the multiplication against overflow and cap the reservation + if (elements > max_hint / 4) + { + return max_hint; + } + return (elements * 4) + 2; + } + return 0; +} + /*! @brief serialization to CBOR and MessagePack values */ @@ -25571,6 +25600,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static std::vector to_cbor(const basic_json& j) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_cbor(j); return result; @@ -25595,6 +25625,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static std::vector to_msgpack(const basic_json& j) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_msgpack(j); return result; @@ -25621,6 +25652,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool use_type = false) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_ubjson(j, use_size, use_type); return result; @@ -25650,6 +25682,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bjdata_version_t version = bjdata_version_t::draft2) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_ubjson(j, use_size, use_type, true, true, version); return result; @@ -25678,6 +25711,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static std::vector to_bson(const basic_json& j) { std::vector result; + result.reserve(detail::binary_reserve_hint(j)); detail::binary_writer>( detail::output_vector_sink(result)).write_bson(j); return result;