From 2992ca9f087f46370bc63b8ab0d5a8d64df56232 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 20:08:10 +0000 Subject: [PATCH] Stop dump() from heap-allocating its output adapter per call The serializer held its output sink as output_adapter_t (a std::shared_ptr>), which dump() and operator<< built via make_shared -- one heap allocation per call for a sink that only wraps a reference to the caller's string or stream. Hold the sink as a non-owning output_adapter_protocol* instead and construct the concrete adapter on the stack at the call site. The write path (o->write_characters) is unchanged, so output is byte-for-byte identical; a compact dump() of a small object drops from 2 heap allocations to 1 (only the returned string remains), ~3% faster. Completes the per-call allocation cleanup on this branch, which already removed the indent_string buffer (both were reported in #5413). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01L1oJ2ggRHS37zeVe94QTA1 Signed-off-by: Claude --- include/nlohmann/detail/output/serializer.hpp | 11 ++++++----- include/nlohmann/json.hpp | 6 ++++-- single_include/nlohmann/json.hpp | 17 ++++++++++------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 20a65d76e..4b9b63556 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -63,13 +63,14 @@ class serializer public: /*! - @param[in] s output stream to serialize to + @param[in] s output adapter to serialize to; not owned by the serializer, + so it must outlive it (it lives at the call site) @param[in] ichar indentation character to use @param[in] error_handler_ how to react on decoding errors */ - serializer(output_adapter_t s, const char ichar, + serializer(output_adapter_protocol* s, const char ichar, error_handler_t error_handler_ = error_handler_t::strict) - : o(std::move(s)) + : o(s) , loc(std::localeconv()) , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) @@ -1677,8 +1678,8 @@ class serializer } private: - /// the output of the serializer - output_adapter_t o = nullptr; + /// the output of the serializer (non-owning; the adapter lives at the call site) + output_adapter_protocol* o = nullptr; /// a (hopefully) large enough character buffer std::array number_buffer{{}}; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 1dcc0a13a..8a571b835 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1341,7 +1341,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const error_handler_t error_handler = error_handler_t::strict) const { string_t result; - serializer s(detail::output_adapter(result), indent_char, error_handler); + detail::output_string_adapter string_adapter(result); + serializer s(&string_adapter, indent_char, error_handler); if (indent >= 0) { @@ -4055,7 +4056,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec o.width(0); // do the actual serialization - serializer s(detail::output_adapter(o), o.fill()); + detail::output_stream_adapter stream_adapter(o); + serializer s(&stream_adapter, o.fill()); s.dump(j, pretty_print, false, static_cast(indentation)); return o; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 464e83609..51c4f2287 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -21043,13 +21043,14 @@ class serializer public: /*! - @param[in] s output stream to serialize to + @param[in] s output adapter to serialize to; not owned by the serializer, + so it must outlive it (it lives at the call site) @param[in] ichar indentation character to use @param[in] error_handler_ how to react on decoding errors */ - serializer(output_adapter_t s, const char ichar, + serializer(output_adapter_protocol* s, const char ichar, error_handler_t error_handler_ = error_handler_t::strict) - : o(std::move(s)) + : o(s) , loc(std::localeconv()) , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) @@ -22657,8 +22658,8 @@ class serializer } private: - /// the output of the serializer - output_adapter_t o = nullptr; + /// the output of the serializer (non-owning; the adapter lives at the call site) + output_adapter_protocol* o = nullptr; /// a (hopefully) large enough character buffer std::array number_buffer{{}}; @@ -24360,7 +24361,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const error_handler_t error_handler = error_handler_t::strict) const { string_t result; - serializer s(detail::output_adapter(result), indent_char, error_handler); + detail::output_string_adapter string_adapter(result); + serializer s(&string_adapter, indent_char, error_handler); if (indent >= 0) { @@ -27074,7 +27076,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec o.width(0); // do the actual serialization - serializer s(detail::output_adapter(o), o.fill()); + detail::output_stream_adapter stream_adapter(o); + serializer s(&stream_adapter, o.fill()); s.dump(j, pretty_print, false, static_cast(indentation)); return o; }