Compare commits

..
Author SHA1 Message Date
Niels Lohmann 15348ea128 Fix develop CI: GCC 4.8, Clang 3.4 and clang-tidy errors
- unit-json_patch.cpp: move multi-line raw strings out of CHECK(), which
  GCC 4.8 cannot parse inside a macro argument
- unit-recursion_depth_limit.cpp: value-initialize the const std::hash
  (Clang 3.4 rejects default initialization of a const object)
- json_view: silence modernize-use-transparent-functors (std::less<> is
  C++14), bugprone-suspicious-stringview-data-usage (the length comes from
  the node) and the deliberate use-after-clear() / owning-memory hits in
  unit-json_view_builder.cpp; use a raw string literal there

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-12 12:15:54 +01:00
Niels Lohmann cdfb130fa6 Read a std::u8string from a JSON string and store it as one in every configuration (#6075)
* Read a std::u8string from a JSON string and store it as one in every configuration

In C++20, json(std::u8string) was stored as a JSON string only when
std::filesystem::path support was available (the to_json overload sat
inside the JSON_HAS_FILESYSTEM guard), and there was no matching
from_json, so get<std::u8string>() threw type_error.302 ("type must be
array, but is string"). Without std::filesystem, the same expression
still produced an array of code units (the 3.12.0 behavior).

Move the to_json overload out of the filesystem guard so that a
std::u8string is a string in every configuration, build the value
directly from the code units (this also works for a custom string_t
such as the tests' alt_string), and add a from_json overload that reads
a JSON string. The array of numbers written by 3.12.0 is still accepted;
other types throw type_error.302 ("type must be string or array").
std::u8string_view and char8_t arrays are unchanged.

The tests now run in C++20/23 (the file mentions JSON_HAS_CPP_20/23),
also without std::filesystem (test-conversions2_no_filesystem), and with
ordered_json and a custom string type. The new behavior is documented.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fill the std::u8string with resize and indexing instead of push_back

The push_back loop is not vectorized: reading a 1 kB string took about
2.2 us against 0.39 us with resize and direct element access (clang -O2).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Write the std::u8string from_json template header in the compact form

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Ignore libstdc++ char to char8_t sign change in the clang sanitizer job

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-12 08:40:18 +01:00
Niels Lohmann 17e3de0fcc docs: add version-history notes for fixed start_pos and error positions (#6089)
Two bugs of 3.12.0 are fixed on develop without a note on the pages.
With JSON_DIAGNOSTIC_POSITIONS, start_pos() of a string that contains
escape sequences was too large; the position is now that of the opening
quote. A parse error for an unexpected number that is followed by a line
feed reported column 0 (for "1 2" and a line feed, line 1, column 0); for
"1", line feed, "2", line feed it now reports line 2, column 1.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-12 08:40:06 +01:00
Niels Lohmann 07aa3d1d69 docs: fix NaN ordering, FILE* locking and binary integer-range statements (#6088)
* docs: state that NaN ordering inside containers depends on the standard

When the element-wise comparison of arrays or objects reaches a NaN and
a number (or another NaN) before any other difference, the result
depends on the language standard. Before C++20 the pair counts as
equivalent and the following elements (or the sizes) decide, so
[1] <= [NaN] is true. Since C++20 the comparison is unordered and
<, <=, >, >= all yield false. Document this on operator<, <=, >, >=
and <=>.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: fix the FILE* notes of parse() and the locking promise

The performance note of parse() said a FILE* is read one character at a
time. With glibc and the Apple C library the characters in the FILE's
buffer are scanned in bulk; only elsewhere is it one character at a
time. The file is locked once only where the C library has unlocked
stdio functions (not on musl, MinGW, newlib, with
JSON_DISABLE_UNLOCKED_FILE_IO, or under MSan). Qualify the promise on
parse.md, sax_parse.md, the parsing page and the performance page.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: fix integer ranges in the MessagePack and BJData tables

number_integer_t is std::int64_t, so number_integer cannot hold values
above 9223372036854775807; those are number_unsigned. The MessagePack
row for uint 64 listed the range up to 18446744073709551615, and the
BJData table had a uint64 row for number_integer that can never be
selected. The CBOR table was already corrected.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: name the macros that turn off FILE* locking and bulk scans

parse() and sax_parse() now say that the FILE* is locked unless
JSON_DISABLE_UNLOCKED_FILE_IO is defined to 1, and the input-source
table and the performance page name JSON_DISABLE_STREAM_BULK_SCAN and
JSON_DISABLE_UNLOCKED_FILE_IO with what each turns off.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-12 08:40:05 +01:00
dependabot[bot] 03ad4a3e7f build(deps): bump the codeql-action group with 3 updates (#6065)
Bumps the codeql-action group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/upload-sarif](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.39.2 to 4.39.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1fc58d04a4d4374f921bdac573c3811c240f9c26...33d7b85da1a04171e211befe2c739303b9a4c066)

Updates `github/codeql-action/analyze` from 4.39.2 to 4.39.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1fc58d04a4d4374f921bdac573c3811c240f9c26...33d7b85da1a04171e211befe2c739303b9a4c066)

Updates `github/codeql-action/upload-sarif` from 4.39.2 to 4.39.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1fc58d04a4d4374f921bdac573c3811c240f9c26...33d7b85da1a04171e211befe2c739303b9a4c066)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.39.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.39.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.39.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-11 22:37:57 +01:00
Niels Lohmann e0b76bac31 Guard the image part of the alt_string test against big-endian targets (#6074)
The test for json_view documents over an alternative string type called alt_editable_document::load() and save(). Both exist only on little-endian targets, so the test suite did not compile on big-endian targets such as s390x. Guard the two image lines with the condition the other image tests use (unit-json_view_image.cpp, unit-json_view_positions.cpp).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:37:46 +01:00
Niels Lohmann 02d57db31e Do not compare an edited NaN equal to itself in json_view (#6085)
Two views compared equal when the tokens of two floats were equal, and
the same node was equal to itself. An edit writes a NaN as the token
"nan", so a view of it (or of an array holding it) was equal to itself
and to another NaN, while json(NaN) == json(NaN) and view == json are
false. The token shortcut now excludes the edited "nan" token (found by
the missing digit layout), dispatched on the document mode by a tag: a
read-only document cannot hold a NaN, and its walk is unchanged
(instruction count and cycles on canada, citm and twitter within
noise). For editable documents the check costs about 2 % instructions
on canada (cycles within noise). The same-node shortcut is kept for
read-only documents and for editable ones that were never edited; a
document with edits is walked, so comparing it with itself is linear
instead of constant.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:36:54 +01:00
Niels Lohmann 9d35fe6d21 Reject integer tokens in float nodes of fully checked images (#6084)
image_check::full accepted a float node whose token is an integer literal
that fits 64 bits (e.g. 10446744073709551616 changed from 18446744073709551616).
The parser stores an integer node for such a token, so the loaded
document differed from what parsing the same text gives. check_float_ranges
now requires an integer token of a float node not to fit uint64_t (or
int64_t when negative).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:36:54 +01:00
Niels Lohmann 53d097f980 docs: correct the json_view dump() guarantee for loaded images (#6087)
dump() said a view never throws type_error.316 because the text is always
validated. A document loaded with image_check::bounds or none can hold
invalid UTF-8: dump() writes it unchanged, and dump(ensure_ascii) throws
type_error.316. (The matching correction of number_token() is part of the
read() documentation change below which this commit is stacked.)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:36:17 +01:00
Niels Lohmann 94331d1050 Document that read() must not be given a view of the document itself (#6086)
* Document that read() must not be given a view of the document itself

basic_json_document::read() releases the previous text, the decoded
strings and the edits before it parses its input. An input that points
into them (source(), get_string(), number_token(), or a string of an
edited document) is read after its memory was cleared or freed.

A guard that copies such input costs about 1 ns per read() (5 % of a
read of a 7-byte input), which this API exists to avoid, so state the
precondition in read.md, lifetime.md and the pages of the views that
hand out such strings instead, and show how to copy the string first.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Assert in debug builds that read() is not given its own text

read() releases the document's text, decoded strings, owned image and
edits before it parses, so an input that points into them is read after
release. Without NDEBUG, read() now asserts that a borrowed input, a
copied contiguous input and (before C++17) a string_ref input are not
held by the document: its owned text, its decoded strings, an owned
image, and the current buffer of edited strings. Release builds keep
the documented precondition without a check, which would cost 2-4.5 %
per read() of a small message.

JSON_ASSERT is undefined at the end of json.hpp, so the view uses
assert() through a new internal NLOHMANN_VIEW_ASSERT.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:36:17 +01:00
Niels Lohmann a7b377e9ec docs: state what the converting constructor does on a string/key mismatch (#6092)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:33:11 +01:00
Niels Lohmann abab6f2f76 docs: qualify push_back({value}) and += {value} brace-init behavior (#6091)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:32:50 +01:00
Niels Lohmann b5967d0ff9 Export the editing API of json_view from the C++20 module (#6090)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:32:29 +01:00
Niels Lohmann 1dae5a66b1 Add JSON_RECURSION_DEPTH_LIMIT to set how deep value walks recurse (#6081)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:32:05 +01:00
Niels Lohmann 0660d78b9c Correct the UTF-8 claims for binary input in the security guide (#6080)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:31:28 +01:00
Niels Lohmann 0d38e99d83 Resume patch operations from the common prefix in constant time per step (#6077)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:31:24 +01:00
Niels Lohmann be08f42456 Fix parse(container, function) for plain functions as callback (#6073)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:31:20 +01:00
Niels Lohmann 93d2dd2c69 ci: pin Semgrep version without hashes (#6072)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 22:31:15 +01:00
Niels Lohmann 017a3ce001 ci: revert importlib-metadata to 8.7.1 to fix Semgrep install (#6064)
The Dependabot bump to 10.1.0 (#6059) conflicts with the pinned
opentelemetry-api 1.37.0 (importlib-metadata<8.8.0), so the Semgrep job
failed with ResolutionImpossible. Ignore importlib-metadata in Dependabot
since semgrep pins it indirectly.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-11 09:18:40 +01:00
Niels LohmannandClaude Sonnet 5.5 8d591bf720 Read iterator pairs of std::string and std::vector as pointer ranges (#6029)
* Read iterator pairs of std::string and std::vector as pointer ranges

Before C++20, a pair of iterators of a contiguous container cannot be told
from any other random-access iterator, so json::parse(s.begin(), s.end())
on a std::string or std::vector<char> was read one character at a time,
without the bulk scans of strings, numbers and indentation that the
container itself gets (the container overload of input_adapter() already
maps it to a pointer range). The iterators of std::string and of a
std::vector of 1-byte integral elements are now matched exactly and mapped
to the pointer range they cover; an empty range is not dereferenced. The
character type stays the same, so the results and error positions do not
change.

parse(first, last) of a std::string gets 1.7-2.0x as fast in C++17 on an
Apple M1 Max, accept(first, last) 2.7-3.1x, and from_cbor(first, last) of a
std::vector 8-23% faster.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix CI: clang-tidy, unused result, Infer false positive in tests

- unit-deserialization.cpp: raw string literals, and assign the result
  of parse() in CHECK_THROWS_AS (-Wunused-result)
- unit-bson.cpp: mark Infer's NULLPTR_DEREFERENCE on from_bson() with an
  iterator pair as a false positive

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix implicit char conversion in iterator-pair test

The test built std::vector<unsigned char> and <signed char> from a char
range, which -fsanitize=integer reports as an implicit conversion.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Tests: sweep_allocator::construct() accepts pointers to const

Recent libc++ (emsdk latest) probes construct(const T*, const T&) in an
unevaluated context while checking for trivial copies; the static_cast to
void* in the noexcept specification then fails to compile.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Tests: guard sweep_allocator::construct() against const types instead of const_cast

The const_cast added in the previous commit is rejected by
cppcoreguidelines-pro-type-const-cast (clang-tidy). Make construct() not
viable for const U instead, which is what libc++ probes for.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix CI: astyle and clang-tidy for sweep_allocator::construct()

astyle 3.4.13 reformats the enable_if<!...> default template argument
(Check amalgamation). Delete the const overload instead, which makes the
libc++ probe for const objects fail in the SFINAE context just the same
and needs no <type_traits>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Iterator ranges: astyle-safe trait members; reserve in the test helper

The byte check and the container match are static constexpr members
instead of integral_constant arguments and a default template argument
with comparisons, which astyle spaced out.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 22:30:31 +01:00
Niels LohmannandClaude Sonnet 5.5 2a4aec3e69 SWAR and vector scans: test for special bytes in fewer operations (#6033)
* SWAR: test for special bytes in the reduced form

The usual test for a zero byte, (x - ones) & ~x & high, needs `& ~x` only
to keep bytes from 0x80 out. The tests for quotes, backslashes and control
characters in string_scan.hpp and the serializer also flag every byte from
0x80 with `v & high`, so the `& ~x` terms can be dropped. Clang finds this
reduction itself; GCC does not and emits 24 instead of 16 instructions.

The masks are the same for 1.3 billion random words and for every pair of
adjacent byte values. With GCC 15 on x86-64: accept() -2 to -4 %,
sax_parse() -2 %, dump() -3 %. Clang is unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Vector scans: one compare for the quote and the control characters

The SSE2/NEON tests for the bytes a string run stops at compared with the
quote, with the backslash and with 0x20 separately. Flipping bit 1 maps
0x00-0x1F onto itself and the quote onto 0x20, and every other ASCII byte
to 33 or more, so (c ^ 2) < 33 finds the quote and the control characters
with one compare: signed where the bytes from 0x80 stop the run too,
unsigned (or with a saturating subtraction / minimum on SSE2) where they
do not. This saves one vector operation per 16 bytes in all twelve vector
tests: the lexer's vector_plain_run() and vector_utf8_run(), dump()'s
copy_plain_halves() and copy_plain_vectors(), and json_view's scanner and
serializer. The DEL tests of the ensure_ascii paths stay.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Tests: sweep_allocator::construct() accepts pointers to const

Recent libc++ (emsdk latest) probes construct(const T*, const T&) in an
unevaluated context while checking for trivial copies; the static_cast to
void* in the noexcept specification then fails to compile.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
(cherry picked from commit 794bf70823c12a2a1cb2a8a95f34dfb6357746a8)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Tests: guard sweep_allocator::construct() against const types instead of const_cast

The const_cast added in the previous commit is rejected by
cppcoreguidelines-pro-type-const-cast (clang-tidy). Make construct() not
viable for const U instead, which is what libc++ probes for.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
(cherry picked from commit de099fa607001b710862fd1bbecef3609c54cbc4)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix CI: astyle and clang-tidy for sweep_allocator::construct()

astyle 3.4.13 reformats the enable_if<!...> default template argument
(Check amalgamation). Delete the const overload instead, which makes the
libc++ probe for const objects fail in the SFINAE context just the same
and needs no <type_traits>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 22:30:28 +01:00
Niels Lohmann 8c0b48088c ci: ignore Dependabot bumps of packages pinned by semgrep (#6062)
semgrep requires exact or compatible-release versions of mcp, boltons and
the opentelemetry packages. Bumping them alone makes the Semgrep job fail
at pip install (see #6054, #6055, #6057, #6058, #6060, #6061), so leave
them to move with the semgrep bump.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 21:45:05 +01:00
dependabot[bot] 0dbeb39811 build(deps): bump importlib-metadata in /cmake/requirements (#6059)
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.7.1 to 10.1.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v8.7.1...v10.1.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-version: 10.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-10 21:44:55 +01:00
dependabot[bot] d430252840 build(deps): bump the codeql-action group with 3 updates (#6056)
Bumps the codeql-action group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/upload-sarif](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.39.1 to 4.39.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/9b3254085494b55a0f7c4c2b3d2d52cc712ce370...1fc58d04a4d4374f921bdac573c3811c240f9c26)

Updates `github/codeql-action/analyze` from 4.39.1 to 4.39.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/9b3254085494b55a0f7c4c2b3d2d52cc712ce370...1fc58d04a4d4374f921bdac573c3811c240f9c26)

Updates `github/codeql-action/upload-sarif` from 4.39.1 to 4.39.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/9b3254085494b55a0f7c4c2b3d2d52cc712ce370...1fc58d04a4d4374f921bdac573c3811c240f9c26)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-10 21:44:46 +01:00
Niels Lohmann 9ed8f85b21 build(deps): bump semgrep from 1.168.0 to 1.184.0 in /cmake/requirements (#6052)
Regenerate the hash-pinned requirements file. This raises the transitive
pins pyjwt 2.13.0 -> 2.15.1 and mcp 1.23.3 -> 1.29.0 (also click 8.1.8 ->
8.4.2, new structlog 26.1.0), closing 18 of the 23 open Dependabot alerts.

The remaining alerts cannot be fixed until Semgrep relaxes its pins:
mcp needs 1.30.0 (Semgrep pins mcp==1.29.0) and opentelemetry-api needs
1.45.1 (Semgrep pins ~=1.37.0).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 15:25:03 +01:00
Niels Lohmann 88ab2d56f5 docs: update the README for the release (#6051)
* docs: update the README for the release

- Show the animated SVG feature slideshow (light and dark variant) instead
  of json.gif.
- Thank 20 further contributors and extend two existing entries.
- Fix the bad_serializer example (invalid template header, second function
  named to_json instead of from_json, missing template keyword).
- Add "Zero-copy views" to the table of contents, link ordered_json to its
  API page, and point the speed design goal to the performance page.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* chore: remove the stale ChangeLog.md and docs/json.gif

ChangeLog.md was last generated in January 2025 and still ends at
"unreleased (2024-12-22)"; 3.12.0 shipped without an entry. The release
notes live on the GitHub releases page and in docs/mkdocs/docs/home/releases/.
Remove the file, the Makefile target that called github_changelog_generator,
and the README credit for that tool.

docs/json.gif is an old copy of the feature slideshow that nothing
references; the home page uses the SVGs built by docs/mkdocs/scripts/slideshow/.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* chore: remove the unreferenced docs/mkdocs/docs/images/json.gif

The README header and the home page use the SVG slideshow built by
docs/mkdocs/scripts/slideshow/.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 15:24:47 +01:00
Niels Lohmann 5901d861ff json_view: keep tokens without an integer digit out of the float batch (#6049)
dump() batches float tokens of 16 or 17 digits with at least 8 fraction
digits. A damaged image loaded with image_check::bounds can record a
token with no digit before the point and 17 after it, which no JSON
number has. Its q = -17 is outside the range of locate_token(), which
then read fives[17] past its 17 entries; on x86-64 the resulting digits
had 18 digits and failed the assertion of write_decimal() (OSS-Fuzz
issue 583898810, json_view_image_fuzzer).

The batch now takes 8 to 16 fraction digits, which every valid token of
16 or 17 digits has, so it batches the same tokens of valid documents.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 15:24:33 +01:00
Niels Lohmann eac1b4343b ci: install the remaining pip dependencies with --require-hashes (#6050)
Install Semgrep from a new hash-pinned requirements file in
cmake/requirements (covered by Dependabot) instead of a version-only
"pip install semgrep==1.168.0", and pass --require-hashes to the astyle
and natvis installs in check_amalgamation.yml, whose requirements files
got their hashes in #6046. Addresses the Scorecard Pinned-Dependencies
code-scanning alerts for semgrep.yml and check_amalgamation.yml.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 08:33:30 +01:00
Niels Lohmann a78e2a6b10 docs: blog post: remeasure on the 3.13.0 candidate and describe the latest performance work (#6041)
* docs: blog post: describe the performance work of #6020, #6028-#6031 and #6033

Theme 1 gets the iterator pairs of std::string and std::vector, a new
subsection on the fused contiguous parser after yyjson's reader, and the
FILE buffer window after gnulib's freadptr(). Theme 2 shows the reduced
SWAR test and the (c ^ 2) < 33 vector compare. Themes 4 and 6 get the
binary strings without a zero fill and the 64 KiB window of the vector
sink. Appendices H and J list the new pull requests and their checks.

The numbers of the suite, the timeline, the comparison and the compile
times are not remeasured yet.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: blog post: remeasure on develop with #6029, #6031, #6033 and #6039

Rerun on develop 7fc8312c64 with the open pull requests #6029, #6031,
#6033 and #6039 merged, measured on November 7: the suite (median of 12
runs, now 3.3x), the timeline (358 commits, #6031 shown as a pending
step on the candidate, #6028 labeled, a tick for November), the compile
times, the stream gap, the allocator check and the comparison on the
Mac and on x86-64, including the held-out files.

The comparison now leaves out the "copy untimed" variants, as the post
describes, and the held-out chart no longer counts json::accept() as
another library. Appendix D, the Intel column of Appendix B and the
GCC 16/Clang 23 check on the Mac are still from November 3.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 07:12:46 +01:00
dependabot[bot] ffe77931b0 build(deps): bump mkdocs-git-revision-date-localized-plugin (#6047)
Bumps [mkdocs-git-revision-date-localized-plugin](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin) from 1.6.2 to 1.6.3.
- [Release notes](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/releases)
- [Commits](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/compare/v1.6.2...v1.6.3)

---
updated-dependencies:
- dependency-name: mkdocs-git-revision-date-localized-plugin
  dependency-version: 1.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-10 07:11:35 +01:00
dependabot[bot] 2e52aa372b build(deps): bump watchdog from 6.0.0 to 7.0.0 in /tools/serve_header (#6048)
Bumps [watchdog](https://github.com/gorakhargosh/watchdog) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/gorakhargosh/watchdog/releases)
- [Changelog](https://github.com/gorakhargosh/watchdog/blob/master/changelog.rst)
- [Commits](https://github.com/gorakhargosh/watchdog/compare/v6.0.0...v7.0.0)

---
updated-dependencies:
- dependency-name: watchdog
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-10 07:11:30 +01:00
dependabot[bot] 667fc8e3ab build(deps): bump katex and mermaid in /docs/mkdocs/scripts/mermaid (#6043)
Bumps [katex](https://github.com/KaTeX/KaTeX) to 0.18.10 and updates ancestor dependency [mermaid](https://github.com/mermaid-js/mermaid). These dependencies need to be updated together.


Updates `katex` from 0.16.47 to 0.18.10
- [Release notes](https://github.com/KaTeX/KaTeX/releases)
- [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.47...v0.18.10)

Updates `mermaid` from 11.17.2 to 12.3.0
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.17.2...mermaid@12.3.0)

---
updated-dependencies:
- dependency-name: katex
  dependency-version: 0.18.10
  dependency-type: indirect
- dependency-name: mermaid
  dependency-version: 12.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-10 07:09:12 +01:00
Niels LohmannandClaude Sonnet 5.5 33f15727ce Parse JSON in one block of memory with a fused pointer-based parser (#6031)
* Parse JSON in one block of memory with a fused pointer-based parser

The parser and the lexer exchange one token at a time, and the lexer keeps
its state (the current character, a pending unget, where the token starts)
in members, which every character pays for: json::accept() of twitter.json
took 2.8x as long as json_document::accept(). detail::contiguous_parser
reads text in one block of memory through a local pointer, with the
grammar, the whitespace and the tokens in one loop (after yyjson's reader),
and drives a SAX handler: parse()'s DOM handler, accept()'s handler, or the
user's handler of sax_parse(). It is used with the default options (no
callback, no comments, no trailing commas; sax_parse() with strict = true).

Errors are reported by the library parser, so the exception, its message and
its position stay the same. contiguous_parser stops at the token that the
library parser would reject, before reporting it, so the events up to the
error are the same; then:
- parse() runs the library parser with a handler that only reports the
  error (json_sax_error_reporter), with the lexer of parse(), which is
  instantiated anyway;
- sax_parse() runs it with a handler that forwards only parse_error() to
  the user's handler (json_sax_error_forwarder), so no event arrives twice;
- accept() returns the verdict.
A handler that returns false stops the parse as before. An error at the end
of a large input costs parse() about 1.2x of what it cost before.

On an Apple M1 Max: accept() 1.6-2.0x as fast, parse() 10-16% faster,
ordered_json 11-16%, sax_parse() with a user's handler 1.36-1.46x, small
messages 12-22% (parse()) and 2.1-2.4x (accept()). On x86-64 (GCC 15,
Clang 21): accept() 1.6-1.9x, parse() 5-17%, ordered_json 12-19%,
sax_parse() 1.32-1.57x. A translation unit that parses a string compiles 3%
(-O0) and 9% (-O2) slower.

unit-contiguous_parser.cpp compares the contiguous path with the byte path
(std::list iterators) on corner cases, generated documents, their
truncations and mutations, deep nesting, and narrow number types: the same
values with the same number types and float bits, exception messages,
accept() verdicts and sax_parse() events.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix CI: unused parameter without exceptions, cpplint, BUILD.bazel

- json_sax_error_reporter::parse_error: mark the exception unused, as
  JSON_THROW does not use it with JSON_NOEXCEPTION
- unit-contiguous_parser.cpp: compare the exceptions of both paths only
  when exceptions are enabled (invalid input aborted the test otherwise)
- silence cpplint's readability/braces false positives on the if
  statements inside the parser's macros
- add contiguous_parser.hpp to BUILD.bazel

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* accept(): skip digits without computing their value

accept() needs the value of a number only for a float whose exponent may
reach the largest value. skip_digits() finds the end of the digits without
the multiply-adds of accumulate_digits(), and once an 8-byte word fails the
digit test, the remaining loop needs no bounds check: a non-digit is in
that word. The value is computed afterwards for the rare floats that need
it.

accept() of canada.json: -16 % on Apple M1, -22 % with GCC 15 and -21 %
with Clang 21 on x86-64; files with many numbers -9 to -15 %.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Tests: mark parser callback in unit-contiguous_parser noexcept

Fixes the -Werror=noexcept failure with GCC 26 (ci_test_standards_gcc (26)).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Tests: sweep_allocator::construct() accepts pointers to const

Recent libc++ (emsdk latest) probes construct(const T*, const T&) in an
unevaluated context while checking for trivial copies; the static_cast to
void* in the noexcept specification then fails to compile.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
(cherry picked from commit 794bf70823c12a2a1cb2a8a95f34dfb6357746a8)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Tests: guard sweep_allocator::construct() against const types instead of const_cast

The const_cast added in the previous commit is rejected by
cppcoreguidelines-pro-type-const-cast (clang-tidy). Make construct() not
viable for const U instead, which is what libc++ probes for.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
(cherry picked from commit de099fa607001b710862fd1bbecef3609c54cbc4)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix CI: astyle and clang-tidy for sweep_allocator::construct()

astyle 3.4.13 reformats the enable_if<!...> default template argument
(Check amalgamation). Delete the const overload instead, which makes the
libc++ probe for const objects fail in the SFINAE context just the same
and needs no <type_traits>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Contiguous parser: review fixes (wording, macros, diagnostic positions)

- "default parser" instead of "library parser" in comments and tests
- the three comments in json.hpp say the same thing
- current() replaces the repeated `p != end ? *p : 0`
- the backslashes of each macro are aligned
- the docs say that JSON_DIAGNOSTIC_POSITIONS turns the fast path off

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 07:08:44 +01:00
Niels Lohmann b2626e25f6 ci: pin pip dependencies by hash (#6046)
Add sha256 hashes for every release file to the requirements of the
astyle and natvis tools, and install Meson in the ubuntu workflow from a
new hashed requirements file with --require-hashes instead of an
unpinned "pip install meson". pip verifies all installs from these files
once they contain hashes, including the ones of the Makefile and
cmake/ci.cmake. Addresses the Scorecard Pinned-Dependencies alerts for
ubuntu.yml; the check_amalgamation.yml flags follow once develop has the
hashes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 07:08:25 +01:00
Niels Lohmann 77a608c2ce ci: run CodeQL with build-mode none (#6045)
Autobuild compiled the whole test suite. The resulting database was so
large that the hosted runner repeatedly lost communication with the
server while importing the TRAP files (about 80 minutes in), which left
failed CodeQL analyses on develop and on pull requests. Analyze the
sources without building them instead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-10 07:08:13 +01:00
Niels Lohmann 6f01030c4e docs: fix json_view links in the 3.13.0 performance blog post (#6044)
#6018 split features/json_view.md into features/json_view/ sub-pages while
#6022 added a blog post linking to the old page, so `mkdocs build --strict`
failed on develop (Publish documentation run 42577793091). Point the links
to json_view/index.md and json_view/editing.md.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-09 11:37:52 +01:00
Niels Lohmann 6a852ab96d Split the json_view feature page into sub-pages (#6018)
* Split the json_view feature page into sub-pages

The single features/json_view.md page grew to ~600 lines covering eight
topics. Split it into features/json_view/ with an overview page and
pages for ownership and lifetime, differences from basic_json, reading
values, generic code, editing, and images.

The new differences page collects what was spread over several sections
("What is the same", "What is different", "Duplicate keys", and notes in
the conversion and serialization sections) under one summary table, and
adds the parts of the basic_json interface a view does not have. The
other pages now link to it instead of restating the rules.

Internal links across the documentation point to the new pages.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Docs: fix stale comments and trim repeated text in json_view pages

- fig-jsonview.js/.css: point the header comments at the new pages
- reading.md: views' strings are valid as long as the document (and a
  borrowed source text), as lifetime.md says
- reading.md, generic_code.md: link to differences.md instead of
  restating the member-order and repeated-key rules
- differences.md: drop the third mention of ordering comparisons, name
  rbegin()/rend() in the iterator row
- exceptions.md: link editable documents to editing.md

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-09 08:12:00 +01:00
Niels Lohmann 7757de3a58 Iterator: test for objects before arrays (#6039)
#5835 replaced the switch on the value type in operator*, operator->,
operator++, operator-- and operator== of iter_impl with an if chain that
tests for an array first. Every step over an object then pays for a
failed test and a taken branch: with the iterator inlined, walking
twitter.json took 14% more cycles than with 3.12.0, and citm_catalog.json
3.5% more.

Test for objects first. Walking every value of the three files now
takes fewer cycles than with 3.12.0 (Apple M1 Max, Apple clang 21):
citm_catalog.json 1.21x, twitter.json 1.04x, canada.json 1.17x as fast.
canada.json, which holds only arrays, gives back part of the gain of
#5835 (develop: 1.62x); the other orders tried (arrays first without
hints, objects first without hints, a switch) each left one of the
files slower than 3.12.0.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-09 08:11:51 +01:00
Niels Lohmann 2c7474affc docs: add the blog post "How nlohmann/json 3.13.0 got 3× faster" (#6022)
* docs: add the blog post "How 3.13.0 got faster"

A post for the documentation (Home > Blog) on how 3.13.0 became faster:
eight themes with the pull requests behind them, the ideas taken from other
libraries, what did not work, how the fast paths are tested, and the
compile-time cost. Every number comes from a run on 2026-10-27 (3.12.0 against
develop plus the open non-draft PRs, Apple M1 Max) or from the linked PR.

- docs/mkdocs/docs/home/blog/how_3_13_0_got_faster.md: the post.
- docs/mkdocs/scripts/figures/perf_blog.py: draws its 12 figures (overview,
  eight timeline panels, the SWAR test, the cliffs, json_view throughput) as
  inline SVGs in the nj-charts style; data/perf_timeline.csv holds the
  timeline.
- tests/benchmarks/history: history.cpp and history.py time one fixed program
  against the header of every commit that changed it, interleaved, and write
  one CSV row per commit with its PR. The timeline of the post comes from it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: compare json_document with Glaze, sonic, RapidJSON and Rust parsers

Extends the comparison in "How 3.13.0 got faster" from yyjson, simdjson and
Boost.JSON to eleven engines: sonic-cpp, RapidJSON and Glaze (generic DOM,
lazy_json, reading into structs) in C++, sonic-rs, simd-json and serde_json in
Rust. Measured on an Apple M1 Max (Apple clang 21, Rust 1.99) and an x86-64
Haswell server (GCC 13, Clang 18). Every engine must return the same values,
string bytes and bit-identical number sums before it is timed.

The post now answers whether json_document is the fastest parser: it is the
fastest document parser at parse+dump everywhere and at parsing on Apple
silicon, ties with simdjson's DOM on x86-64, and loses to simdjson On-Demand
and Glaze structs when only a few fields are read.

tests/benchmarks/json_view/others holds the programs (bench_all.cpp, the Rust
crate with its Cargo.lock), summarize.py and the pinned versions and checksums.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: captions, test-file table and figure fixes for the 3.13.0 post

- caption every table and figure
- describe what makes twitter.json, citm_catalog.json, canada.json and
  jeopardy.json hard, and what each timeline panel exercises
- split the json_view layout figure into the tree and the index, with
  the example text above; fix the arrows into the string boxes
- arrows and a "less is better" axis label in the cliffs figure
- name nlohmann/json in the comparison tables and chart
- licenses of the adapted code in the thanks section

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: byline and licenses for the 3.13.0 post

- author and date below the title, styled as .nj-byline; its link gets
  no external-link icon
- the license of every library in the thanks section, checked against
  each project's license files; credit sonic-rs for QUOTE_TAB, which
  orjson adapted from it

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: dump comparison and split acknowledgments in the 3.13.0 post

- a dump workload in bench_all and bench_rs: each engine writes a
  document parsed beforehand, and every output must read back with
  bit-identical numbers; an optional argument selects one workload
- the post's comparison gains a write table, a throughput chart, a dump
  row for all three configurations, and the x86-64 cases it loses
- the thanks section is split into adapted code and borrowed ideas

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: x86-64 dump numbers with #5908's branch-free token digits

The dump of canada.json with GCC on x86-64 lost to yyjson by 0.78 because
GCC compiled #5908's token-digit selections as branches; 9fad526888 fixed
that. The post's x86-64 dump and parse+dump rows of canada.json are now
measured with it (GCC 0.95, Clang 0.91 against yyjson), theme 5 tells the
story, and "How we measured" says which rows include the commit.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Compare JSON libraries by use case in the json_view harness

bench_all and bench_rs now time use cases (validate, sum, select, edit,
minify, write) instead of library functions. Every library may solve a
use case in every way it offers; each variant's result is checked, and
three malformed inputs give its validation level (full, no UTF-8,
partial). summarize.py reports each library's fastest fully validating
variant and a faster one at a lower level, as text and as JSON.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: restructure the 3.13.0 post around themes and use cases

- deeper technical detail and code in the eight themes; pull request
  numbers move to the appendix, timeline steps are labelled by theme
- comparison with other libraries by use case, as heatmaps per
  platform, with validation levels and the caveats next to the results
- appendix: test files, all 182 suite rows with a histogram, timeline
  steps, algorithmic fixes, per-file tables, behaviour changes, setup,
  pull requests by theme

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Measure each library's fastest modes in the json_view harness

Address the points of a review from the other libraries' side:

- simdjson 5.0.8 (On-Demand structs, key selectors) and Glaze 9.1.0
- number-skipping modes: yyjson and RapidJSON raw numbers, Boost.JSON
  number_precision::none
- reused parsers and allocators for yyjson, RapidJSON and Boost.JSON
- Boost.JSON parse_into, Glaze generic with double numbers, and the
  same struct types for nlohmann, Glaze, simdjson and serde
- minified copies of twitter.json and citm_catalog.json
- a fourth probe (trailing garbage) and Glaze "(strict)" variants
- a new random variant order in every round, as a variant's time could
  depend on the one before it by up to 15 % on the Mac
- --files for other test files and --conformance for JSONTestSuite,
  with conformance-rs.sh for the Rust program

summarize.py also reports each library's best reused variant.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: rerun the library comparison of the 3.13.0 post with fairer rules

The figures and Appendix E use the reruns with each library's fastest
modes. Skipping number conversion while reading is now allowed for
every library; copying number text is still not chosen when writing.

The text now says who set the rules, where libraries that check less
win select, how minified files change the picture, and that the
reused jeopardy.json claim holds with every library reused. A new
section covers 19 held-out files and JSONTestSuite, including where
json_document loses, and lists what was not measured. Every
comparison number was recomputed; Glaze is credited in the intro.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: measure the 3.13.0 post against develop with #5964 and #5908

The suite (Appendix B, the short-version table, the overview and distribution
figures) and the timeline endpoint now measure develop d20c4dcd89 with #5964
and #5908 merged. The geometric mean stays 2.9x (2.95); Dump/signed_ints and
Dump/unsigned_ints gain 1.5x and 2.1x from #5964; GetVectorDouble/floats drops
from 10.3x to 7.0x because the old candidate's suite binary happened to compile
that loop faster (a standalone build takes 0.97 ms with both headers).

The timeline endpoint is the 10-27 value scaled by the ratio of the new to the
old candidate, both measured on 10-30, so that it stays comparable with the
other points of the timeline.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: add three figures to the 3.13.0 post

- short strings: two overlapping 16-byte loads and stores
- eight digits at a time: the three multiplications, lane by lane
- validate on the 19 held-out files, per file, Mac and x86-64

The prose the figures replace is trimmed.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: measure the 3.13.0 post against develop with all open pull requests

The baseline is now develop 9c349b1d7c with every open non-draft pull
request merged, including #5968, #5970, #5971, #5975, #5976, #5978,
#5979, #5980, #5981 and #5982. The suite was rerun on 11-01 with every
step repeated when the load rose above 5 while it ran; the comparison
with other libraries and the held-out files were rerun on 10-31.

- suite: 3.0x geometric mean (median 2.9x); Appendix B, the overview
  and the distribution regenerated
- timeline endpoint, Appendix D and the cliffs figure updated
- the held-out figure compares json_document with the fastest other
  library
- new paragraphs for the pull requests listed above

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: measure the 3.13.0 post against develop with the open pull requests of 11-02

Suite, timeline endpoint, Appendix D and the scalar-run numbers now come from
develop b2674a4a2f with #5990, #5991, #5992, #5996, #5997, #5998, #5999 and
#6000 merged. The scalar runs are measured against a build without them on
develop's code, which no longer checks for a scalar before entering the loop.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: measure the 3.13.0 post against develop with all open pull requests of 11-02

Suite, timeline endpoint, Appendix D and the scalar-run numbers now come from
develop b2674a4a2f with #5990-#5992, #5996-#6006 and #6008 merged. With #6008,
arrays of one element skip the scalar run, so nested arrays no longer pay for
it. The suite ran three rounds per version; the round of 3.12.0 and of 3.13.0
that differed from the other two was dropped, as the setup table says.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: fix the facts the review of the 3.13.0 post found wrong or imprecise

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: re-measure the 3.13.0 post and apply the review

Measure everything again against develop with the open pull requests of
11-03 (suite, timeline, comparison on the Mac and on x86-64, compile
times, stream gap), add the suite on an Intel MacBook Pro as a check,
mark the ways that keep number text as "N" in the use-case charts, move
the loop-shape box to theme 5, and apply the style and wording fixes of
the review.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: make the comparison of the 3.13.0 post fairer

Three reviews of "How it compares" (a maintainer audit of the harness, a
methodology review, a reader's red team) led to a full rerun on the Mac and
x86-64 with simdjson 5.1.1 and new ways in the harness:

- in-place parses with the copy made before the timer ("(copy untimed)",
  never chosen as a library's best) and json_document::parse_copy();
- RapidJSON through StringStream, and with default precision in validate,
  which reads no number;
- BENCH_ONLY takes several prefixes.

The post now gives the copy's real cost (11-14 us for twitter.json on the
Mac, not 30), what yyjson gains without it, the run-to-run noise, the LTO
check, the single-file losses that were missing, and qualifies the headline
claim. Every number in the section and Appendix E is recomputed; the
NUL-byte remark moved out of the body.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Format bench_all.cpp with astyle

The amalgamation check runs astyle over tests/ and failed on bench_all.cpp. The variant tables keep one variant per
line between *INDENT-OFF* and *INDENT-ON*; astyle formats the rest, and the list of use cases becomes a named array so
the loop header stays on one line.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Print the history benchmark's results with a stream

Flawfinder flagged the printf in history.cpp (CWE-134), although its format string is a constant. The stream prints the
same text, so history.py reads it as before.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: address review comments on the performance blog post

- fix wording: "are new", "for each input source", "the strings", "the last zero to seven bytes", "by" Danila
  Kutenin, "the result of that query"
- introduce `json_document` where it first gets a detail, say that the `-mbranches` flag is not used, explain
  why the position and token bookkeeping overloads are empty, and say that a step's percentage is relative to
  the time before the step and that a median of 4 is the mean of the two middle values
- write percentages without a space before `%` in the post and in the price figure (regenerated)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: wrap the prose of the performance blog post at 120 columns

Only line breaks change: the rendered page is identical after whitespace normalization.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: blog post: address the second round of review comments

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: blog post: note that Boost.JSON's tree lead comes from its arena

The comparison builds Boost.JSON's tree in a monotonic_resource. With its
default allocator and precise numbers, boost::json::parse() takes as long
as json::parse() on twitter.json, 5% longer on citm_catalog.json and 17%
less time on canada.json (M1, C++23 -O3, best of 3 x 30 runs; the arena
saves Boost.JSON 28-41%).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: blog post: comparing already passes the depth; copying measured slower without the counter

operator== walks the standard containers itself and passes the depth as an
argument (#5840). A copy that does the same, inserting element by element,
measured 4-16% slower on an M1 Max with Apple clang, so copying keeps the
thread_local counter.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: blog post: name every user of the depth counter and every input the document copies

The "Bounded recursion" paragraph said only copying and equality for custom
containers use the thread_local counter. Converting from another basic_json
specialization and the ordering comparisons use it too; equality for the
standard containers passes the depth instead.

The "Strings stay in the input" bullet named only parse_copy() and an rvalue
std::string as owning inputs. Streams, FILE* and other input that is not one
contiguous block of bytes are read into an owned buffer as well.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-09 08:11:10 +01:00
dependabot[bot] 6343775000 build(deps): bump reuse from 6.2.0 to 6.3.0 in /cmake/requirements (#6035)
Bumps [reuse](https://github.com/RichardLitt/standard-readme) from 6.2.0 to 6.3.0.
- [Release notes](https://github.com/RichardLitt/standard-readme/releases)
- [Commits](https://github.com/RichardLitt/standard-readme/commits)

---
updated-dependencies:
- dependency-name: reuse
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-09 08:10:36 +01:00
253 changed files with 81642 additions and 11594 deletions

No files matched your search

-2
View File
@@ -111,8 +111,6 @@ context which existing file needs to be extended, and only very few cases requir
When fixing a bug, edit `unit-regression3.cpp` and add a section referencing the fixed issue.
`unit-regression2.cpp` holds the older tests; the two files exist because a single one grew large enough for the
MinGW linker to fail relocating it, so please keep adding to the smaller file rather than growing the larger one.
Tests that call `sax_parse` go into `unit-sax_parse.cpp` rather than into a large test file: every call instantiates
the parser and the binary reader that recover from errors, which grows a test file considerably.
#### Exceptions
+2 -2
View File
@@ -67,10 +67,10 @@ jobs:
- name: Install astyle
run: |
python3 -mvenv venv
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
venv/bin/pip3 install --require-hashes -r $MAIN_DIR/tools/astyle/requirements.txt
- name: Install generate_natvis dependencies
run: pip3 install -r $NATVIS_TOOL_DIR/requirements.txt
run: pip3 install --require-hashes -r $NATVIS_TOOL_DIR/requirements.txt
- name: Regenerate the tools/macro_builder tables in macro_scope.hpp
run: |
+6 -7
View File
@@ -38,14 +38,13 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
uses: github/codeql-action/init@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
with:
languages: c-cpp
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
# Analyze the sources without compiling them. Autobuild compiled the
# whole test suite and the resulting database was so large that the
# runner often lost communication with the server while importing it.
build-mode: none
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
uses: github/codeql-action/analyze@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
+1 -1
View File
@@ -48,6 +48,6 @@ jobs:
output: 'flawfinder_results.sarif'
- name: Upload analysis results to GitHub Security tab
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
uses: github/codeql-action/upload-sarif@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
with:
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
+1 -1
View File
@@ -80,6 +80,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
uses: github/codeql-action/upload-sarif@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
with:
sarif_file: results.sarif
+6 -2
View File
@@ -52,8 +52,12 @@ jobs:
# the old action fell back to, running community rules with no token.
# SEMGREP_APP_TOKEN is still passed through so registry auth works if a
# token is ever added.
# The version is pinned, but not by hash: Semgrep pins its own dependencies
# to exact or compatible-release versions, so a hash-pinned lock file broke
# on almost every Dependabot bump of a transitive dependency. Dependabot
# only bumps the semgrep line; pip resolves the rest.
- name: Install Semgrep
run: python3 -m pip install --user semgrep==1.168.0
run: python3 -m pip install --user -r cmake/requirements/requirements-semgrep.txt
# `semgrep scan --sarif` always exits 0 even with findings; continue-on-error
# is a safety net so the SARIF upload still runs if the scan itself errors.
@@ -65,7 +69,7 @@ jobs:
# Upload SARIF file generated in previous step
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
uses: github/codeql-action/upload-sarif@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
with:
sarif_file: semgrep.sarif
if: always()
+2 -2
View File
@@ -42,7 +42,7 @@ jobs:
- name: Check that Meson and pkg-config offer the CMake options
run: make check_build_options
- name: Install Meson
run: pip install meson
run: pip install --require-hashes -r cmake/requirements/requirements-meson.txt
- name: Install with Meson
run: |
meson setup build-meson --prefix=${{ github.workspace }}/install
@@ -153,7 +153,7 @@ jobs:
container: ubuntu:24.04
strategy:
matrix:
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local]
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local, ci_test_recursion_depth_limit_0]
steps:
- name: Install build-essential
run: apt-get update ; apt-get install -y build-essential unzip wget git
+1
View File
@@ -30,6 +30,7 @@ cc_library(
"include/nlohmann/detail/exceptions.hpp",
"include/nlohmann/detail/hash.hpp",
"include/nlohmann/detail/input/binary_reader.hpp",
"include/nlohmann/detail/input/contiguous_parser.hpp",
"include/nlohmann/detail/input/input_adapters.hpp",
"include/nlohmann/detail/input/json_sax.hpp",
"include/nlohmann/detail/input/lexer.hpp",
-3272
View File
File diff suppressed because it is too large. Load diff
+1 -18
View File
@@ -1,4 +1,4 @@
.PHONY: all amalgamate check-amalgamation check_build_options pretty install_astyle clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check run_benchmarks pvs_studio serve_header reuse spdx
.PHONY: all amalgamate check-amalgamation check_build_options pretty install_astyle clean release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check run_benchmarks pvs_studio serve_header reuse spdx
##########################################################################
# configuration
@@ -40,7 +40,6 @@ MACRO_SCOPE_HPP=include/nlohmann/detail/macro_scope.hpp
all:
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd,_literals,_view}.hpp from the include/nlohmann sources"
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
@echo "ChangeLog.md - generate ChangeLog file"
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
@echo "check_build_options - check that the Meson build and the pkg-config files offer the options of the CMake target"
@echo "clean - remove built files"
@@ -175,22 +174,6 @@ check-amalgamation:
BUILD.bazel:
cmake -P cmake/scripts/gen_bazel_build_file.cmake
##########################################################################
# ChangeLog
##########################################################################
# Create a ChangeLog based on the git log using the GitHub Changelog Generator
# (<https://github.com/github-changelog-generator/github-changelog-generator>).
# variable to control the diffs between the last released version and the current repository state
NEXT_VERSION ?= "unreleased"
ChangeLog.md:
github_changelog_generator -o ChangeLog.md --user nlohmann --project json --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION)
$(SED) -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md
$(SED) -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md
##########################################################################
# Release files
##########################################################################
+36 -11
View File
@@ -1,4 +1,9 @@
[![JSON for Modern C++](docs/mkdocs/docs/images/json.gif)](https://github.com/nlohmann/json/releases)
<a href="https://github.com/nlohmann/json/releases">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/mkdocs/docs/images/json-dark.svg">
<img src="docs/mkdocs/docs/images/json-light.svg" alt="JSON for Modern C++: animated feature slideshow">
</picture>
</a>
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
[![Ubuntu](https://github.com/nlohmann/json/workflows/Ubuntu/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AUbuntu)
@@ -39,6 +44,7 @@
- [Conversions to/from arbitrary types](#arbitrary-types-conversions)
- [Specializing enum conversion](#specializing-enum-conversion)
- [Binary formats (BSON, CBOR, MessagePack, UBJSON, BJData, and BON8)](#binary-formats-bson-cbor-messagepack-ubjson-bjdata-and-bon8)
- [Zero-copy views](#zero-copy-views)
- [Customers](#customers)
- [Ecosystem](#ecosystem)
- [Supported compilers](#supported-compilers)
@@ -68,7 +74,7 @@ Other aspects were not so important to us:
- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs.
- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set.
- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set. The [performance](https://json.nlohmann.me/features/performance/) page shows how to get the most out of the library.
See the [contribution guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#please-dont) for more information.
@@ -495,7 +501,7 @@ bool key(string_t& val);
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
```
The return value of each function determines whether parsing should proceed. For `parse_error`, returning `true` [recovers from the error](https://json.nlohmann.me/features/parsing/error_recovery/): the parser repairs the input and continues.
The return value of each function determines whether parsing should proceed.
To implement your own SAX handler, proceed as follows:
@@ -503,7 +509,7 @@ To implement your own SAX handler, proceed as follows:
2. Create an object of your SAX interface class, e.g. `my_sax`.
3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
Note the `sax_parse` function only returns a `bool` indicating whether the input was parsed without errors and no SAX event returned `false`. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
### STL-like access
@@ -1052,7 +1058,7 @@ struct less_than_32_serializer {
Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention:
```cpp
template <typename T, void>
template <typename T, typename = void>
struct bad_serializer
{
template <typename BasicJsonType>
@@ -1063,10 +1069,10 @@ struct bad_serializer
}
template <typename BasicJsonType>
static void to_json(const BasicJsonType& j, T& value) {
static void from_json(const BasicJsonType& j, T& value) {
// this calls BasicJsonType::json_serializer<T>::from_json(j, value)
// if BasicJsonType::json_serializer == bad_serializer ... oops!
value = j.get<T>(); // oops!
value = j.template get<T>(); // oops!
}
};
```
@@ -1769,7 +1775,7 @@ I deeply appreciate the help of the following people.
330. [AniketDhemare](https://github.com/AniketDhemare) fixed a version in the documentation.
331. [Philip Müller](https://github.com/philip-paul-mueller) fixed an example.
332. [Leila Shcheglova](https://github.com/LeilaShcheglova) fixed a warning in a test.
333. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation.
333. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation and avoided temporary `basic_json` values for CBOR and MessagePack object keys.
334. [laterlaugh](https://github.com/laterlaugh) fixed some typos.
335. [Yuanhao Jia](https://github.com/MrJia1997) fixed the GDB pretty printer.
336. [Fallen_Breath](https://github.com/Fallen-Breath) fixed an example for JSON Pointer.
@@ -1847,7 +1853,7 @@ I deeply appreciate the help of the following people.
408. [Luke Banicevic](https://github.com/banaboi) fixed corrupt BSON output for lengths exceeding `INT32_MAX`, cleaned up the BSON writer, and improved the documentation.
409. [Patrick Armstrong](https://github.com/Patrick10199) updated the CBOR references and the half-precision float assertions.
410. [Yash Bavadiya](https://github.com/xevrion) added checks to all BSON reads.
411. [hum4nBeing](https://github.com/hum4nBeing) fixed the overflow handling of high-precision numbers in UBJSON.
411. [hum4nBeing](https://github.com/hum4nBeing) and [AJ369ninja](https://github.com/AJ369ninja) fixed the overflow handling of high-precision numbers in UBJSON.
412. [tomatotomata](https://github.com/tomatotomata) added checks for reading CBOR tagged subtypes.
413. [YingqiDuan](https://github.com/YingqiDuan) documented the BSON interoperability.
414. [KBS](https://github.com/youdie006) documented the standards compliance and the strictness of `parse()` and `operator>>`.
@@ -1861,6 +1867,26 @@ I deeply appreciate the help of the following people.
422. [Avionic Harshit](https://github.com/avionicharshit-byte) made `diff()` linear when an array shrinks.
423. [Qatadaha Bin Matloob](https://github.com/qatcod) fixed comparisons between integers and floats and fixed unparsable BJData output.
424. [Wu Shuwen](https://github.com/dajiaohuang) removed an unused include.
425. [Alexander Lanin](https://github.com/AlexanderLanin) improved the error message of `get_to()` for `const` arguments.
426. [satelliteprogrammer](https://github.com/satelliteprogrammer) added `merge_diff()` to create JSON Merge Patches (RFC 7396).
427. [Jeremy Nimmer](https://github.com/jwnimmer-tri) removed `alwayslink` from the Bazel build.
428. [Dadi Reddy Sai Praneeth Reddy](https://github.com/dsp0redy) made `to_msgpack()` throw when a size exceeds the MessagePack limit.
429. [ayush-singh-0601](https://github.com/ayush-singh-0601) reduced the runtime of the test suite.
430. [Kartikey Negi](https://github.com/ReturnKartikey) fixed the CBOR tag handling of tagged values that are not byte strings.
431. [Michiel van Slobbe](https://github.com/mvanslobbe) sped up the parser by checking for the expected separator first.
432. [Mohd Quamar Tyagi](https://github.com/Tyagiquamar) allowed SAX parsing of tagged CBOR.
433. [fhgffy](https://github.com/fhgffy) rejected NUL bytes in UBJSON and BJData high-precision numbers and fixed a `-Wswitch-enum` warning.
434. [Suyog Verma](https://github.com/commit-msuyog) used MSVC intrinsics for the 128-bit multiplication in the floating-point serialization.
435. [Bas van Dijk](https://github.com/basdedas) fixed a TU-local exposure of `UTF8_ACCEPT` and `UTF8_REJECT` in C++20 modules.
436. [Christoph Grüninger](https://github.com/gruenich) fixed a leak and a crash when an allocation throws during parsing.
437. [Amelia Barnaby](https://github.com/ameliabarnabyhub) made serializing discarded values to binary formats throw instead of writing corrupt output.
438. [Vesko Karaganev](https://github.com/veselink1) made the destructor use the provided allocator.
439. [Michael Sam](https://github.com/michaelsam94) kept allocation failures from escaping the `noexcept` destructor.
440. [Mihnea Magheru](https://github.com/sakuntalle) added a natvis visualizer for `detail::json_default_base`.
441. [Joseph Demarest](https://github.com/JosephDemarest) rejected nested indefinite-length CBOR strings.
442. [Afonso Januário](https://github.com/afonsojanu) made `std::hash<basic_json>` consistent with `operator==` for numbers.
443. [Kartik Gupta](https://github.com/KartikeyG-world) limited the natvis namespace variants to two ABI tags.
444. [manop55555](https://github.com/manop55555) reported a buffer over-read in `dump()` with large indentation values.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
@@ -1883,7 +1909,6 @@ The library itself consists of a single header file licensed under the MIT licen
- [**cpplint**](https://github.com/cpplint/cpplint) for style checks
- [**doctest**](https://github.com/doctest/doctest) for the unit tests
- [**Flawfinder**](https://dwheeler.com/flawfinder/) for static analysis
- [**GitHub Changelog Generator**](https://github.com/github-changelog-generator/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
- [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks
- [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros
- [**include-what-you-use**](https://include-what-you-use.org) to check the included headers
@@ -1950,7 +1975,7 @@ For more information, see [JSON With Commas and Comments (JWCC)](https://nigelta
By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs".
If you do want to preserve the insertion order, you can try the type [`nlohmann::ordered_json`](https://github.com/nlohmann/json/issues/2179). Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
If you do want to preserve the insertion order, you can try the type [`nlohmann::ordered_json`](https://json.nlohmann.me/api/ordered_json/). Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
See the [**documentation on object order**](https://json.nlohmann.me/features/object_order/) for more information.
+21
View File
@@ -342,6 +342,27 @@ add_custom_target(ci_test_no_thread_local
COMMENT "Compile and test without thread-local storage"
)
###############################################################################
# Change the number of levels the walks over a value recurse into.
###############################################################################
# JSON_RECURSION_DEPTH_LIMIT is 128 by default, which is what the whole test
# suite otherwise runs with. 0 sends every walk to its explicit stack at once,
# so this target runs the whole suite through the walks that the default only
# uses for values nested more deeply than 128 levels: results must not depend on
# the limit. The largest limit, 254, is covered by a build of
# tests/src/unit-recursion_depth_limit.cpp (see tests/CMakeLists.txt).
add_custom_target(ci_test_recursion_depth_limit_0
COMMAND ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_FastTests=ON
-DCMAKE_CXX_FLAGS=-DJSON_RECURSION_DEPTH_LIMIT=0
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_recursion_depth_limit_0
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_recursion_depth_limit_0
COMMAND cd ${PROJECT_BINARY_DIR}/build_recursion_depth_limit_0 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
COMMENT "Compile and test with JSON_RECURSION_DEPTH_LIMIT=0"
)
###############################################################################
# Coverage.
###############################################################################
+5
View File
@@ -9,6 +9,11 @@
# __n2)`, which stores `__n1 - __n2` in a signed difference_type and clamps it to int. Any comparison with a shorter
# view wraps that subtraction on purpose, and -fsanitize=integer reports it as implicit-integer-sign-change. json_view
# compares keys as string views, so the same check is excluded for <string_view> as well.
# std::filesystem::path::u8string() in libstdc++ 14 builds a std::u8string from the native char sequence, so
# basic_string's range copy (_S_copy_chars) converts every char >= 0x80 to char8_t, which -fsanitize=integer reports as
# implicit-integer-sign-change (test-conversions2_cpp20/cpp23, non-ASCII path). The conversion is intended and happens
# inside the standard library, not in this library.
[implicit-integer-sign-change]
src:*/include/c++/*/format
src:*/include/c++/*/string_view
src:*/include/c++/*/bits/basic_string.h
@@ -0,0 +1,3 @@
meson==1.12.1 \
--hash=sha256:930bc7542cbd9f57009e182fd014eba48cf1a0180a7b9006d2d32d8f168d8b02 \
--hash=sha256:ab0a6ca09f8ef70c564c8241fb5a23957886a0b53fb58412b5e07eaf07dba743
+1 -1
View File
@@ -1 +1 @@
reuse==6.2.0
reuse==6.3.0
@@ -0,0 +1 @@
semgrep==1.184.0
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

+17 -5
View File
@@ -67,7 +67,11 @@ basic_json(basic_json&& other) noexcept;
- **objects**: [`object_t`](object_t.md) and all kinds of compatible associative containers such as `std::map`,
`std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with a `key_type` compatible to `string_t`
and a `value_type` from which a `basic_json` value can be constructed.
- **strings**: `string_t`, string literals, and all compatible string containers can be used.
- **strings**: `string_t`, string literals, and all compatible string containers can be used. Since version 3.13.0,
this includes `#!cpp std::u8string` (C++20): its code units are stored as a JSON string and
[`get<std::u8string>()`](get.md) reads it back (in version 3.12.0, a `#!cpp std::u8string` was stored as an array
of numbers; `get<std::u8string>()` still accepts such an array). `#!cpp std::u8string_view` and `#!cpp char8_t`
arrays are not strings; they are stored as arrays of code units.
!!! note "Rvalue containers are moved from"
@@ -93,10 +97,18 @@ basic_json(basic_json&& other) noexcept;
The constructor tries to convert the internal `m_value` of the parameter. Each member value (object, array, string,
etc.) is serialized via the corresponding `to_json()` overload. For objects and strings, the conversion requires
that the *target* `basic_json` type's `object_t::key_type` (or `string_t`) be directly constructible from the
*source* type's corresponding member type via `is_constructible`. If this requirement is not met, the conversion
does not fail to compile; instead, it silently falls back to the array-conversion path, which represents objects
as arrays of `[key, value]` pairs and strings as arrays of character codes. This is a known limitation tracked in
[issue #3425](https://github.com/nlohmann/json/issues/3425).
*source* type's corresponding member type via `is_constructible`. If this requirement is not met, the result
depends on the *source* string type:
- If it is a range of characters (it has `begin()` and `end()`, as `std::string` does), the conversion falls back to
the array-conversion path, which represents objects as arrays of `[key, value]` pairs and strings as arrays of
character codes. This result has a different type than the source value, so the constructor's
[`JSON_ASSERT`](../macros/json_assert.md) fails; with its default definition, `assert`, the program aborts unless
`NDEBUG` is defined, in which case the array is returned.
- Otherwise, the conversion does not compile, even though `#!cpp std::is_constructible` reports `#!cpp true`; for
example, when converting a `basic_json` whose `string_t` is the `alt_string` of the unit tests to `json`.
This is a known limitation tracked in [issue #3425](https://github.com/nlohmann/json/issues/3425).
5. Creates a JSON value of type array or object from the passed initializer list `init`. In case `type_deduction` is
`#!cpp true` (default), the type of the JSON value to be created is deducted from the initializer list `init`
+6
View File
@@ -130,6 +130,12 @@ overload (3).
`#!cpp json(true).get<double>()` throw. This is the behavior of all 3.x versions;
[`basic_json_view::get<T>()`](../basic_json_view/get.md) behaves the same.
!!! note "`std::u8string` conversions"
Since version 3.13.0, `#!cpp get<std::u8string>()` (C++20) reads a JSON string; it also accepts an array of numbers,
the form in which version 3.12.0 stored a `#!cpp std::u8string`. Any other JSON type throws
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) ("type must be string or array").
!!! note "`std::optional` conversions"
Prior to version 3.13.0, `#!cpp get<std::optional<T>>()` (and other conversions to `std::optional<T>`) failed to
+2 -2
View File
@@ -50,7 +50,7 @@ Besides `basic_json`, the library offers a second way to hold a parsed JSON text
[`basic_json_document`](../basic_json_document/index.md), which owns or borrows the text and holds a flat index of the
values in it, and a [`basic_json_view`](../basic_json_view/index.md), a read-only handle to one value of such a document.
Both are defined in the header `<nlohmann/json_view.hpp>`; see
[Zero-copy JSON views](../../features/json_view.md) for the concept and the trade-offs.
[Zero-copy JSON views](../../features/json_view/index.md) for the concept and the trade-offs.
- [**json_document**](../json_document.md) / [**ordered_json_document**](../ordered_json_document.md) - read-only
documents of the `basic_json` specialization they belong to
@@ -363,7 +363,7 @@ Access to the JSON value
specification of the JSON data interchange format
- [`basic_json_document`](../basic_json_document/index.md) - / [`basic_json_view`](../basic_json_view/index.md) - parse a JSON
text into a flat index and read it through a view
- [Zero-copy JSON views](../../features/json_view.md) - when to use a document and a view instead of a `basic_json`
- [Zero-copy JSON views](../../features/json_view/index.md) - when to use a document and a view instead of a `basic_json`
- [Types](../../features/types/index.md) - how JSON values are stored and which types exist
- [Converting values](../../features/conversions.md) - get values out of a JSON value and put values in
- [Creating JSON values](../../features/creating_values.md) - all the ways to build a JSON value
+6 -3
View File
@@ -102,9 +102,12 @@ objects.
When converting an object from one `basic_json` specialization to another via the
[converting constructor](basic_json.md) (overload 4), the target `object_t`'s `key_type` must be
directly constructible from the source `basic_json`'s `string_t` type (or more generally, from the
source object's key type). If this requirement is not met, the conversion does not fail; instead,
the object is silently converted as an array of key-value pairs, which is incorrect. See
directly constructible from the source object's key type (with the default `std::map`, the source `basic_json`'s
`string_t`). If this requirement is not met and the source string type is a range of characters, the
object is converted as an array of key-value pairs, which is incorrect: the converting constructor's
[`JSON_ASSERT`](../macros/json_assert.md) fails; with its default definition, `assert`, the program aborts unless
`NDEBUG` is defined, in which case the array is returned. If the source string type is not a range of characters, the
conversion does not compile at all. See [`basic_json`](basic_json.md) and
[issue #3425](https://github.com/nlohmann/json/issues/3425) for details and an example.
## Examples
@@ -27,6 +27,16 @@ reference operator+=(initializer_list_t init);
`init` is converted into an object element and added using `operator+=(const typename object_t::value_type&)`.
Otherwise, `init` is converted to a JSON value and added using `operator+=(basic_json&&)`.
!!! note "Single braced JSON value"
For a call such as `#!cpp arr += {obj}`, where the initializer list contains a single JSON value `obj`,
compilers may disagree unless
[`JSON_BRACE_INIT_COPY_SEMANTICS`](../macros/json_brace_init_copy_semantics.md) is defined to `1`: GCC, for
example, selects this overload and appends a single-element array `[obj]`, whereas Clang selects the
`basic_json` overload and appends a copy of `obj`. With the macro, GCC and Clang both append a copy of `obj`.
To get a portable result without the macro, write `#!cpp arr += obj` to append a copy, or
`#!cpp arr += json::array({obj})` to append a single-element array.
## Iterator invalidation
For all cases where an element is added to an **array**, a reallocation can happen, in which case all iterators (including
@@ -60,6 +60,14 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -60,6 +60,14 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -61,6 +61,14 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -71,6 +71,14 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -55,6 +55,10 @@ Linear.
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
- For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result is `std::partial_ordering::unordered`. (Without C++20, the
comparison operators `<`, `<=`, `>`, and `>=` use the comparison of the containers, which treats such a pair as
equivalent; see [**operator<**](operator_lt.md).)
## Examples
+12 -6
View File
@@ -124,17 +124,19 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
[`basic_json_document::parse`](../basic_json_document/parse.md) accepts the same inputs and the same options, but
keeps the result as a flat index of the parsed values instead of a tree of `basic_json` values. It is faster and uses
less memory when only parts of the document are read, and it also accepts a pointer to a null-terminated wide string,
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view.md).
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view/index.md).
!!! tip "Performance"
Contiguous inputs (strings, string views, vectors, arrays, pointer ranges) are the fastest to parse. A
`#!cpp std::istream` comes close: the parser scans the characters its stream buffer holds in bulk, without reading
ahead, so the stream is left exactly where it would be after reading one character at a time. A `#!cpp FILE*` is
read one character at a time, but the file is locked only once, so other threads that use the same `FILE` object
wait until parsing is done. See [input sources and performance](../../features/parsing/index.md#input-sources-and-performance),
[`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md), and
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md).
ahead, so the stream is left exactly where it would be after reading one character at a time. With glibc
and the Apple C library, the characters the `#!cpp FILE` holds in its buffer are scanned in bulk like a stream's;
elsewhere a `#!cpp FILE*` is read one character at a time. Where the C library has unlocked stdio functions, the
file is locked only once, so other threads that use the same `FILE` object wait until parsing is done, unless
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) is defined to `1`. See
[input sources and performance](../../features/parsing/index.md#input-sources-and-performance) and
[`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md).
## Examples
@@ -311,6 +313,10 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
it as end of input; planned to become the default in version 4.0.0.
- Extended empty-input detection to also cover an `std::istream` without a stream buffer, and fixed a crash
(`std::terminate`) when parsing from an `std::istream` with `eofbit` in its exception mask, in version 3.13.0.
- Fixed the position that a [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) reports for an
unexpected number that is followed by a line feed in version 3.13.0: for the input `1`, line feed, `2`, line feed,
version 3.12.0 reported "line 2, column 0" and version 3.13.0 reports "line 2, column 1"; for `1 2` and a line feed,
the report was "line 1, column 0" and is now "line 1, column 3".
- Parsing from an `std::istream` scans the characters in its stream buffer in bulk in version 3.13.0
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md) restores reading one character at a
time).
@@ -27,6 +27,16 @@ void push_back(initializer_list_t init);
`init` is converted into an object element and added using `push_back(const typename object_t::value_type&)`.
Otherwise, `init` is converted to a JSON value and added using `push_back(basic_json&&)`.
!!! note "Single braced JSON value"
For a call such as `#!cpp arr.push_back({obj})`, where the initializer list contains a single JSON value `obj`,
compilers may disagree unless
[`JSON_BRACE_INIT_COPY_SEMANTICS`](../macros/json_brace_init_copy_semantics.md) is defined to `1`: GCC, for
example, selects this overload and appends a single-element array `[obj]`, whereas Clang selects the
`basic_json` overload and appends a copy of `obj`. With the macro, GCC and Clang both append a copy of `obj`.
To get a portable result without the macro, write `#!cpp arr.push_back(obj)` to append a copy, or
`#!cpp arr.push_back(json::array({obj}))` to append a single-element array.
## Iterator invalidation
For all cases where an element is added to an **array**, a reallocation can happen, in which case all iterators (including
+5 -7
View File
@@ -104,9 +104,7 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
## Return value
`#!cpp true` if the input was parsed without errors and no SAX event returned `#!cpp false`; `#!cpp false` otherwise.
In particular, the result is `#!cpp false` for input with errors, even if the SAX parser recovered from all of them
(see [error recovery](../../features/parsing/error_recovery.md)).
return value of the last processed SAX event
## Exception safety
@@ -126,9 +124,10 @@ consumer `sax` has a super-linear complexity.
A UTF-8 byte order mark is silently ignored.
When reading from a `#!c FILE*`, the file is locked while it is parsed. The lock is recursive, so the SAX handler may
use the same file with stdio functions, but it must not wait for another thread that uses the same `FILE` object; see
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md).
When reading from a `#!c FILE*` where the C library has unlocked stdio functions, the file is locked while it is parsed,
unless [`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) is defined to `1`.
The lock is recursive, so the SAX handler may use the same file with stdio functions, but it must not wait for another
thread that uses the same `FILE` object.
## Examples
@@ -165,7 +164,6 @@ use the same file with stdio functions, but it must not wait for another thread
- Added `ignore_trailing_commas` in version 3.13.0.
- Added `tag_handler` in version 3.13.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Recovering from parse errors (see [`parse_error`](../json_sax/parse_error.md)) added in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
after the parsed value when `strict` is `#!cpp false`.
@@ -67,3 +67,7 @@ Constant.
## Version history
- Added in version 3.12.0.
- Fixed in version 3.13.0: in version 3.12.0, the start position of a string that contains escape sequences (for
example `#!cpp "\n"` or `#!cpp "\u00e4"`) was too large, by the number of bytes the escape sequences add to the
source text. The position is now always that of the opening `"`. The "(bytes a-b)" range in the messages of
[`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) was wrong in the same way.
+5 -3
View File
@@ -60,9 +60,11 @@ String values are stored as pointers in a `basic_json` type. That is, for any ac
When converting a string value from one `basic_json` specialization to another via the
[converting constructor](basic_json.md) (overload 4), the target `string_t` must be directly
constructible from the source `basic_json`'s `string_t` type. If this requirement is not met, the
conversion does not fail; instead, the string is silently converted as an array of character codes,
which is incorrect. See [issue #3425](https://github.com/nlohmann/json/issues/3425) for details
and an example.
conversion depends on the source string type. If it is a range of characters, the string is converted as an array of
character codes, which is incorrect: the converting constructor's [`JSON_ASSERT`](../macros/json_assert.md) fails; with
its default definition, `assert`, the program aborts unless `NDEBUG` is defined, in which case the array is returned.
If the source string type is not a range of characters, the conversion does not compile at all. See
[issue #3425](https://github.com/nlohmann/json/issues/3425) for details and an example.
## Examples
@@ -77,7 +77,7 @@ compile; see [`parse`](parse.md#notes).
- [`parse`](parse.md) - deserialize from a compatible input
- [`BasicJsonType::accept`](../basic_json/accept.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how parsing a document differs from, and matches, basic_json::parse
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how parsing a document differs from, and matches, basic_json::parse
## Version history
@@ -52,7 +52,7 @@ Constant.
- [`parse`](parse.md) - deserialize from a compatible input
- [`is_discarded`](is_discarded.md) - return whether the last parse failed
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
## Version history
@@ -123,7 +123,7 @@ by an erase, not only those at or after the removed element, since the container
- [`root`](root.md) - the view of the root value, the starting point of overload 3
- [`BasicJsonType::erase`](../basic_json/erase.md) - the corresponding function of `basic_json`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
## Version history
@@ -109,16 +109,17 @@ points apply to every edit:
anything `BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
- [`dump()`](../basic_json_view/dump.md) writes an edited document with members in document order, new members at
the end, and, with [`number_format::source`](../basic_json_view/number_format.md), keeps the spelling of every
number that was not itself edited — see [Editing a document](../../features/json_view.md#editing-a-document) for
number that was not itself edited — see [Editing a document](../../features/json_view/editing.md) for
why this matters.
- [`read()`](read.md) discards all edits, [`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once
there are edits, and [`memory_usage()`](memory_usage.md) includes the memory edits use.
- [`read()`](read.md) discards all edits (so its `input` must not be a string obtained from this document),
[`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once there are edits, and
[`memory_usage()`](memory_usage.md) includes the memory edits use.
[`source_offset()`](../basic_json_view/source_offset.md) of a value introduced by an edit is
`#!cpp static_cast<std::size_t>(-1)`, the same value it reports for a decoded string.
## See also
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
- [`basic_json_view`](../basic_json_view/index.md) - the non-owning handle to a value in a document
- [`json_document`](../json_document.md) - and [`json_editable_document`](../json_editable_document.md) - the default specializations
- [`materialize`](../basic_json_view/materialize.md) - build a `basic_json` value from a view
@@ -108,7 +108,7 @@ an edit in general.
- [`set`](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
- [`BasicJsonType::insert`](../basic_json/insert.md) - the corresponding function of `basic_json`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
## Version history
@@ -43,7 +43,7 @@ When the document is discarded, [`root()`](root.md) returns a discarded view (it
- [`parse`](parse.md) - deserialize from a compatible input
- [`basic_json_view::is_discarded`](../basic_json_view/is_discarded.md) - return whether a view is invalid
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how a discarded document arises
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how a discarded document arises
## Version history
@@ -24,7 +24,7 @@ static basic_json_document load(std::vector<std::uint8_t>&& image,
In every overload, the node index is copied into storage the document itself owns — so that it is properly aligned,
and, for an [editable](index.md#edits) document, can be edited — while the text and the decoded strings stay in
`image`. The hash indexes [large objects](../../features/json_view.md) use for lookup are rebuilt, exactly as after
`image`. The hash indexes [large objects](../../features/json_view/index.md) use for lookup are rebuilt, exactly as after
parsing.
## Parameters
@@ -113,11 +113,12 @@ How thoroughly `load()` validates `image` before trusting it.
`full` (the default)
: Checks everything the parser itself guarantees: structure and bounds; that every string is valid UTF-8 (and, for a
string still in the source text, that it contains no quote, backslash, or control character); and that every number
token is well-formed and matches the value stored for it. Strings and float tokens may share a range only if the
ranges are identical (as nodes that share a value do), and never overlap otherwise. Reading and serializing a
checked image is safe and always produces valid JSON, exactly as for a parsed document. This is the right choice for
an image from anything you do not fully control — a file, a cache shared with other processes, a peer on the
network.
token is well-formed and matches the value stored for it, and has the type the parser gives it (a float node only
for a token with a fraction or an exponent, or an integer that does not fit 64 bits). Strings and float tokens may
share a range only if the ranges are identical (as nodes that share a value do), and never overlap otherwise.
Reading and serializing a checked image is safe and always produces valid JSON, exactly as for a parsed document.
This is the right choice for an image from anything you do not fully control — a file, a cache shared with other
processes, a peer on the network.
`bounds`
: Checks structure and bounds only: that every offset and count in the node index stays inside the image. So
@@ -172,7 +173,7 @@ returned document, and any [view](../basic_json_view/index.md) taken from it, is
- [`save`](save.md) - write the document as an image
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [`parse`](parse.md) - deserialize from JSON text instead of an image
- [Images](../../features/json_view.md#images) - why and when to use images
- [Images](../../features/json_view/images.md) - why and when to use images
## Version history
@@ -44,7 +44,7 @@ to observe the effect of [`shrink_to_fit()`](shrink_to_fit.md).
- [`node_count`](node_count.md) - the number of index entries
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
## Version history
@@ -43,7 +43,7 @@ strings and, for an owned document, the text).
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
## Version history
@@ -43,7 +43,7 @@ document (`#!cpp owns_source() == false`) is only valid while the buffer it was
- [`parse`](parse.md) - deserialize from a compatible input
- [`parse_copy`](parse_copy.md) - deserialize a copy of a compatible input, always owned
- [`source`](source.md) - the parsed text
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
## Version history
@@ -112,7 +112,7 @@ told apart from other class-type iterators, so their range is read into an owned
range (e.g. of a `#!cpp std::list<char>`).
See [`owns_source`](owns_source.md) to check which happened after a call, and the
[feature page](../../features/json_view.md) for the reasoning.
[feature page](../../features/json_view/index.md) for the reasoning.
**Numbers.** As for [`BasicJsonType::parse()`](../basic_json/parse.md), an integer literal too large for the 64-bit
integer type becomes a floating-point value.
@@ -74,7 +74,7 @@ compile; see [`parse`](parse.md#notes).
- [`parse`](parse.md) - deserialize from a compatible input, borrowing it where possible
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - borrowing or copying the source text
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - borrowing or copying the source text
## Version history
@@ -96,7 +96,7 @@ Like [`set`](set.md) on a member or an element, `push_back` never moves an exist
- [`root`](root.md) - the view of the root value
- [`BasicJsonType::push_back`](../basic_json/push_back.md) - the corresponding function of `basic_json`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
## Version history
@@ -46,6 +46,34 @@ Linear in the length of the input.
Every view taken from `#!cpp *this` before the call — including the previous [`root()`](root.md) — is invalidated,
whether or not the new parse succeeds; take fresh views from [`root()`](root.md) afterward.
!!! warning "`input` must not point into the document itself"
`read()` releases the document's previous text, its decoded strings, and (for an editable document) its edits
*before* it parses `input`. An `input` that is, or points into, any of them violates a precondition: it may seem to
work, parse empty or garbled text, be reported by a sanitizer, or read freed memory (undefined behavior).
This applies to every `string_view` obtained from the document whose characters live in memory that `read()`
releases:
- [`source()`](source.md) of a document that owns its text;
- [`get_string()`](../basic_json_view/get_string.md) (also as `get<string_view_t>()` or a `string_view` member of a
decoded struct) of a string with escapes, of a string of a document that owns its text, and of an edited string;
- [`number_token()`](../basic_json_view/number_token.md) of a number of a document that owns its text, and of a
number written by an edit;
- the key of an object member (`key()`) if it has escapes, was written by an edit, or the document owns its text.
It does not apply to a text that the caller owns and the document borrows, nor to the strings, keys, and tokens that
are read from such a text unchanged, nor to the decoded strings of a borrowed [image](load.md), which live in the
caller's image: `read()` never touches them. To parse JSON that a document holds in a string of its own (for
example, JSON embedded in a string value), copy the string first, for example with
`#!cpp std::string inner(doc.root()["payload"].get_string()); doc.read(inner);`.
A debug build (one without `NDEBUG`) checks this with `assert()`: an `input` in the document's text, decoded
strings, or image, or in the most recent buffer of strings written by edits, ends the program. (Strings written
by earlier edits are not covered.) A release build does not check, to keep the cost of reusing a document at a
minimum. The check uses `assert()` directly, not [`JSON_ASSERT`](../macros/json_assert.md), which `json.hpp`
undefines at its end.
`input` is borrowed or owned by the same rules as [`parse()`](parse.md#notes); a document can borrow on one call and
own on the next, since ownership is decided freshly each time. An input that is copied (see the table in
[`parse()`](parse.md#notes)) is written into the document's previous owned copy, reusing its memory; a moved-in
@@ -81,7 +109,7 @@ compile; see [`parse`](parse.md#notes).
- [`parse`](parse.md) - deserialize from a compatible input
- [`root`](root.md) - the view of the root value
- [`load`](load.md) - read a document from an image instead of parsing JSON text
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - reusing a document and what it invalidates
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - reusing a document and what it invalidates
## Version history
@@ -40,7 +40,7 @@ auto v = doc.root();
returned view is valid under the same conditions as any other view of the document — see
[Object inspection](../basic_json_view/index.md) — in particular, it is invalidated by the next
[`read()`](read.md) or [`shrink_to_fit()`](shrink_to_fit.md) on this document. A view can dangle, like a
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view.md#ownership-and-lifetime).
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view/lifetime.md).
## Examples
@@ -91,7 +91,7 @@ big-endian reader could not interpret correctly.
- [`load`](load.md) - read an image written by `save()`
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document to JSON text instead of an image
- [Images](../../features/json_view.md#images) - why and when to use images
- [Images](../../features/json_view/images.md) - why and when to use images
## Version history
@@ -195,7 +195,7 @@ one-node scalar.
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document, keeping an untouched number's
spelling with `#!cpp number_format::source`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Editing a document](../../features/json_view.md#editing-a-document) - why editable documents keep the source
- [Editing a document](../../features/json_view/editing.md) - why editable documents keep the source
text's order and number spelling
## Version history
@@ -56,7 +56,7 @@ possible would be an internal optimization only, not a guarantee to rely on.
- [`node_count`](node_count.md) - the number of index entries
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
- [`root`](root.md) - the view of the root value
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - what releasing memory invalidates
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - what releasing memory invalidates
## Version history
@@ -24,6 +24,9 @@ Constant.
For a borrowed document, `source()` points directly into the caller's buffer, so it is only valid while that buffer
is; see [`owns_source`](owns_source.md).
The text of a document that owns it must not be passed to [`read()`](read.md) of the same document: `read()` releases
an owned text before it parses its input. Copy it first. (A borrowed text belongs to the caller and is not affected.)
## Examples
??? example
@@ -42,7 +45,7 @@ is; see [`owns_source`](owns_source.md).
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [`source_offset`](../basic_json_view/source_offset.md) - byte offset of a value in the source text
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
## Version history
+1 -1
View File
@@ -92,7 +92,7 @@ Pointer path of the value within the document, and with
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
the last value.
Unlike [`operator[]`](operator[].md), which returns a [discarded](is_discarded.md) view for a missing key or an
@@ -58,7 +58,7 @@ placeholder for "no value yet" and later be assigned a real view.
- [`is_discarded`](is_discarded.md) - return whether the view is invalid
- [`root`](../basic_json_document/root.md) - the view of a document's root value
- [`view_mode`](../view_mode.md) - whether a document and its views can be edited
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - how long a view stays valid
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - how long a view stays valid
## Version history
@@ -47,7 +47,7 @@ No-throw guarantee: this function never throws exceptions.
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
the last value.
Overload 1 always returns `#!cpp false` when the value is not an object — including a [discarded](is_discarded.md)
+7 -4
View File
@@ -48,8 +48,11 @@ Strong exception safety: if an exception is thrown, there are no changes to the
May throw `#!cpp std::bad_alloc` if allocating the output string fails. Unlike
[`BasicJsonType::dump`](../basic_json/dump.md), there is no `error_handler` parameter and no
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316): the view only ever holds text the parser
already validated as UTF-8, so there is nothing to replace or ignore.
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316): the parser validated the text the view
holds as UTF-8, so there is nothing to replace or ignore. Only a document loaded with
[`image_check::bounds` or `image_check::none`](../basic_json_document/load.md#image_check) can still throw it: these do
not check the strings, so `dump()` writes such a string as it is, which can be invalid UTF-8 or invalid JSON, and
`dump()` with `ensure_ascii` set to `#!cpp true` throws `type_error.316` for invalid UTF-8.
## Complexity
@@ -70,7 +73,7 @@ except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
for `#!cpp -0` — `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
digits than any number type holds (such a literal is itself classified as a float, see
[What is different](../../features/json_view.md#what-is-different)) — something `BasicJsonType` cannot do, since
[What is different](../../features/json_view/differences.md)) — something `BasicJsonType` cannot do, since
parsing already reduces every number to its parsed value.
## Examples
@@ -98,7 +101,7 @@ parsing already reduces every number to its parsed value.
- [`operator<<`](operator_ltlt.md) - serialize this value to a stream
- [`materialize`](materialize.md) - build a `BasicJsonType` value, e.g. to use `BasicJsonType::dump`'s `error_handler`
- [`BasicJsonType::dump`](../basic_json/dump.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
## Version history
+1 -1
View File
@@ -37,7 +37,7 @@ constant time on average.
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
the last value.
Unlike [`BasicJsonType::find`](../basic_json/find.md), which always returns `#!cpp end()` for a non-object type, this
+3 -3
View File
@@ -39,14 +39,14 @@ as [`BasicJsonType::parse()`](../basic_json/parse.md) does), as well as the type
accept views, e.g. with [`is_json_view`](../is_json_view.md). A `from_json()` that also accepts types that are no JSON
values at all, such as the unconstrained `#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType&,
T&)` of [arbitrary type conversions](../../features/arbitrary_types.md), is not called with a view: it may use
`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view.md#writing-generic-code).
`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view/generic_code.md).
!!! warning "Duplicate keys"
Such a `from_json()` looks members up in the view, where a repeated key finds the **first** member, while
`#!cpp materialize().get<T>()` sees the **last** one, as [`BasicJsonType::parse()`](../basic_json/parse.md)
keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get<point>()` reads `#!cpp x == 1`. See
[Duplicate keys](../../features/json_view.md#duplicate-keys).
[Duplicate keys](../../features/json_view/differences.md#duplicate-keys).
Every other `T` — user types whose `from_json()` is written for `BasicJsonType` only, types without a default
constructor, `#!cpp std::tuple`s holding references, ... — is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
@@ -152,7 +152,7 @@ instead: its path is relative to the root of the view's subtree, because `materi
- [`number_token`](number_token.md) - a number's token text, without a copy
- [`materialize`](materialize.md) - build the `BasicJsonType` value of this subtree
- [`BasicJsonType::get`](../basic_json/get.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
## Version history
@@ -46,6 +46,10 @@ The returned [`string_view_t`](index.md#member-types) is valid exactly as long a
source text, which for a borrowed document must remain valid **and unmodified** for as long as the document exists,
since floating-point values and unescaped strings are read from the source lazily.
A string with escapes, a string of a document that owns its text, and an edited string must not be passed to
[`read()`](../basic_json_document/read.md) of the document it came from: `read()` releases them before it parses its
input. Copy the string first. (A string read unchanged from a borrowed text is not affected.)
## Examples
??? example
@@ -70,7 +74,7 @@ since floating-point values and unescaped strings are read from the source lazil
- [`get`](get.md) - convert the value to a given type (`#!cpp get<string_view_t>()` is equivalent to this function)
- [`number_token`](number_token.md) - a number's token text, without a copy
- [`BasicJsonType::get_ref`](../basic_json/get_ref.md) - the closest counterpart of `basic_json`
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - borrowing strings instead of copying them
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - borrowing strings instead of copying them
## Version history
@@ -75,7 +75,7 @@ them with those of a default-constructed `T`.
- [`get`](get.md) - convert the value to a given type
- [`BasicJsonType::get_to`](../basic_json/get_to.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
## Version history
@@ -147,7 +147,7 @@ still refers to it — including ones taken before the change — reads the new
## See also
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
- [`basic_json_document`](../basic_json_document/index.md) - the document that owns the parsed data
- [`json_view`](../json_view.md) - and [`json_editable_view`](../json_editable_view.md) - the default specializations
- [`materialize`](materialize.md) - build a `basic_json` value from a view
@@ -54,7 +54,7 @@ A `basic_json_view` is not convertible to `#!cpp bool`: such a conversion would
- [`(constructor)`](basic_json_view.md) - the default constructor creates a discarded view
- [`basic_json_document::is_discarded`](../basic_json_document/is_discarded.md) - return whether the last parse failed
- [`BasicJsonType::is_discarded`](../basic_json/is_discarded.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view.md#what-is-different) - how views differ from basic_json
- [Zero-copy JSON views](../../features/json_view/differences.md) - how views differ from basic_json
## Version history
@@ -78,7 +78,7 @@ never caches the result.
- [`root`](../basic_json_document/root.md) - the view of a document's root value
- [`BasicJsonType::parse`](../basic_json/parse.md) - build a `BasicJsonType` value directly from a JSON text
- [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) - source positions on parsed values
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - when to build a basic_json value from a view
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - when to build a basic_json value from a view
## Version history
@@ -45,7 +45,7 @@ source
- [`number_token`](number_token.md) - get a single number's token text without dumping the whole value
- [`BasicJsonType::error_handler_t`](../basic_json/error_handler_t.md) - the analogous enumeration for
`BasicJsonType::dump`'s decoding-error behavior
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing numbers as they appeared in the source
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing numbers as they appeared in the source
## Version history
@@ -38,9 +38,14 @@ by the grammar; trailing zeros in the fraction; the case and sign of the exponen
useful precisely where that form must survive — a price or an identifier that must be reproduced exactly, or a
number too large for any of `BasicJsonType`'s number types to hold without loss.
The returned [`string_view_t`](index.md#member-types) always points into the document's
[`source()`](../basic_json_document/source.md) text — numbers are never decoded into the document's separate string
buffer — and is valid exactly as long as that text is, see the [validity rules](index.md) of `basic_json_view`.
The returned [`string_view_t`](index.md#member-types) points into the document's
[`source()`](../basic_json_document/source.md) text or, for a number written by an
[edit](../basic_json_document/index.md#edits), into storage owned by the document (the token of an edited `NaN` is
`nan`). It is valid exactly as long as that text or storage is, see the [validity rules](index.md) of
`basic_json_view`. Numbers are never decoded into the document's separate string buffer.
For the same reason, the token must not be passed to [`read()`](../basic_json_document/read.md) of the document it came
from if that document owns its text or the number was written by an edit: `read()` releases both before it parses.
## Examples
@@ -68,7 +73,7 @@ buffer — and is valid exactly as long as that text is, see the [validity rules
- [`BasicJsonType::number_integer_t`](../basic_json/number_integer_t.md),
[`number_unsigned_t`](../basic_json/number_unsigned_t.md), [`number_float_t`](../basic_json/number_float_t.md) - the
number types `#!cpp get<T>()` and `materialize()` convert into
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - reading a number exactly as written
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - reading a number exactly as written
## Version history
@@ -120,7 +120,7 @@ document.
[`materialize()`](materialize.md) or iterate the members with [`items()`](items.md) and keep the last match.
The key hash index of a larger object (128 members or more) leads to the first member of a key as well.
[`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
duplicates, in document order. See [Duplicate keys](../../features/json_view.md#duplicate-keys) and
duplicates, in document order. See [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) and
[`size()`](size.md#notes).
!!! info "JSON pointer resolution"
@@ -19,7 +19,7 @@ Neither overload builds a `BasicJsonType` value for a view to do the comparison
compare by value across their types (`#!cpp 1 == 1.0`), and an object compares by its members, with duplicate keys
resolved exactly as `parse()` resolves them — the last value, at the position of the first occurrence of the key.
This is not what [`operator[]`](operator[].md) and the other lookups return for a duplicate key: they find the *first*
member (see [Duplicate keys](../../features/json_view.md#duplicate-keys)).
member (see [Duplicate keys](../../features/json_view/differences.md#duplicate-keys)).
## Parameters
@@ -57,7 +57,9 @@ compared. Two arrays of different [`size()`](size.md) are rejected without visit
Only a single number, boolean, or `#!cpp null` value is ever materialized into a `BasicJsonType`, to reuse its
`operator==` — for numbers, so that values written differently in the source text but equal in value (e.g. an
integer and a floating-point literal) still compare equal, following the same rules `BasicJsonType` does for special
values such as `#!cpp NaN`. Constructing one of these scalars never allocates. Strings are compared directly, without
values such as `#!cpp NaN`: a `#!cpp NaN` that an [edit](../basic_json_document/set.md) wrote is not equal to itself, so
a view of it is not equal to itself either, nor is a view of an array or object that contains one, as for
`BasicJsonType`. Constructing one of these scalars never allocates. Strings are compared directly, without
allocating, either from the source text on both sides or, for overload 2, against `BasicJsonType`'s own string.
Arrays and objects are never materialized at all; only their elements or members are visited, one pair at a time.
@@ -68,7 +68,7 @@ Linear, as [`dump`](dump.md#complexity).
- [`dump`](dump.md) - serialize to a JSON-formatted string
- [`operator<<(std::ostream&)`](../operator_ltlt.md) - the corresponding operator for `basic_json`
- [`JSON_NO_IO`](../macros/json_no_io.md) - switch off functions relying on certain C++ I/O headers
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
## Version history
@@ -49,7 +49,7 @@ string, whose token length is not the same as its decoded [`size()`](size.md).
- [`source`](../basic_json_document/source.md) - the parsed text
- [`is_string`](is_string.md) - return whether the value is a string
- [Zero-copy JSON views](../../features/json_view.md#images) - images and the source text
- [Zero-copy JSON views](../../features/json_view/images.md) - images and the source text
## Version history
@@ -84,7 +84,7 @@ Pointer path of the value within the document, and with
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
the last value.
!!! info "Differences to `at` and `operator[]`"
+1 -1
View File
@@ -49,7 +49,7 @@ view class.
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
- [get](basic_json_view/get.md) - which conversions read the view directly
- [view_mode](view_mode.md) - read-only or editable
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions and `from_json()` for views
- [Writing generic code](../features/json_view/generic_code.md) - functions and `from_json()` for views
## Version history
+1 -1
View File
@@ -29,7 +29,7 @@ specialization.
- [`json_view`](json_view.md) - a view of a value of a `json_document`
- [`ordered_json_document`](ordered_json_document.md) - the corresponding document for `ordered_json`
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
## Version history
@@ -10,7 +10,7 @@ This type is an **editable** [`basic_json_document`](basic_json_document/index.m
[`json`](json.md) specialization: in addition to everything [`json_document`](json_document.md) offers,
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) change values after
parsing, without ever rewriting the source text — see [Edits](basic_json_document/index.md#edits) and
[Editing a document](../features/json_view.md#editing-a-document).
[Editing a document](../features/json_view/editing.md).
## Examples
+1 -1
View File
@@ -35,7 +35,7 @@ still referring to it seeing the change, see [Edits](basic_json_document/index.m
- [`json_view`](json_view.md) - the corresponding read-only view
- [`ordered_json_editable_view`](ordered_json_editable_view.md) - the corresponding view for
`ordered_json_editable_document`
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
## Version history
+1 -2
View File
@@ -7,8 +7,7 @@ struct json_sax;
This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in
different situations while the input is parsed. The boolean return value informs the parser whether to continue
processing the input; for [`parse_error`](parse_error.md), it decides whether to
[recover from the error](../../features/parsing/error_recovery.md).
processing the input.
For instance, parsing the JSON text `{"a": [1, true]}` triggers the following callbacks, in order:
+2 -26
View File
@@ -21,18 +21,11 @@ A parse error occurred.
## Return value
Whether to recover from the error:
- `#!cpp false` stops parsing.
- `#!cpp true` recovers from the error: the error is repaired and parsing continues. If that is not possible, which
happens in the binary formats when the end of the item with the error is unknown, the value read so far is completed
and parsing stops. See [error recovery](../../features/parsing/error_recovery.md) for how errors are repaired.
Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
Whether parsing should proceed (**must return `#!cpp false`**).
## Examples
??? example "Example: (1) the SAX interface"
??? example
The example below shows how the SAX interface is used.
@@ -46,30 +39,13 @@ Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
--8<-- "examples/sax_parse.output"
```
??? example "Example: (2) recovering from errors"
The example below shows how a SAX parser recovers from errors.
```cpp
--8<-- "examples/sax_parse__error_recovery.cpp"
```
Output:
```
--8<-- "examples/sax_parse__error_recovery.output"
```
## See also
- [`sax_parse`](../basic_json/sax_parse.md) - SAX parser
- [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without
exceptions
- [Error Recovery](../../features/parsing/error_recovery.md) - the article on recovering from parse errors
- [SAX Interface](../../features/parsing/sax_interface.md) - guide to event-based parsing
## Version history
- Added in version 3.2.0.
- Returning `#!cpp true` recovers from the error since version 3.13.0; before, parsing stopped, but the result of
[`sax_parse`](../basic_json/sax_parse.md) could be wrong.
+1 -1
View File
@@ -28,7 +28,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`j
- [`json_document`](json_document.md) - the document type this view refers into
- [`ordered_json_view`](ordered_json_view.md) - the corresponding view for `ordered_json_document`
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
## Version history
+2
View File
@@ -40,6 +40,8 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
- [**JSON_NO_SIMD**](json_no_simd.md) - use only portable code instead of vector instructions (SSE2, SSSE3, NEON)
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
- [**JSON_RECURSION_DEPTH_LIMIT**](json_recursion_depth_limit.md) - set how many nesting levels the operations on a value
recurse into on the call stack (default 128)
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
- [**JSON_USE_GLOBAL_UDLS**](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
@@ -27,9 +27,13 @@ The default value is `0` (disabled — existing behavior is preserved).
json j{obj};
```
creates a single-element **array** `[{"key":"value"}]` instead of a copy of `obj`. This behavior is
compiler-dependent for older compilers (GCC wrapped, Clang did not), but starting from Clang 20, both compilers
behave the same way.
creates a single-element **array** `[{"key":"value"}]` instead of a copy of `obj`. For this constructor, the
behavior was compiler-dependent for older compilers (GCC wrapped, Clang did not), but starting from Clang 20, both
compilers behave the same way.
The calls `#!cpp arr.push_back({obj})` and `#!cpp arr += {obj}` are still compiler-dependent without the macro,
even with Clang 20 and later: GCC converts `{obj}` to a single-element array and appends that array, whereas Clang
appends a copy of `obj`. With the macro defined to `1`, both compilers append a copy of `obj`.
Enabling this macro opts into copy/move semantics for this case
(see [#5074](https://github.com/nlohmann/json/issues/5074)).
@@ -34,6 +34,9 @@ or quotes.
Note that enabling this macro increases the size of every JSON value by two `std::size_t` fields and adds slight runtime
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
these values to be reported in those error messages.
In addition, `parse()`, `accept()`, and `sax_parse()` no longer use the
[faster path for input in one block of memory](../../features/parsing/index.md#input-sources-and-performance), so
parsing a string or a buffer is slower than without the macro.
If [`JSON_DIAGNOSTICS`](json_diagnostics.md) is also set, the message contains the path and the byte range, such as
`(/1) (bytes 4-8)`. An exception about the root value has no path and only the range, such as `(bytes 0-4)`.
@@ -122,3 +125,5 @@ When the macro is not defined, the library will define it to its default value.
## Version history
- Added in version 3.12.0.
- Fixed in version 3.13.0: in version 3.12.0, [`start_pos()`](../basic_json/start_pos.md) of a string that contains
escape sequences was too large; it is now the position of the opening `"`.
@@ -48,8 +48,8 @@ The default value is `0`: streams are scanned in bulk.
!!! note "Performance"
Parsing from a `std::ifstream` or `std::istringstream` is typically 1.5 to 2 times as fast as before, and reading
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream is then within
about 10 to 20 % of first reading the whole file into a `std::string` and parsing that.
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream then takes about
1.15 to 1.45 times as long as first reading the whole file into a `std::string` and parsing that.
A stream buffer without a get area (one that only overrides `underflow()` and `uflow()` and hands out every
character from there) offers nothing to scan in bulk, so it is read one character at a time as before.
+1 -1
View File
@@ -80,7 +80,7 @@ By default, `#!cpp JSON_NO_SIMD` is not defined, and vector instructions are use
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance) - how input
sources affect parsing performance
- [Serialization](../../features/serialization.md) - pretty-printing, UTF-8 handling, and number output
- [Zero-copy JSON views](../../features/json_view.md) - the zero-copy view, whose parser the macro also affects
- [Zero-copy JSON views](../../features/json_view/index.md) - the zero-copy view, whose parser the macro also affects
## Version history
@@ -45,6 +45,7 @@ Copying and comparing fall back to working without the call stack there, as they
## See also
- [`JSON_RECURSION_DEPTH_LIMIT`](json_recursion_depth_limit.md) - set how many nesting levels the operations recurse into
- [FAQ: Thread safety](../../home/faq.md#thread-safety) - thread-safety guarantees of the library
- [Supported Macros](../../features/macros.md) - guide to all supported macros
@@ -0,0 +1,307 @@
# JSON_RECURSION_DEPTH_LIMIT
```cpp
#define JSON_RECURSION_DEPTH_LIMIT /* value */
```
This macro sets how many nesting levels the operations that walk a value recurse into on the call stack. Beyond that,
they continue with an explicit stack on the heap. The value is an integer constant from `0` to `254`; the default is
`128`.
The operations are copying a value, comparing two values, serializing with [`dump`](../basic_json/dump.md) or
[`operator<<`](../operator_ltlt.md), hashing with `std::hash`, [`merge_patch`](../basic_json/merge_patch.md),
[`update`](../basic_json/update.md) with `merge_objects`, [`diff`](../basic_json/diff.md),
[`merge_diff`](../basic_json/merge_diff.md), converting a value to another specialization of `basic_json`, destroying a
value, and the binary writers [`to_cbor`](../basic_json/to_cbor.md),
[`to_msgpack`](../basic_json/to_msgpack.md), [`to_ubjson`](../basic_json/to_ubjson.md),
[`to_bjdata`](../basic_json/to_bjdata.md), and [`to_bon8`](../basic_json/to_bon8.md). They share this one limit; none
has
a limit of its own. Recursing is the fastest way to walk a value, but a value nested deeply enough would exhaust the
call
stack, so the library recurses only as far as the limit and finishes the levels below with an explicit stack. This is
what makes all of them safe for untrusted input
(see [Nesting depth](../../features/parsing/untrusted_input.md#nesting-depth)). Parsing, the binary readers,
[`flatten`](../basic_json/flatten.md), [`unflatten`](../basic_json/unflatten.md), and
[`to_bson`](../basic_json/to_bson.md) always use an explicit stack and do not depend on the macro. The lockstep
comparison of two [`json_view`](../basic_json_view/index.md) documents has a fixed bound of its own, 48 levels,
and does not depend on it either.
The call stack that these operations need grows with the limit: roughly *a few kilobytes plus a cost per level times the
limit*, where the cost per level is up to about 2.3 KiB in an unoptimized and up to about 0.5 KiB in an optimized build
(see [Stack needed](#stack-needed)). The default of `128` levels is tuned for threads with ordinary stacks of a few
hundred kilobytes or more. Set a lower limit if your threads have small stacks (RTOS tasks, fibers, threads of the musl
C
library, which has a default stack of 128 KiB), or a higher limit to trade stack for speed on deeply nested values.
The macro changes only how much stack is used and how fast the operations are. It never changes a result: every
operation returns the same value for every setting of the limit, for values nested less deeply than the limit as well as
for values nested more deeply.
## Default definition
The default value is `128`.
```cpp
#define JSON_RECURSION_DEPTH_LIMIT 128
```
When the macro is not defined, the library defines it to its default value.
## Notes
!!! note "Allowed values"
The value must be an integer constant expression from `0` to `254`; any other value (a negative number, a number
above `254`, a floating-point number such as `254.0`, or `true` or `false`) is rejected at compile time with an
error message that names the macro. A character of any character type (`'a'`, `L'a'`, `u8'a'`, ...) is rejected,
too, although it is an integer.
- `0` is allowed, and means that every operation uses its explicit stack from the first level on and no operation
recurses over the nesting of a value at all. That needs a constant amount of call stack (at most 16 KiB in the
measurements below, for a value nested 20,000 levels deep) and is the setting for tiny stacks, at the price of
speed (see [Speed](#speed)).
- `254` is the largest allowed value. Copying a value and comparing two values (with `<=>`, or with the operators on
objects and arrays of a custom type) cannot pass the depth down as an argument, because the signatures of the
copy constructor and the operators are fixed. They count the levels they have descended into in a variable of type
`#!cpp std::uint8_t` that exists once per thread (see [`JSON_NO_THREAD_LOCAL`](json_no_thread_local.md)). Only
the levels that are descended into are counted, so the count never exceeds the limit, not even when user code (a
copy constructor of a custom base class, say) copies a value while a copy is going on. A byte holds `255` at
most, and the limit stays below that, so that the count cannot wrap around. (More levels would hardly be
useful: `254` levels need up to 576 KiB of stack in an unoptimized build already, twice as much as the
default.)
### Stack needed
The tables show the smallest stack that an operation needs, in KiB, at several limits, on a value that is nested
more deeply than the limit (limit + 10 levels). Each cell shows the result for Clang (Apple clang 21) and for
GCC 16, separated by a slash; both measured on macOS with arm64, compiling in C++20 mode. The operations run in a
thread whose stack has the size given, and are not given any stack that they do not need: the value is built, and
the result destroyed, outside of that thread. Stacks can only be set in steps of 16 KiB (the page size), so `16`
means "16 KiB or less". A row stands for all operations listed; the table shows the largest value among them.
Unoptimized (`-O0`):
| Operation | limit 16 | limit 32 | limit 64 | limit 128 | limit 254 |
|---|---:|---:|---:|---:|---:|
| `dump()` and `dump(indent)`, `operator<<` | 48 / 32 | 80 / 64 | 144 / 128 | 288 / 256 | 576 / 512 |
| copy construction and assignment | 32 / 16 | 48 / 32 | 112 / 80 | 224 / 144 | 432 / 304 |
| `==` | 16 / 16 | 16 / 16 | 16 / 16 | 48 / 32 | 96 / 48 |
| `<=>` (C++20) | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 64 | 144 / 128 |
| `std::hash` | 16 / 16 | 16 / 16 | 32 / 32 | 80 / 64 | 160 / 128 |
| `merge_patch`, `update(j, true)`, `patch` | 32 / 16 | 48 / 32 | 112 / 80 | 224 / 144 | 432 / 304 |
| `diff` | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 48 | 144 / 96 |
| `merge_diff` | 16 / 16 | 48 / 32 | 80 / 48 | 176 / 112 | 352 / 224 |
| `to_cbor`, `to_msgpack` | 16 / 16 | 32 / 16 | 64 / 32 | 112 / 80 | 224 / 160 |
| `to_ubjson`, `to_bjdata` | 16 / 16 | 16 / 16 | 48 / 32 | 96 / 64 | 176 / 112 |
| `to_bon8` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 16 | 64 / 48 |
| conversion to another `basic_json` type | 32 / 16 | 64 / 32 | 112 / 80 | 224 / 144 | 448 / 304 |
| destruction | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 |
Optimized (`-O2`):
| Operation | limit 16 | limit 32 | limit 64 | limit 128 | limit 254 |
|---|---:|---:|---:|---:|---:|
| `dump()` and `dump(indent)`, `operator<<` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 |
| copy construction and assignment | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 48 |
| `==` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 |
| `<=>` (C++20) | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 48 / 16 |
| `std::hash` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 48 | 80 / 80 |
| `merge_patch`, `update(j, true)`, `patch` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 48 |
| `diff` | 16 / 16 | 16 / 16 | 16 / 32 | 48 / 48 | 96 / 112 |
| `merge_diff` | 16 / 16 | 16 / 16 | 16 / 16 | 48 / 32 | 96 / 64 |
| `to_cbor`, `to_msgpack` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 |
| `to_ubjson`, `to_bjdata` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 64 |
| `to_bon8` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 32 | 32 / 64 |
| conversion to another `basic_json` type | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 48 | 64 / 96 |
| destruction | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 |
With a limit of `0`, every operation in the tables ran in a 16 KiB stack at `-O0` (Clang and GCC), on a value nested
20,000 levels deep. The operations that do not depend on the macro (`flatten`, `unflatten`, `to_bson`) needed
16 KiB or less at every limit.
The rule of thumb is
> stack needed ≈ a few KiB + cost per level × limit
with a cost per level of up to 2.3 KiB (`dump` at `-O0` with Clang; 2.1 KiB with GCC), 1.7 KiB for copying and merging
(1.3 KiB with GCC), and 0.1 to 0.5 KiB for most other operations at `-O0`; and 0.5 KiB or less at `-O2`. The numbers
come from the difference between the limits `128` and `254` in the tables, so they are rough: other compilers,
other targets (the frames of x86-64 code are about as large), other standard libraries, `JSON_DIAGNOSTICS`, and
sanitizers change them. Measure with your own tool chain (see the example below) rather than relying on the tables.
The stack that your own code and the operating system need comes on top of these numbers.
Which limit to choose, for the stack that you can spare for the library (the largest limit of those measured
for which the slowest operation needs at most that much):
| Stack for the library | optimized (`-O2`) | unoptimized (`-O0`) |
|---|---:|---:|
| 32 KiB | 64 | 0 |
| 64 KiB | 128 | 16 |
| 128 KiB (the default stack of threads on musl) | 254 | 32 |
| 256 KiB | 254 | 64 |
| 512 KiB | 254 | 128 |
| 1 MiB or more | 254 | 254 |
### Speed
Operations are faster the more of a value they handle with plain recursion. The levels below the limit are handled with
an explicit stack, which has to be kept on the heap and costs more per level. The macro therefore trades speed against
stack.
The table shows the time of copying a value (including destroying the copy), of `==` on two equal values, of `dump`, and
of `std::hash` for several limits, relative to the default of `128` (`1.00` means the same time; more is slower). It was
measured with Clang 21 at `-O2` in C++17 mode on macOS with arm64, as the best of seven runs of each setting (two
rounds, best of both). The documents are ordinary JSON files that are nested 7 (`canada.json`), 8
(`citm_catalog.json`), 10 (`twitter.json`), and 2 levels deep (`jeopardy.json`); the other values are one array or object
with one member per level, nested 1,000 and 100,000 levels deep. The absolute time at the limit `128` is given in the
third column. Differences of up to about 0.1 are noise: a different limit changes the layout of the compiled code, and
timings of the same binary vary by about as much. The slow columns (limits 0 and 1) vary more: repeating `std::hash` on
`canada.json` gave 1.27 to 1.30 for limit 0 and 1.4 to 1.9 for limit 1, so read the cells of those two columns as
accurate to about 0.3.
| Value | Operation | limit 128 (absolute) | limit 0 | limit 1 | limit 16 | limit 254 |
|---|---|---:|---:|---:|---:|---:|
| `canada.json` (2.1 MB) | copy | 3.5 ms | 1.80 | 1.80 | 0.99 | 1.00 |
| `canada.json` (2.1 MB) | `==` | 350.5 µs | 2.36 | 2.38 | 1.00 | 0.99 |
| `canada.json` (2.1 MB) | `dump` | 2.4 ms | 1.11 | 1.11 | 1.00 | 1.00 |
| `canada.json` (2.1 MB) | `std::hash` | 920.2 µs | 1.33 | 1.59 | 1.00 | 1.00 |
| `citm_catalog.json` (1.7 MB) | copy | 1.8 ms | 1.38 | 1.36 | 1.00 | 0.99 |
| `citm_catalog.json` (1.7 MB) | `==` | 275.5 µs | 1.31 | 1.31 | 0.99 | 1.00 |
| `citm_catalog.json` (1.7 MB) | `dump` | 314.6 µs | 1.24 | 1.23 | 1.00 | 1.00 |
| `citm_catalog.json` (1.7 MB) | `std::hash` | 309.1 µs | 1.77 | 1.42 | 1.06 | 1.05 |
| `twitter.json` (0.6 MB) | copy | 865.7 µs | 1.45 | 1.47 | 1.01 | 1.00 |
| `twitter.json` (0.6 MB) | `==` | 135.8 µs | 1.26 | 1.23 | 1.00 | 1.00 |
| `twitter.json` (0.6 MB) | `dump` | 224.6 µs | 1.12 | 1.12 | 1.01 | 1.00 |
| `twitter.json` (0.6 MB) | `std::hash` | 132.9 µs | 1.28 | 1.27 | 1.03 | 1.04 |
| `jeopardy.json` (53 MB) | copy | 111.1 ms | 1.22 | 1.25 | 1.00 | 1.00 |
| `jeopardy.json` (53 MB) | `==` | 23.5 ms | 1.29 | 1.53 | 1.07 | 1.00 |
| `jeopardy.json` (53 MB) | `dump` | 33.2 ms | 1.14 | 1.34 | 1.03 | 1.01 |
| `jeopardy.json` (53 MB) | `std::hash` | 19.9 ms | 1.17 | 1.52 | 1.05 | 1.00 |
| arrays, 1,000 levels | copy | 69.9 µs | 0.95 | 0.98 | 0.92 | 0.99 |
| arrays, 1,000 levels | `==` | 10.4 µs | 1.02 | 1.01 | 0.96 | 1.00 |
| arrays, 1,000 levels | `dump` | 9.6 µs | 0.97 | 0.96 | 0.97 | 1.06 |
| arrays, 1,000 levels | `std::hash` | 16.9 µs | 1.00 | 1.01 | 1.01 | 0.98 |
| objects, 1,000 levels | copy | 77.1 µs | 0.98 | 0.97 | 0.97 | 1.03 |
| objects, 1,000 levels | `==` | 12.0 µs | 0.96 | 0.95 | 0.95 | 1.07 |
| objects, 1,000 levels | `dump` | 12.6 µs | 0.97 | 0.95 | 0.98 | 1.01 |
| objects, 1,000 levels | `std::hash` | 17.3 µs | 0.99 | 1.01 | 0.98 | 1.00 |
| arrays, 100,000 levels | copy | 6.4 ms | 1.00 | 1.00 | 1.00 | 1.00 |
| arrays, 100,000 levels | `==` | 1.1 ms | 1.01 | 1.03 | 1.00 | 1.06 |
| arrays, 100,000 levels | `dump` | 917.9 µs | 1.01 | 1.01 | 1.02 | 1.01 |
| arrays, 100,000 levels | `std::hash` | 1.7 ms | 1.04 | 1.05 | 0.99 | 0.99 |
| objects, 100,000 levels | copy | 7.5 ms | 0.99 | 1.02 | 1.01 | 0.99 |
| objects, 100,000 levels | `==` | 1.3 ms | 1.02 | 1.09 | 1.08 | 0.96 |
| objects, 100,000 levels | `dump` | 1.2 ms | 1.06 | 0.99 | 1.01 | 0.99 |
| objects, 100,000 levels | `std::hash` | 1.8 ms | 1.00 | 1.00 | 1.00 | 1.00 |
The results are as follows.
- A limit of `16` or more does not change the speed of ordinary documents (differences are within noise), because they
are not nested more deeply than that anyway. A document nested more deeply than the limit is walked partly with the
explicit stack.
- A limit of `0` or `1` makes the operations on ordinary documents 1.1 to 2.4 times as slow, because even their first
levels are handled with the explicit stack. The slowdown is largest for `==` on a document of many small containers
(`canada.json`: 2.4 times) and for copying (up to 1.8 times).
- For values that are nested far more deeply than any limit (1,000 and 100,000 levels deep), the limit makes no
measurable difference: almost all of the levels are handled with the explicit stack for every setting.
Raising the limit gains nothing for them, and lowering it costs nothing.
!!! note "Other macros"
- With [`JSON_NO_THREAD_LOCAL`](json_no_thread_local.md) defined (or for Clang targeting MinGW, where the library
defines it by itself), copying and comparing never descend, so for them the limit does not matter. The other
operations still recurse as many levels as the limit says. The range of the limit is the same.
- The macro is not part of the [ABI tag](../../features/namespace.md), as `JSON_NO_THREAD_LOCAL` is not, and it is
not undefined at the end of `json.hpp`.
!!! warning "Use the same value in every translation unit"
All translation units of a program that include the library must define the same value. Otherwise, the program
violates the One Definition Rule: the function `nlohmann::detail::recursion_depth_limit()` and all the functions
that use it are defined differently in different translation units. Which limit a call uses then depends on which
functions the compiler inlined (an inlined copy uses the value of its own translation unit) and which out-of-line
copy of each remaining function the linker kept, which is not specified. A function compiled with one value can
call a function compiled with another, so a single operation can mix both limits.
Mixing values cannot corrupt the byte per thread that counts the levels of copying and comparing: each limit
is at most `254`, and all of them stop counting at their own limit, so the count never exceeds `254`, whichever
translation unit's definition runs. The counter exists once per thread and `basic_json` specialization, and is
shared by all translation units. Nor can mixing values change any result, as no result depends on the limit. What
changes is how much stack is used and how fast the operations are. Nevertheless, avoid it: the program has undefined
behavior as far as the C++ standard is concerned.
## Examples
??? example "Example: lower the limit"
The example nests an array 5,000 levels deep. With the limit set to 16, the library recurses over the first 16
levels
and over the rest uses an explicit stack. The results are those of the default limit.
```cpp
--8<-- "examples/json_recursion_depth_limit.cpp"
```
Output:
```
--8<-- "examples/json_recursion_depth_limit.output"
```
??? example "Example: find out how much stack an operation needs"
The program runs `dump()` on a value nested `depth` levels deep in a thread with a stack of `kib` KiB. Compile it
with the options and the limit that you use, e.g. `-DJSON_RECURSION_DEPTH_LIMIT=64`, run it with `depth` set to the
limit plus 10 and decreasing values of `kib` (the stack size must be a multiple of the page size, often 4 or 16
KiB),
and note the smallest stack with which it still finishes. That is how the tables above were measured.
```cpp
#include <cstdlib>
#include <iostream>
#include <string>
#include <utility>
#include <pthread.h>
#include <nlohmann/json.hpp>
static nlohmann::json value;
static void* run(void*)
{
std::cout << "dump() ok, " << value.dump().size() << " bytes\n";
return nullptr;
}
int main(int argc, char** argv)
{
const int depth = std::atoi(argv[1]);
const std::size_t kib = static_cast<std::size_t>(std::atoi(argv[2]));
// built on the main thread, without recursion
value = 1;
for (int i = 0; i < depth; ++i)
{
nlohmann::json outer = nlohmann::json::array();
outer.push_back(std::move(value));
value = std::move(outer);
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, kib * 1024);
pthread_t thread;
pthread_create(&thread, &attr, run, nullptr);
pthread_join(thread, nullptr);
}
```
## See also
- [Nesting depth](../../features/parsing/untrusted_input.md#nesting-depth) - how the library handles deeply nested
values
- [`JSON_NO_THREAD_LOCAL`](json_no_thread_local.md) - switch off the use of `thread_local` storage
- [Supported Macros](../../features/macros.md) - guide to all supported macros
## Version history
- Added in version 3.13.0.
@@ -93,7 +93,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i
## See also
- [Binary formats](../../features/binary_formats/index.md) - overview of the supported binary formats
- [`from_cbor`](../basic_json/from_cbor.md) - also affected when parsing CBOR strings
- [`from_cbor`](../basic_json/from_cbor.md) - not affected by the macro; pass `error_handler` to validate CBOR strings
- [`to_cbor`](../basic_json/to_cbor.md) - create a CBOR serialization of a JSON value
- [`to_ubjson`](../basic_json/to_ubjson.md) - create a UBJSON serialization of a JSON value
- [`to_bjdata`](../basic_json/to_bjdata.md) - create a BJData serialization of a JSON value
@@ -32,7 +32,7 @@ of object keys, instead of sorting them like [`json_document`](json_document.md)
- [`ordered_json_view`](ordered_json_view.md) - a view of a value of an `ordered_json_document`
- [`json_document`](json_document.md) - the corresponding document for the default `json` specialization
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
## Version history
@@ -41,7 +41,7 @@ members [`set`](basic_json_document/set.md) added — instead of sorting them li
specialization
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
## Version history
@@ -34,7 +34,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
- [`ordered_json_editable_document`](ordered_json_editable_document.md) - the document type this view refers into
- [`ordered_json_view`](ordered_json_view.md) - the corresponding read-only view
- [`json_editable_view`](json_editable_view.md) - the corresponding view for `json_editable_document`
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
## Version history
+1 -1
View File
@@ -29,7 +29,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
- [`ordered_json_document`](ordered_json_document.md) - the document type this view refers into
- [`json_view`](json_view.md) - the corresponding view for `json_document`
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
## Version history
+1 -1
View File
@@ -82,7 +82,7 @@ nor a [`json_editable_view`](json_editable_view.md). Views of the two modes comp
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
- [json_editable_document](json_editable_document.md) - an editable document of `json`
- [(constructor)](basic_json_view/basic_json_view.md) - the conversion from a read-only to an editable view
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions that accept views
- [Writing generic code](../features/json_view/generic_code.md) - functions that accept views
## Version history
+5 -2
View File
@@ -53,12 +53,15 @@ flowchart LR
only as bytes are actually read, arrays reserve at most a fixed number of elements up front, and sizes that no
container can hold are rejected.
- **Memory is owned by values.** Each `basic_json` value owns its content, and there is no manual memory management in
user code. The destructor does not recurse, so destroying a deeply nested value does not exhaust the stack.
user code. The destructor recurses only over a bounded number of nesting levels and then continues without the call
stack, so destroying a deeply nested value does not exhaust the stack.
- **Bounded recursion.** The JSON parser and the binary readers keep their state in explicit stacks instead of
recursing per nesting level. Operations that walk a value, such as [`dump`](../api/basic_json/dump.md), copying,
comparison, hashing, [`merge_patch`](../api/basic_json/merge_patch.md), [`diff`](../api/basic_json/diff.md),
[`flatten`](../api/basic_json/flatten.md), and the binary writers, recurse only up to a fixed depth and continue with
an explicit stack below it. Applications that process untrusted input can limit its nesting depth with a
an explicit stack below it. The depth is 128 levels by default and can be changed with
[`JSON_RECURSION_DEPTH_LIMIT`](../api/macros/json_recursion_depth_limit.md), for example to run on a small stack.
Applications that process untrusted input can limit its nesting depth with a
[parser callback](../features/parsing/parser_callbacks.md).
- **Invariants are checked.** The class invariant (for instance, that the pointer for the stored type is never null) is
checked with runtime assertions throughout the test suite.
+25 -1
View File
@@ -759,9 +759,14 @@ body:has(.nj-hero) .md-content {
}
}
/* blog posts: author and date right below the title */
.md-typeset .nj-byline { margin: -.8rem 0 1.6rem; font-size: .75rem; color: var(--md-default-fg-color--light); }
.md-typeset .nj-byline a { color: inherit; }
.md-typeset .nj-byline a:hover { color: var(--md-accent-fg-color); }
/* figure captions: "Figure N." in bold, close to the figure, set in from the text column; figures and tables are
numbered per page, each with its own counter */
.md-content__inner { counter-reset: nj-figure nj-table; }
.md-content__inner { counter-reset: nj-figure nj-table nj-listing; }
.md-typeset :is(.nj-caption, .nj-charts-caption) {
max-width: none;
margin: .3rem 2rem 1.5rem;
@@ -782,6 +787,25 @@ body:has(.nj-hero) .md-content {
.md-typeset :is(.nj-caption, .nj-charts-caption) { margin-inline: 1rem; }
}
/* code captions: "Listing N." in bold, directly below the code block, in the figure caption style */
.md-typeset .nj-code-caption {
max-width: none;
margin: -.4rem 2rem 1.5rem;
font-size: .75rem;
line-height: 1.5;
text-align: left;
color: var(--md-default-fg-color--light);
}
.md-typeset .nj-code-caption::before {
counter-increment: nj-listing;
content: "Listing " counter(nj-listing) ". ";
font-weight: 700;
color: var(--md-default-fg-color);
}
@media screen and (max-width: 44.9375em) {
.md-typeset .nj-code-caption { margin-inline: 1rem; }
}
/* table captions: "Table N." in bold, directly below the table, in the figure caption style */
.md-typeset .nj-table-caption {
max-width: none;
+44
View File
@@ -27,6 +27,10 @@
.md-typeset svg.nj-charts--compile, .md-typeset svg.nj-charts--scaling { max-width: 26rem; }
.md-typeset svg.nj-charts--sizes { max-width: 26rem; }
/* home/blog/how_3_13_0_got_faster.md (scripts/figures/perf_blog.py) */
.md-typeset svg.nj-charts--blog { max-width: 34rem; }
.md-typeset svg.nj-charts--swar { max-width: 34rem; }
.md-typeset svg.nj-charts--timeline { max-width: 22rem; }
.nj-charts text { fill: currentColor; font-size: 13px; }
.nj-charts .nj-charts-leg { font-size: 13.5px; }
@@ -43,10 +47,34 @@
paint-order: stroke; stroke: var(--md-default-bg-color); stroke-width: 4px; stroke-linejoin: round; }
.nj-charts .nj-charts-factor { fill: var(--nj-charts-ink2); font-size: 12px; font-weight: 600; text-anchor: middle; }
.nj-charts .nj-charts-note { fill: var(--nj-charts-ink2); font-size: 12px; }
/* heatmap cells (perf_blog_usecases_*.svg): green for the fastest, gray around twice the time, red for the slowest;
adjacent steps checked for CVD separation on the light and the slate surface; the numbers are in every cell */
.md-typeset svg.nj-charts--heat { max-width: 49rem; }
.nj-charts--heat { --nj-heat-0: #3fa557; --nj-heat-1: #8bcc95; --nj-heat-2: #ebeae6; --nj-heat-3: #f2ae9b;
--nj-heat-4: #dc5a43; --nj-heat-ink: #1b1b1b; --nj-heat-ink-strong: #1b1b1b; }
[data-md-color-scheme="slate"] .nj-charts--heat { --nj-heat-0: #3a9d50; --nj-heat-1: #2c6b3a; --nj-heat-2: #33353a;
--nj-heat-3: #8a3f2e; --nj-heat-4: #d95b3a; --nj-heat-ink: #e8e8e6; --nj-heat-ink-strong: #141414; }
.nj-charts .nj-heat-0 { fill: var(--nj-heat-0); }
.nj-charts .nj-heat-1 { fill: var(--nj-heat-1); }
.nj-charts .nj-heat-2 { fill: var(--nj-heat-2); }
.nj-charts .nj-heat-3 { fill: var(--nj-heat-3); }
.nj-charts .nj-heat-4 { fill: var(--nj-heat-4); }
.nj-charts .nj-heat-na { fill: none; stroke: var(--nj-charts-grid); stroke-width: 1; }
.nj-charts .nj-charts-cell { fill: var(--nj-heat-ink); font-size: 12px; font-variant-numeric: tabular-nums; }
.nj-charts :is(.nj-heat-0, .nj-heat-4) ~ .nj-charts-cell { fill: var(--nj-heat-ink-strong); }
.nj-charts .nj-charts-cell--small { font-size: 10.5px; }
.nj-charts .nj-heat-cell { cursor: default; outline: none; }
.nj-charts .nj-heat-cell:is(:hover, :focus-visible, .nj-heat-on) rect { stroke: var(--md-default-fg-color); stroke-width: 1.5; }
.nj-heat-tip { position: absolute; z-index: 5; max-width: 24rem; padding: .5rem .7rem; border-radius: 6px;
background: var(--md-default-bg-color); color: var(--md-default-fg-color);
box-shadow: 0 2px 10px rgba(0, 0, 0, .25); font-size: .68rem; line-height: 1.45; pointer-events: none; }
.nj-heat-tip strong { display: block; margin-bottom: .2rem; }
.nj-heat-tip[hidden] { display: none; }
.nj-charts .nj-charts-grid { stroke: var(--nj-charts-grid); stroke-width: 1; }
.nj-charts .nj-charts-zero, .nj-charts .nj-charts-axis { stroke: var(--nj-charts-base); stroke-width: 1.5; }
.nj-charts .nj-charts-ref { stroke: currentColor; stroke-width: 1.5; }
.nj-charts .nj-charts-head { fill: var(--nj-charts-base); }
.nj-charts .nj-charts-c1 { fill: var(--nj-charts-c1); }
.nj-charts .nj-charts-c2 { fill: var(--nj-charts-c2); }
@@ -140,3 +168,19 @@
/* direct line labels replace the legend */
.nj-charts .nj-charts-name-json { fill: var(--nj-charts-json-c); font-weight: 600; }
.nj-charts .nj-charts-name-ojson { fill: var(--nj-charts-ojson-c); font-weight: 600; }
/* theme badges of home/blog/how_3_13_0_got_faster.md: the number of one of the eight themes, as a digit in a blue
circle, in headings, text, tables and the timeline figures (nj-theme-dot); "U" and other marks are gray */
.md-typeset .nj-theme { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
min-width: 1.45em; height: 1.45em; padding: 0 .3em; border-radius: .725em; vertical-align: .08em;
background: var(--nj-blue); color: var(--md-default-bg-color); font-size: .78em; font-weight: 700; line-height: 1;
font-variant-numeric: tabular-nums; text-decoration: none; }
.md-typeset a.nj-theme:is(:hover, :focus-visible) { background: var(--nj-blue-deep); color: var(--md-default-bg-color); }
.md-typeset .nj-theme--other { background: var(--md-default-fg-color--light); }
.md-typeset h2 .nj-theme { margin-right: .35em; vertical-align: .12em; }
.md-typeset .nj-theme-list > ul { list-style: none; margin-left: 0; }
.md-typeset .nj-theme-list > ul > li { margin-left: 0; padding-left: 2em; text-indent: -2em; }
.md-typeset .nj-theme-list > ul > li > .nj-theme { margin-right: .55em; text-indent: 0; }
.nj-charts .nj-theme-dot { fill: var(--nj-blue); }
.nj-charts .nj-theme-dot--other { fill: var(--nj-charts-ink2); }
.nj-charts .nj-theme-num { fill: var(--md-default-bg-color); font-size: 11px; font-weight: 700; }
+1 -1
View File
@@ -1,4 +1,4 @@
/* figures: jsonview (features/json_view.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
/* figures: jsonview (features/json_view/*.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
/* one colour per concept, the same in every figure: structure (object/array, views) blue, keys navy, strings green,
numbers orange, decoded strings purple, owned edit storage teal, dangling/invalid red */
@@ -0,0 +1,29 @@
#include <functional>
#include <iostream>
#include <utility>
// walk over at most 16 nesting levels on the call stack, and over the rest with an explicit stack
#define JSON_RECURSION_DEPTH_LIMIT 16
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// an array nested 5,000 levels deep, built without recursion
json j = 1;
for (int i = 0; i < 5000; ++i)
{
json outer = json::array();
outer.push_back(std::move(j));
j = std::move(outer);
}
// the results are those of any other limit
const json copy = j;
std::cout << std::boolalpha;
std::cout << "limit: " << JSON_RECURSION_DEPTH_LIMIT << '\n';
std::cout << "copy equals original: " << (copy == j) << '\n';
std::cout << "length of dump(): " << j.dump().size() << '\n';
std::cout << "same hash: " << (std::hash<json> {}(copy) == std::hash<json> {}(j)) << '\n';
}
@@ -0,0 +1,4 @@
limit: 16
copy equals original: true
length of dump(): 10001
same hash: true
@@ -1,43 +0,0 @@
#include <iostream>
#include <iomanip>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// a SAX parser that creates a JSON value like json::parse does, but that
// recovers from parse errors instead of stopping at the first one
class recovering_parser : public nlohmann::detail::json_sax_dom_parser<json>
{
public:
explicit recovering_parser(json& result)
: nlohmann::detail::json_sax_dom_parser<json>(result, false)
{}
bool parse_error(std::size_t position,
const std::string& /*last_token*/,
const json::exception& ex)
{
std::cout << "byte " << position << ": " << ex.what() << '\n';
// repair the input and continue
return true;
}
};
int main()
{
// JSON text with several mistakes that ends too early
const std::string text = R"({
"name": "Hello World",
"tags": ["a" "b",],
"valid": tru,
"size": 1.,
"nested": {"x": 1)";
json result;
recovering_parser sax(result);
const bool valid = json::sax_parse(text, &sax);
std::cout << "\nvalid JSON: " << std::boolalpha << valid << '\n'
<< std::setw(4) << result << std::endl;
}
@@ -1,19 +0,0 @@
byte 49: [json.exception.parse_error.101] parse error at line 3, column 20: syntax error while parsing array - unexpected string literal; expected ']'
byte 51: [json.exception.parse_error.101] parse error at line 3, column 22: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
byte 70: [json.exception.parse_error.101] parse error at line 4, column 17: syntax error while parsing value - invalid literal; last read: '"valid": tru,'
byte 86: [json.exception.parse_error.101] parse error at line 5, column 15: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.,'
byte 109: [json.exception.parse_error.101] parse error at line 6, column 22: syntax error while parsing object - unexpected end of input; expected '}'
valid JSON: false
{
"name": "Hello World",
"nested": {
"x": 1
},
"size": 1,
"tags": [
"a",
"b"
],
"valid": null
}
@@ -88,7 +88,6 @@ The library uses the following mapping from JSON values types to BJData types ac
| number_integer | 65536..2147483647 | int32 | `l` |
| number_integer | 2147483648..4294967295 | uint32 | `m` |
| number_integer | 4294967296..9223372036854775807 | int64 | `L` |
| number_integer | 9223372036854775808..18446744073709551615 | uint64 | `M` |
| number_unsigned | 0..127 | int8 | `i` |
| number_unsigned | 128..255 | uint8 | `U` |
| number_unsigned | 256..32767 | int16 | `I` |
@@ -61,7 +61,7 @@ specification:
| number_integer | 128..255 | uint 8 | 0xCC |
| number_integer | 256..65535 | uint 16 | 0xCD |
| number_integer | 65536..4294967295 | uint 32 | 0xCE |
| number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF |
| number_integer | 4294967296..9223372036854775807 | uint 64 | 0xCF |
| number_unsigned | 0..127 | positive fixint | 0x00..0x7F |
| number_unsigned | 128..255 | uint 8 | 0xCC |
| number_unsigned | 256..65535 | uint 16 | 0xCD |
+2 -2
View File
@@ -48,7 +48,7 @@ which avoids repeating it:
[`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other
type is obtained from a subtree that is built on demand with
[`materialize()`](../api/basic_json_view/materialize.md). See
[Zero-copy JSON views](json_view.md#getting-values-out-without-copying).
[Zero-copy JSON views](json_view/reading.md#getting-values-out-without-copying).
The library already knows how to convert to and from the scalar types and the STL containers (such as
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
@@ -269,7 +269,7 @@ The conversions above are built in for standard types. To make the same syntax w
- [`get_ref`](../api/basic_json/get_ref.md) - / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
- [Zero-copy JSON views](json_view.md) - get values out of a document or a view
- [Zero-copy JSON views](json_view/index.md) - get values out of a document or a view
- [`adl_serializer`](../api/adl_serializer/index.md) - the default serializer behind the conversions
- [`type`](../api/basic_json/type.md) - query the type of a value
- [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) - toggle conversion to tuples of references
Loaded 100 of 253 files, more files were not shown because too many files have changed in this diff. Show more