Return the parsed value by move from from_cbor() and friends - #5501
Open
nlohmann wants to merge 1 commit into
Open
Return the parsed value by move from from_cbor() and friends#5501nlohmann wants to merge 1 commit into
nlohmann wants to merge 1 commit into
Conversation
nlohmann
marked this pull request as ready for review
September 6, 2026 17:41
This was referenced Sep 6, 2026
gregmarr
reviewed
Sep 7, 2026
The binary entry points end with
return res ? result : basic_json(value_t::discarded);
The condition operator's second operand is an lvalue, so this is not a case
where the return value can be elided or implicitly moved from: every
successful from_cbor(), from_msgpack(), from_ubjson(), from_bjdata() and
from_bson() call deep-copies the value it just parsed, and then destroys the
original.
The copy is not cheap, and it is not incidental: basic_json's copy
constructor walks the whole value. Parsing a 2 MB CBOR document with 60,000
objects, median of 25 runs, clang 17 -O3:
from_cbor 26.99 ms -> 14.65 ms
from_msgpack 26.82 ms -> 14.82 ms
Moving instead of copying is the entire change; the parsed value is not used
again after the return expression is evaluated.
There is a second reason to prefer the move. The copy constructor recurses
once per nesting level, so the copy is also a stack-overflow path on the
return side, on a value the reader has already accepted. That is currently
masked because the readers themselves recurse and overflow first (#5104), but
it has to be fixed for making them iterative to have any effect.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann
force-pushed
the
binary-readers-move-result
branch
from
September 7, 2026 05:58
c3219fd to
755c547
Compare
5 tasks
gregmarr
reviewed
Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the series that makes the binary readers non-recursive (#5104); this one stands on its own.
What
The binary entry points end with
The conditional operator's second operand is an lvalue, so this is not a case where the return value can be elided or implicitly moved from. Every successful
from_cbor,from_msgpack,from_ubjson,from_bjdataandfrom_bsoncall deep-copies the value it just parsed, and then destroys the original.Why it matters
basic_json's copy constructor walks the whole value, so the cost is proportional to the document. Parsing a 2 MB CBOR document with 60,000 objects, median of 25 runs, clang 17-O3:from_cborfrom_msgpackThere is a second reason. The copy constructor recurses once per nesting level, so it is also a stack-overflow path on the return side, on a value the reader has already accepted. That is currently masked because the readers themselves recurse and overflow first, but it has to be fixed for making them iterative to have any effect — see #5104 and the rest of this stack.
API impact
No breaking changes. No signature, no return type and no observable behaviour changes; the parsed value is simply not copied on its way out. It is not used again after the return expression is evaluated.
Checklist
make amalgamate.🤖 Generated with Claude Code