From fae364db248a797a24b74c71028e583c62b58ec1 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 11 Sep 2026 08:22:51 +0200 Subject: [PATCH] Move the scanned string into the value instead of copying it (#5458) * Move the scanned string into the value instead of copying it The SAX interface documents that the string handed to json_sax::string() may be moved from, and the DOM handlers already move the one handed to binary(). string() did not, so every string value was copy-constructed out of the lexer's token buffer, which then kept the buffer alive at its high-water mark until the next token overwrote it. Moving hands that buffer to the new value instead. The allocation count is unchanged - the value needed one either way - but the copy is gone. jeopardy 247.3 ms -> 240.9 ms (-2.6%) citm_catalog 4.61 ms -> 4.48 ms (-2.8%) 40k 30-char strings 7.80 ms -> 7.64 ms (-2.1%) Note this deliberately does not extend to the object key. Moving the key hands the lexer's buffer - sized for the largest token seen so far - to a key that is usually short, so the next value has to grow a fresh buffer. Measured, that costs 11.9% on a document of many small keys with longer values. Signed-off-by: Niels Lohmann * Spell out the move rationale at every handle_value(std::move) site The comment explaining why the value is moved sat only on json_sax_dom_parser::string(), and the callback parser's string() pointed at it with "see json_sax_dom_parser::string()". That reference cannot be searched for - the function is declared as `bool string(string_t& val)` inside the class, so the qualified name appears nowhere - and the two binary() overloads, which have always moved, carried no explanation at all. Put the same comment on all four sites and name json_sax, which is greppable, instead of a member that is not. Comment-only; no generated code changes. Signed-off-by: Niels Lohmann --------- Signed-off-by: Niels Lohmann --- include/nlohmann/detail/input/json_sax.hpp | 12 ++++++++++-- single_include/nlohmann/json.hpp | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 684fac047..1627d2326 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -222,12 +222,16 @@ class json_sax_dom_parser bool string(string_t& val) { - handle_value(val); + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it + handle_value(std::move(val)); return true; } bool binary(binary_t& val) { + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it handle_value(std::move(val)); return true; } @@ -532,12 +536,16 @@ class json_sax_dom_callback_parser bool string(string_t& val) { - handle_value(val); + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it + handle_value(std::move(val)); return true; } bool binary(binary_t& val) { + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it handle_value(std::move(val)); return true; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 890a7f721..e2c314c5f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -10935,12 +10935,16 @@ class json_sax_dom_parser bool string(string_t& val) { - handle_value(val); + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it + handle_value(std::move(val)); return true; } bool binary(binary_t& val) { + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it handle_value(std::move(val)); return true; } @@ -11245,12 +11249,16 @@ class json_sax_dom_callback_parser bool string(string_t& val) { - handle_value(val); + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it + handle_value(std::move(val)); return true; } bool binary(binary_t& val) { + // json_sax documents that the passed value may be moved from, + // so hand the buffer over instead of copying it handle_value(std::move(val)); return true; }