mirror of
https://github.com/nlohmann/json.git
synced 2026-09-24 06:10:22 +01:00
Stop dump() from heap-allocating its output adapter per call (#5449)
* Stop dump() from heap-allocating its output adapter per call The serializer held its output sink as output_adapter_t<char> (a std::shared_ptr<output_adapter_protocol<char>>), 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<char>* 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L1oJ2ggRHS37zeVe94QTA1 Signed-off-by: Claude <noreply@anthropic.com> * Take the output adapter by reference at the serializer ctor Per review: the serializer still holds the adapter as a non-owning pointer, but the constructor now takes output_adapter_protocol<char>& and takes its address internally, so every call site passes a reference. A reference cannot be null and reads as a borrow, which makes the lifetime contract harder to get wrong than handing over a raw pointer. The stored member and the write path are unchanged. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAYM1qhSA2FDaDcGfPW3fG Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Claude <noreply@anthropic.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -62,7 +62,8 @@ class serializer
|
|||||||
|
|
||||||
public:
|
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] ichar indentation character to use
|
||||||
@param[in] pretty_print_ whether the output shall be pretty-printed
|
@param[in] pretty_print_ whether the output shall be pretty-printed
|
||||||
@param[in] ensure_ascii_ If @a ensure_ascii_ is true, all non-ASCII
|
@param[in] ensure_ascii_ If @a ensure_ascii_ is true, all non-ASCII
|
||||||
@@ -76,12 +77,12 @@ class serializer
|
|||||||
being threaded through every call to @ref dump, @ref dump_internal and
|
being threaded through every call to @ref dump, @ref dump_internal and
|
||||||
@ref dump_iteratively.
|
@ref dump_iteratively.
|
||||||
*/
|
*/
|
||||||
serializer(output_adapter_t<char> s, const char ichar,
|
serializer(output_adapter_protocol<char>& s, const char ichar,
|
||||||
const bool pretty_print_ = false,
|
const bool pretty_print_ = false,
|
||||||
const bool ensure_ascii_ = false,
|
const bool ensure_ascii_ = false,
|
||||||
const std::size_t indent_step_ = 0,
|
const std::size_t indent_step_ = 0,
|
||||||
error_handler_t error_handler_ = error_handler_t::strict)
|
error_handler_t error_handler_ = error_handler_t::strict)
|
||||||
: o(std::move(s))
|
: o(&s)
|
||||||
, locale(std::localeconv())
|
, locale(std::localeconv())
|
||||||
, indent_char(ichar)
|
, indent_char(ichar)
|
||||||
, pretty_print(pretty_print_)
|
, pretty_print(pretty_print_)
|
||||||
@@ -1678,8 +1679,8 @@ class serializer
|
|||||||
const char decimal_point;
|
const char decimal_point;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// the output of the serializer
|
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
||||||
output_adapter_t<char> o = nullptr;
|
output_adapter_protocol<char>* o = nullptr;
|
||||||
|
|
||||||
/// a (hopefully) large enough character buffer
|
/// a (hopefully) large enough character buffer
|
||||||
std::array<char, 64> number_buffer{{}};
|
std::array<char, 64> number_buffer{{}};
|
||||||
|
|||||||
@@ -1343,16 +1343,17 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
const error_handler_t error_handler = error_handler_t::strict) const
|
const error_handler_t error_handler = error_handler_t::strict) const
|
||||||
{
|
{
|
||||||
string_t result;
|
string_t result;
|
||||||
|
detail::output_string_adapter<char, string_t> string_adapter(result);
|
||||||
|
|
||||||
if (indent >= 0)
|
if (indent >= 0)
|
||||||
{
|
{
|
||||||
serializer s(detail::output_adapter<char, string_t>(result), indent_char,
|
serializer s(string_adapter, indent_char,
|
||||||
true, ensure_ascii, static_cast<std::size_t>(indent), error_handler);
|
true, ensure_ascii, static_cast<std::size_t>(indent), error_handler);
|
||||||
s.dump(*this);
|
s.dump(*this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serializer s(detail::output_adapter<char, string_t>(result), indent_char,
|
serializer s(string_adapter, indent_char,
|
||||||
false, ensure_ascii, 0, error_handler);
|
false, ensure_ascii, 0, error_handler);
|
||||||
s.dump(*this);
|
s.dump(*this);
|
||||||
}
|
}
|
||||||
@@ -4083,7 +4084,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
o.width(0);
|
o.width(0);
|
||||||
|
|
||||||
// do the actual serialization
|
// do the actual serialization
|
||||||
serializer s(detail::output_adapter<char>(o), o.fill(),
|
detail::output_stream_adapter<char> stream_adapter(o);
|
||||||
|
serializer s(stream_adapter, o.fill(),
|
||||||
pretty_print, false, static_cast<std::size_t>(indentation));
|
pretty_print, false, static_cast<std::size_t>(indentation));
|
||||||
s.dump(j);
|
s.dump(j);
|
||||||
return o;
|
return o;
|
||||||
|
|||||||
@@ -21410,7 +21410,8 @@ class serializer
|
|||||||
|
|
||||||
public:
|
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] ichar indentation character to use
|
||||||
@param[in] pretty_print_ whether the output shall be pretty-printed
|
@param[in] pretty_print_ whether the output shall be pretty-printed
|
||||||
@param[in] ensure_ascii_ If @a ensure_ascii_ is true, all non-ASCII
|
@param[in] ensure_ascii_ If @a ensure_ascii_ is true, all non-ASCII
|
||||||
@@ -21424,12 +21425,12 @@ class serializer
|
|||||||
being threaded through every call to @ref dump, @ref dump_internal and
|
being threaded through every call to @ref dump, @ref dump_internal and
|
||||||
@ref dump_iteratively.
|
@ref dump_iteratively.
|
||||||
*/
|
*/
|
||||||
serializer(output_adapter_t<char> s, const char ichar,
|
serializer(output_adapter_protocol<char>& s, const char ichar,
|
||||||
const bool pretty_print_ = false,
|
const bool pretty_print_ = false,
|
||||||
const bool ensure_ascii_ = false,
|
const bool ensure_ascii_ = false,
|
||||||
const std::size_t indent_step_ = 0,
|
const std::size_t indent_step_ = 0,
|
||||||
error_handler_t error_handler_ = error_handler_t::strict)
|
error_handler_t error_handler_ = error_handler_t::strict)
|
||||||
: o(std::move(s))
|
: o(&s)
|
||||||
, locale(std::localeconv())
|
, locale(std::localeconv())
|
||||||
, indent_char(ichar)
|
, indent_char(ichar)
|
||||||
, pretty_print(pretty_print_)
|
, pretty_print(pretty_print_)
|
||||||
@@ -23026,8 +23027,8 @@ class serializer
|
|||||||
const char decimal_point;
|
const char decimal_point;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// the output of the serializer
|
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
||||||
output_adapter_t<char> o = nullptr;
|
output_adapter_protocol<char>* o = nullptr;
|
||||||
|
|
||||||
/// a (hopefully) large enough character buffer
|
/// a (hopefully) large enough character buffer
|
||||||
std::array<char, 64> number_buffer{{}};
|
std::array<char, 64> number_buffer{{}};
|
||||||
@@ -24738,16 +24739,17 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
const error_handler_t error_handler = error_handler_t::strict) const
|
const error_handler_t error_handler = error_handler_t::strict) const
|
||||||
{
|
{
|
||||||
string_t result;
|
string_t result;
|
||||||
|
detail::output_string_adapter<char, string_t> string_adapter(result);
|
||||||
|
|
||||||
if (indent >= 0)
|
if (indent >= 0)
|
||||||
{
|
{
|
||||||
serializer s(detail::output_adapter<char, string_t>(result), indent_char,
|
serializer s(string_adapter, indent_char,
|
||||||
true, ensure_ascii, static_cast<std::size_t>(indent), error_handler);
|
true, ensure_ascii, static_cast<std::size_t>(indent), error_handler);
|
||||||
s.dump(*this);
|
s.dump(*this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serializer s(detail::output_adapter<char, string_t>(result), indent_char,
|
serializer s(string_adapter, indent_char,
|
||||||
false, ensure_ascii, 0, error_handler);
|
false, ensure_ascii, 0, error_handler);
|
||||||
s.dump(*this);
|
s.dump(*this);
|
||||||
}
|
}
|
||||||
@@ -27478,7 +27480,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
o.width(0);
|
o.width(0);
|
||||||
|
|
||||||
// do the actual serialization
|
// do the actual serialization
|
||||||
serializer s(detail::output_adapter<char>(o), o.fill(),
|
detail::output_stream_adapter<char> stream_adapter(o);
|
||||||
|
serializer s(stream_adapter, o.fill(),
|
||||||
pretty_print, false, static_cast<std::size_t>(indentation));
|
pretty_print, false, static_cast<std::size_t>(indentation));
|
||||||
s.dump(j);
|
s.dump(j);
|
||||||
return o;
|
return o;
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ void check_escaped(const char* original, const char* escaped = "", bool ensure_a
|
|||||||
void check_escaped(const char* original, const char* escaped, const bool ensure_ascii)
|
void check_escaped(const char* original, const char* escaped, const bool ensure_ascii)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
json::serializer s(nlohmann::detail::output_adapter<char>(ss), ' ', false, ensure_ascii);
|
nlohmann::detail::output_stream_adapter<char> adapter(ss);
|
||||||
|
json::serializer s(adapter, ' ', false, ensure_ascii);
|
||||||
s.dump_escaped(original);
|
s.dump_escaped(original);
|
||||||
s.flush(); // dump_escaped writes into the serializer's internal buffer
|
s.flush(); // dump_escaped writes into the serializer's internal buffer
|
||||||
CHECK(ss.str() == escaped);
|
CHECK(ss.str() == escaped);
|
||||||
|
|||||||
Reference in New Issue
Block a user