mirror of
https://github.com/nlohmann/json.git
synced 2026-11-13 07:25:23 +00:00
Merge fix/ubjson-valueless-budget into fix/reserve-unbacked-count, wrap docs, skip ICC-incompatible tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
96 files changed
+2880
-1464
No files matched your search
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -130,11 +130,13 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
||||
|
||||
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
|
||||
|
||||
@@ -124,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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -111,8 +111,9 @@ points apply to every edit:
|
||||
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/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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -42,6 +42,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)).
|
||||
|
||||
@@ -125,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 `"`.
|
||||
@@ -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
|
||||
|
||||
@@ -40,9 +40,10 @@ The default value is `4`.
|
||||
```
|
||||
|
||||
The first is reported for `0` and negative values, the second for values such as `4.5`, `1e3`, `true`, or a
|
||||
character of any character type (`'a'`, `L'a'`, `u8'a'`, ...), and the third for an integer type that is wider than
|
||||
`#!cpp std::uintmax_t` (for example `unsigned __int128`). A value that is larger than the largest `#!cpp std::size_t` is treated as that largest value, so that a project can use one
|
||||
spelling, for example `0xFFFFFFFFFFFFFFFFu`, for 32-bit and 64-bit targets.
|
||||
character of any character type (`'a'`, `L'a'`, `u8'a'`, ...), and the third for an integer type that is wider
|
||||
than `#!cpp std::uintmax_t` (for example `unsigned __int128`). A value that is larger than the largest `#!cpp
|
||||
std::size_t` is treated as that largest value, so that a project can use one spelling, for example
|
||||
`0xFFFFFFFFFFFFFFFFu`, for 32-bit and 64-bit targets.
|
||||
|
||||
!!! info "Large values: the limit is switched off"
|
||||
|
||||
|
||||
@@ -53,12 +53,15 @@ flowchart LR
|
||||
only as bytes are actually read, arrays reserve at most a fixed number of elements up front (and all of them
|
||||
together no more than the input read so far can back), 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.
|
||||
|
||||
@@ -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
|
||||
@@ -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 |
|
||||
|
||||
@@ -50,6 +50,13 @@ The same holds for what a view hands out without copying: a string from
|
||||
[`number_token()`](../../api/basic_json_view/number_token.md), and [`string_view` members](reading.md#string_view-members)
|
||||
of decoded structs are valid only as long as the document and, if it borrows, its source text.
|
||||
|
||||
This includes [`read()`](../../api/basic_json_document/read.md): it releases the document's text, decoded strings, and
|
||||
edits before it parses its input, so the input must not be a `string_view` obtained from the document itself: its
|
||||
[`source()`](../../api/basic_json_document/source.md), a string from `get_string()`, a token from `number_token()`, or a
|
||||
key from an iterator (those of a borrowed text that the caller owns, read unchanged, are not affected). To re-parse a
|
||||
string that the document holds, for example JSON embedded in a string value, copy the string into a `#!cpp std::string`
|
||||
first and pass the copy; see [`read()`](../../api/basic_json_document/read.md#notes) for the exact list.
|
||||
|
||||
??? example "Example: borrowed and owned documents, and when views become invalid"
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -198,6 +198,16 @@ this is planned to become the default in version 4.0.0.
|
||||
|
||||
See [full documentation of `JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md).
|
||||
|
||||
## `JSON_RECURSION_DEPTH_LIMIT`
|
||||
|
||||
Sets the number of nesting levels that the operations walking a value (copying, comparing, serializing, hashing,
|
||||
merging, diffing, converting, destroying, and the binary writers) recurse into on the call stack before they continue
|
||||
with an explicit stack on the heap. The default is `128`; allowed are the integers from `0` to `254`. Lower it for
|
||||
threads with small stacks, raise it to trade stack for speed. Results are the same for every setting; the value must be
|
||||
the same in all translation units of a program.
|
||||
|
||||
See [full documentation of `JSON_RECURSION_DEPTH_LIMIT`](../api/macros/json_recursion_depth_limit.md).
|
||||
|
||||
## `JSON_SKIP_LIBRARY_VERSION_CHECK`
|
||||
|
||||
When defined, the library will not create a compiler warning when a different version of the library was already
|
||||
|
||||
@@ -36,15 +36,21 @@ Only the following symbols are exported from `nlohmann.json`:
|
||||
- `nlohmann::basic_json`
|
||||
- `nlohmann::basic_json_document`
|
||||
- `nlohmann::basic_json_view`
|
||||
- `nlohmann::is_json_view`
|
||||
- `nlohmann::json`
|
||||
- `nlohmann::json_document`
|
||||
- `nlohmann::json_editable_document`
|
||||
- `nlohmann::json_editable_view`
|
||||
- `nlohmann::json_pointer`
|
||||
- `nlohmann::json_view`
|
||||
- `nlohmann::ordered_json`
|
||||
- `nlohmann::ordered_json_document`
|
||||
- `nlohmann::ordered_json_editable_document`
|
||||
- `nlohmann::ordered_json_editable_view`
|
||||
- `nlohmann::ordered_json_view`
|
||||
- `nlohmann::ordered_map`
|
||||
- `nlohmann::to_string`
|
||||
- `nlohmann::view_mode`
|
||||
- `nlohmann::literals::json_literals::operator""_json`
|
||||
- `nlohmann::literals::json_literals::operator""_json_pointer`
|
||||
|
||||
@@ -56,6 +62,7 @@ Additionally, the following `nlohmann::detail` symbols are exported, solely to w
|
||||
and should not be used directly:
|
||||
|
||||
- `nlohmann::detail::json_sax_dom_callback_parser`
|
||||
- `nlohmann::detail::json_sax_dom_parser`
|
||||
- `nlohmann::detail::unknown_size`
|
||||
|
||||
## Known issues
|
||||
|
||||
@@ -77,9 +77,15 @@ them faster than others:
|
||||
|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
|
||||
| strings, string views, string literals, contiguous containers, pointer or iterator ranges over contiguous memory | the whole input is scanned in bulk: runs of string characters, numbers, and indentation are recognized several bytes at a time |
|
||||
| `#!cpp std::istream` (`#!cpp std::ifstream`, `#!cpp std::istringstream`, ...) | the characters the stream buffer holds are scanned in bulk like a string; the stream buffer is asked for more when they are used up |
|
||||
| `#!cpp FILE*` | with glibc and the Apple C library, the characters the `FILE` holds in its buffer are scanned in bulk like a stream's; elsewhere one character at a time; the file is locked once for the whole parse |
|
||||
| `#!cpp FILE*` | with glibc and the Apple C library, the characters the `FILE` holds in its buffer are scanned in bulk like a stream's; elsewhere one character at a time; where the C library has unlocked stdio functions, the file is locked once for the whole parse |
|
||||
| other iterator ranges (e.g., `#!cpp std::list<char>`, `#!cpp std::istreambuf_iterator<char>`), wide strings | one character at a time |
|
||||
|
||||
Defining [`JSON_DISABLE_STREAM_BULK_SCAN`](../../api/macros/json_disable_stream_bulk_scan.md) to `1` turns off the bulk
|
||||
scan of a stream buffer, so a `#!cpp std::istream` is read one character at a time. Defining
|
||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../../api/macros/json_disable_unlocked_file_io.md) to `1` makes a `#!cpp FILE*` read
|
||||
every character with `std::fgetc`, without locking the file for the whole parse and without the bulk scan of its buffer
|
||||
(which `JSON_DISABLE_STREAM_BULK_SCAN` does not affect). Neither changes any result.
|
||||
|
||||
Input in one block of memory is read even faster with the default options (no
|
||||
[parser callback](../../api/basic_json/parser_callback_t.md), no comments, no trailing commas, and for `sax_parse()`
|
||||
`strict = true`): `parse()`, `accept()`, and `sax_parse()` then read the grammar and the tokens in one loop over the
|
||||
|
||||
@@ -54,10 +54,32 @@ Once a value has been parsed, operations that walk it recursively — serializin
|
||||
[`dump`](../../api/basic_json/dump.md), hashing it, copying it, comparing two values with `#!cpp ==`, `#!cpp <`, or (in
|
||||
C++20) `#!cpp <=>`, merging with [`update`](../../api/basic_json/update.md), applying a
|
||||
[`merge_patch`](../../api/basic_json/merge_patch.md), creating a patch with [`diff`](../../api/basic_json/diff.md) or
|
||||
[`merge_diff`](../../api/basic_json/merge_diff.md), [`flatten`](../../api/basic_json/flatten.md), and the binary
|
||||
writers (`to_cbor`, `to_msgpack`, ...) — descend at most 128 levels on the call stack and continue
|
||||
below that with an explicit stack instead, so none of them can exhaust the stack either, however deeply the value is
|
||||
nested. Destroying a value (its destructor) never recurses at all, regardless of nesting depth, for the same reason.
|
||||
[`merge_diff`](../../api/basic_json/merge_diff.md), converting it to another `basic_json` type, destroying it, and the
|
||||
binary writers (`to_cbor`, `to_msgpack`, ...) — descend at most
|
||||
[`JSON_RECURSION_DEPTH_LIMIT`](../../api/macros/json_recursion_depth_limit.md) levels on the call stack, 128 by default,
|
||||
and continue below that with an explicit stack on the heap. So the call stack does not grow with the nesting depth of the
|
||||
value, however deeply it is nested. ([`flatten`](../../api/basic_json/flatten.md),
|
||||
[`unflatten`](../../api/basic_json/unflatten.md), and [`to_bson`](../../api/basic_json/to_bson.md) never use the call
|
||||
stack for the nesting.)
|
||||
|
||||
The call stack still needs room for the levels that are recursed into, and the default limit assumes a stack of ordinary
|
||||
size. The stack that an operation needs is roughly proportional to the limit; with the default of 128 levels, the most
|
||||
demanding operation (`dump` in an unoptimized build) needed up to 288 KiB, and an optimized build needed 48 KiB or less,
|
||||
in measurements on macOS with arm64. The most demanding operation needed this much stack at the limits `0`, `32`,
|
||||
`128`, and `254`:
|
||||
|
||||
| Stack needed, in KiB | limit 0 | limit 32 | limit 128 (default) | limit 254 |
|
||||
|---|---:|---:|---:|---:|
|
||||
| unoptimized build (`-O0`) | 16 or less | 80 | 288 | 576 |
|
||||
| optimized build (`-O2`) | 16 or less | 16 or less | 48 | 112 |
|
||||
|
||||
A thread with a smaller stack (a task of a real-time operating system, a fiber, or a thread of the musl C library, whose
|
||||
default stack is 128 KiB) can still overflow its stack on deeply nested input, which is why the limit can be lowered:
|
||||
define `JSON_RECURSION_DEPTH_LIMIT` to a smaller value, down to `0`, before including the library. Such a limit changes
|
||||
only the stack that is needed and the speed (values nested more deeply than the limit are walked more slowly), never a
|
||||
result. See the [documentation of the macro](../../api/macros/json_recursion_depth_limit.md) for the complete tables,
|
||||
the speed, and the rules for choosing a value. Destroying a value (its destructor) recurses over the same number of
|
||||
levels and then continues without the call stack, and needs 32 KiB or less at every limit.
|
||||
|
||||
## Input size
|
||||
|
||||
@@ -107,15 +129,19 @@ For the binary formats, an announced size is never trusted outright:
|
||||
|
||||
## Strings
|
||||
|
||||
Invalid UTF-8 is rejected while parsing, not just while serializing:
|
||||
Invalid UTF-8 is rejected while parsing JSON text, not just while serializing:
|
||||
|
||||
- In JSON text, an ill-formed UTF-8 byte in a string is a
|
||||
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) ("invalid string: ill-formed UTF-8 byte").
|
||||
- In UTF-16 or UTF-32 input, an ill-formed sequence is a
|
||||
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) as well: a lone surrogate, a high surrogate
|
||||
that is not followed by its low surrogate, and a code point above `U+10FFFF` are all rejected.
|
||||
- In a binary format, a string that is not valid UTF-8 is a
|
||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113).
|
||||
|
||||
In a binary format, a string that is not valid UTF-8 is accepted unchanged by default (none of the formats except BON8
|
||||
requires a decoder to reject it). Pass [`error_handler_t::strict`](../../api/basic_json/error_handler_t.md) to the
|
||||
`from_*` function to get a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) instead. BON8
|
||||
always validates ([`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) for an ill-formed
|
||||
sequence).
|
||||
|
||||
A `#!cpp '\0'` (NUL) byte *inside* a quoted JSON string is always rejected (it must be escaped as `\u0000`). A NUL byte
|
||||
*outside* of a string is different: by default it is silently treated as the end of the input, so trailing bytes after
|
||||
@@ -126,8 +152,8 @@ that happens to embed a NUL is a way to make part of it disappear without a pars
|
||||
unexpected byte instead.
|
||||
|
||||
Parsing is not the only place invalid UTF-8 matters: a string that reached a `#!cpp json` value some other way (for
|
||||
example, constructed by application code, or, before JSON_STRICT_NUL_HANDLING existed, read from a binary format that
|
||||
does not validate strings) still has to round-trip back to JSON text. By default,
|
||||
example, constructed by application code, or read from a binary format with the default `error_handler_t::keep`)
|
||||
still has to round-trip back to JSON text. By default,
|
||||
[`dump()`](../../api/basic_json/dump.md) throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316)
|
||||
if the string is not valid UTF-8; passing
|
||||
[`error_handler_t::replace`](../../api/basic_json/error_handler_t.md) or `error_handler_t::ignore` avoids the exception
|
||||
@@ -180,6 +206,8 @@ indices (or the pointer as a whole) before using them, or use [`at`](../../api/b
|
||||
format lengths.
|
||||
- Decide up front how you want a string with invalid UTF-8 — from any source, not only parsing — to be serialized
|
||||
(`strict`, `replace`, or `ignore`), rather than discovering it from an uncaught `type_error.316`.
|
||||
- For untrusted input in a binary format, pass `error_handler_t::strict` to the `from_*` function if the strings must be
|
||||
valid UTF-8; by default they are not checked (BON8 always checks).
|
||||
- If a stray NUL byte silently truncating trailing input is a problem for your input format, define
|
||||
`JSON_STRICT_NUL_HANDLING`.
|
||||
- Decide whether duplicate object keys should be an error for your application, and add a callback if so.
|
||||
|
||||
@@ -27,9 +27,13 @@ A `#!cpp std::istream` (including `#!cpp std::ifstream`) gets the same bulk scan
|
||||
characters its stream buffer already holds (the *get area*) and asks the stream buffer for more only when they are used
|
||||
up. Nothing is read ahead, so the stream is left exactly where it would be after reading one character at a time, and
|
||||
[`operator>>`](../api/operator_gtgt.md) can still read several values from one stream. A stream buffer without a get
|
||||
area is read one character at a time. A `#!cpp FILE*` is locked once per parse instead of once per character, and with
|
||||
glibc and the Apple C library, the characters in its buffer are scanned in bulk as a stream's get area is. See
|
||||
[input sources and performance](parsing/index.md#input-sources-and-performance) for the full table. Parsing from a
|
||||
area is read one character at a time. Where the C library has unlocked stdio functions, a `#!cpp FILE*` is locked once
|
||||
per parse instead of once per character, and with glibc and the Apple C library, the characters in its buffer are
|
||||
scanned in bulk as a stream's get area is.
|
||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md) turns off the bulk scan of streams, and
|
||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md) both the single lock and the bulk scan
|
||||
of a `#!cpp FILE*`. See [input sources and performance](parsing/index.md#input-sources-and-performance) for the full
|
||||
table. Parsing from a
|
||||
stream takes about 1.15 to 1.45 times as long as parsing an equivalent `#!cpp std::string`, which `parse()` reads with
|
||||
a parser of its own (see there).
|
||||
|
||||
|
||||
@@ -28,15 +28,16 @@ Requirements are split into two groups:
|
||||
[`BinaryType`](#binarytype)'s `value_type`, and [`NumberUnsignedType`](#numberintegertype-and-numberunsignedtype)
|
||||
being at least as wide as [`NumberIntegerType`](#numberintegertype-and-numberunsignedtype). The rest are not
|
||||
diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside
|
||||
the library. Four violations are not caught at compile time at all:
|
||||
the library. Four violations are not (always) caught at compile time:
|
||||
|
||||
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and silently misparses numbers
|
||||
stored as a `#!cpp long double` that is not IEEE 754 binary64 (e.g., the 80-bit x87 format), because the lexer
|
||||
hands the buffer to `#!cpp std::strtold`.
|
||||
- A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation,
|
||||
and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance.
|
||||
- The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion
|
||||
in a normal build, and only fail silently under `#!cpp NDEBUG`.
|
||||
- The two [cross-specialization conversions](#cross-specialization-conversions) below, when the source string type
|
||||
is a range of characters. They compile and fail the `#!cpp JSON_ASSERT`: with its default definition, the program
|
||||
aborts unless `#!cpp NDEBUG` is defined, in which case the wrong result is returned silently.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -425,7 +426,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
| Type | Reason |
|
||||
|----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
| `#!cpp std::wstring`, `#!cpp std::u16string`, `#!cpp std::u32string` | the character type is not one byte wide |
|
||||
| `#!cpp std::u8string` | one byte wide, but `#!cpp char8_t` is not `#!cpp char`-compatible |
|
||||
| `#!cpp std::u8string` | one byte wide, but `#!cpp char8_t` is not `#!cpp char`-compatible (as a *value*, it converts to and from a JSON string) |
|
||||
| `absl::Cord` | no `value_type`, and the storage is not contiguous |
|
||||
| `QString` | no `append(const char*, size_type)`; its `QChar` is also two bytes wide, though that is never diagnosed |
|
||||
|
||||
@@ -765,14 +766,18 @@ See [`json_base_class_t`](../../api/basic_json/json_base_class_t.md) for an exam
|
||||
## Cross-specialization conversions
|
||||
|
||||
Converting a value from one `basic_json` specialization into another (see the
|
||||
[converting constructor](../../api/basic_json/basic_json.md)) imposes two additional requirements that are not
|
||||
diagnosed at compile time. With assertions enabled they abort on the `#!cpp JSON_ASSERT` at the end of the converting
|
||||
constructor; under `#!cpp NDEBUG` they fail **silently** at runtime:
|
||||
[converting constructor](../../api/basic_json/basic_json.md)) imposes two additional requirements:
|
||||
|
||||
- The target `string_t` must be directly constructible from the source `string_t`. Otherwise the string is converted to
|
||||
an array of character codes.
|
||||
- The target `object_t::key_type` must be directly constructible from the source object's key type. Otherwise the
|
||||
object is converted to an array of key/value pairs.
|
||||
- The target `string_t` must be directly constructible from the source `string_t`.
|
||||
- The target `object_t::key_type` must be directly constructible from the source object's key type.
|
||||
|
||||
If a 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 compiles and then fails the `#!cpp JSON_ASSERT` at the
|
||||
end of the converting constructor: with its default definition, `assert`, the program aborts unless `#!cpp NDEBUG` is
|
||||
defined, in which case it **silently** returns an array (of character codes for a string, of key/value pairs for an
|
||||
object). 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`.
|
||||
|
||||
See [issue #3425](https://github.com/nlohmann/json/issues/3425), [`string_t`](../../api/basic_json/string_t.md), and
|
||||
[`object_t`](../../api/basic_json/object_t.md).
|
||||
|
||||
@@ -8,6 +8,16 @@ These changes are part of the `develop` branch, but not of a release yet.
|
||||
exception is thrown during the conversion, the source may be partially moved-from, so the strong exception guarantee
|
||||
no longer holds for the source of an rvalue conversion (the JSON value itself is unaffected). Lvalues and `const`
|
||||
objects are still copied. See [`basic_json`](../../api/basic_json/basic_json.md).
|
||||
- In C++20 (library support for `char8_t`, `__cpp_lib_char8_t`), a `#!cpp std::u8string` is a JSON **string**:
|
||||
`json j = std::u8string(u8"abc");` stores `"abc"`, and `#!cpp j.get<std::u8string>()` reads it back. Version 3.12.0
|
||||
stored a `#!cpp std::u8string` as an array of code units (`[97,98,99]`); on the 3.13.0 development branch it was
|
||||
already stored as a string if `std::filesystem` is available, but could not be read back. Now the result no longer
|
||||
depends on whether `std::filesystem::path` is supported, and `#!cpp get<std::u8string>()` accepts both a JSON string
|
||||
and the array of numbers written by 3.12.0. **Consequence:** JSON documents produced from a `#!cpp std::u8string` by
|
||||
3.12.0 (arrays) are still read, but code that checks `is_array()` on such a value, or that reads it as
|
||||
`#!cpp std::vector<char8_t>` / `#!cpp std::vector<std::uint8_t>`, now sees a string; to keep the array form, convert
|
||||
explicitly, e.g., `#!cpp json(std::vector<char8_t>(s.begin(), s.end()))`. `#!cpp std::u8string_view` and
|
||||
`#!cpp char8_t` arrays are unchanged (arrays of code units). See [`basic_json`](../../api/basic_json/basic_json.md).
|
||||
|
||||
## API changes
|
||||
|
||||
|
||||
@@ -454,6 +454,7 @@ nav:
|
||||
- 'JSON_NO_SIMD': api/macros/json_no_simd.md
|
||||
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
||||
- 'JSON_PRECISE_STREAM_POSITION': api/macros/json_precise_stream_position.md
|
||||
- 'JSON_RECURSION_DEPTH_LIMIT': api/macros/json_recursion_depth_limit.md
|
||||
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
||||
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
||||
- 'JSON_STRICT_BINARY_UTF8': api/macros/json_strict_binary_utf8.md
|
||||
|
||||
+92
-246
@@ -6,7 +6,7 @@
|
||||
"": {
|
||||
"name": "check-mermaid",
|
||||
"dependencies": {
|
||||
"jsdom": "30.1.3",
|
||||
"jsdom": "31.0.0",
|
||||
"mermaid": "12.3.0"
|
||||
}
|
||||
},
|
||||
@@ -23,55 +23,12 @@
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/css-color": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-7.1.3.tgz",
|
||||
"integrity": "sha512-1t1U8Cm3RBl9vh3RAZhjkPaduzLSvQJRToRrxpGR/eDRW99mOREleuu+mWfiFa795bUzjwht7NgphCKTs2ianQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/css-calc": "^3.4.2",
|
||||
"@csstools/css-color-parser": "^4.2.5",
|
||||
"@csstools/css-parser-algorithms": "^4.0.2",
|
||||
"@csstools/css-tokenizer": "^4.0.2",
|
||||
"lru-cache": "^11.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/dom-selector": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-10.0.3.tgz",
|
||||
"integrity": "sha512-3+777rEPsOOjoy5/sRmIzHnoRDaLpm3XWY2dmWn/N7f/XwDG0bcpnFaKL3X9FYJUbNwLmpcJC1bUYexJRxsFlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bidi-js": "^1.1.0",
|
||||
"css-tree": "^3.2.1",
|
||||
"is-potential-custom-element-name": "^1.0.1",
|
||||
"lru-cache": "^11.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@braintree/sanitize-url": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",
|
||||
"integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@bramus/specificity": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
|
||||
"integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-tree": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"specificity": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@chevrotain/cst-dts-gen": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-13.2.0.tgz",
|
||||
@@ -109,140 +66,6 @@
|
||||
"integrity": "sha512-SzKYY68rseOrb9KILVzPsl6SjkQtdWw01SRGB5aCVI5kFcHltQsOYV3JB0Ffst72DgkvHCyDQ0NLwxtmCCf97Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@csstools/color-helpers": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.2.tgz",
|
||||
"integrity": "sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-calc": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.3.tgz",
|
||||
"integrity": "sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^4.0.2",
|
||||
"@csstools/css-tokenizer": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-color-parser": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.6.tgz",
|
||||
"integrity": "sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/color-helpers": "^6.1.2",
|
||||
"@csstools/css-calc": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^4.0.2",
|
||||
"@csstools/css-tokenizer": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-parser-algorithms": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.2.tgz",
|
||||
"integrity": "sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-tokenizer": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz",
|
||||
"integrity": "sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT-0",
|
||||
"peerDependencies": {
|
||||
"css-tree": "^3.2.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"css-tree": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-tokenizer": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.2.tgz",
|
||||
"integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@exodus/bytes": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.16.0.tgz",
|
||||
@@ -565,13 +388,29 @@
|
||||
"d3-transition": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz",
|
||||
"integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==",
|
||||
"node_modules/bcp-47": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.2.tgz",
|
||||
"integrity": "sha512-wsv86UuNFuPnVieKLyECTT+CP1WSAHeoIg5P3xu2kA9Z+7fVPoKt7dxzSeEhtJel0AI+NW3fIW3KtTKQKUCngg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
"is-alphabetical": "^2.0.0",
|
||||
"is-alphanumerical": "^2.0.0",
|
||||
"is-decimal": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/bcp-47-match": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
|
||||
"integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/chevrotain": {
|
||||
@@ -608,19 +447,6 @@
|
||||
"layout-base": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
|
||||
"integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdn-data": "2.27.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cytoscape": {
|
||||
"version": "3.34.3",
|
||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.3.tgz",
|
||||
@@ -1170,9 +996,9 @@
|
||||
"license": "EPL-2.0 OR GPL-3.0-or-later"
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
|
||||
"integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-9.0.0.tgz",
|
||||
"integrity": "sha512-oJFNDJg3kTPwAD1cNn2+WmUq4wFoo09npc3Ydf1D7ED4smRiWrsKnaLxykrTWgyGEcusc87P5dxlAleol9kpkg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
@@ -1242,6 +1068,40 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphabetical": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
|
||||
"integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphanumerical": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
|
||||
"integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-alphabetical": "^2.0.0",
|
||||
"is-decimal": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-decimal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
|
||||
"integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-potential-custom-element-name": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
|
||||
@@ -1249,40 +1109,38 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jsdom": {
|
||||
"version": "30.1.3",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.1.3.tgz",
|
||||
"integrity": "sha512-hQMUPvRgN7KxsfIytDQji7bimuJeRQa6Qo5+iuiZQk5CafeRxLIA10/cG5YCBFNpD9RO3oqIiN4CO1Y44kxqXg==",
|
||||
"version": "31.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-31.0.0.tgz",
|
||||
"integrity": "sha512-xAkhD9z1eyFC5+QUi9K24CaZJgdCQ2GOGLW1GdVDoQvkp/AxEH9TQ0pAplPOlIiV92hfcgnFOADZypbjgbBT6Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@asamuzakjp/css-color": "^7.1.3",
|
||||
"@asamuzakjp/dom-selector": "^10.0.3",
|
||||
"@bramus/specificity": "^2.4.2",
|
||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.15",
|
||||
"@exodus/bytes": "^1.16.0",
|
||||
"css-tree": "^3.2.1",
|
||||
"bcp-47": "^2.1.2",
|
||||
"bcp-47-match": "^2.0.3",
|
||||
"data-urls": "^8.0.0",
|
||||
"decimal.js": "^10.6.0",
|
||||
"html-encoding-sniffer": "^7.0.0",
|
||||
"is-potential-custom-element-name": "^1.0.1",
|
||||
"lru-cache": "^11.5.3",
|
||||
"parse5": "^8.0.1",
|
||||
"parse5": "^8.1.0",
|
||||
"parse5-parser-stream": "^8.1.0",
|
||||
"saxes": "^6.0.0",
|
||||
"tough-cookie": "^6.0.2",
|
||||
"undici": "^8.12.1",
|
||||
"undici": "^8.12.3",
|
||||
"w3c-xmlserializer": "^6.0.0",
|
||||
"webidl-conversions": "^8.0.1",
|
||||
"whatwg-mimetype": "^5.0.0",
|
||||
"whatwg-url": "^17.2.0",
|
||||
"whatwg-url": "^17.3.0",
|
||||
"xml-name-validator": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"canvas": "^3.2.3"
|
||||
"@napi-rs/canvas": "^1.0.13"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"canvas": {
|
||||
"@napi-rs/canvas": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
@@ -1350,12 +1208,6 @@
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.27.1",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
|
||||
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "12.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-12.3.0.tgz",
|
||||
@@ -1396,12 +1248,24 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/parse5": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
|
||||
"integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.1.0.tgz",
|
||||
"integrity": "sha512-sq3qBFpee5jeocbFbO48VHlGr8sT7mbXS0vxlKN6sottBtJzrOG3998sr0llSZcQAGgfpdmEYAIZE0P9z0dE8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"entities": "^8.0.0"
|
||||
"entities": "^9.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/parse5-parser-stream": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-8.1.0.tgz",
|
||||
"integrity": "sha512-58MPfNTA+DsIzro41rU94xghnu/h2N4yEPdPdGPlZVHM/QvA5jlVGYVo7IzlHbFLHYx3cZUnr9s9HBrA0tEYyw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parse5": "^8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
@@ -1438,15 +1302,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/robust-predicates": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
|
||||
@@ -1489,15 +1344,6 @@
|
||||
"node": ">=v12.22.7"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
|
||||
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",
|
||||
@@ -1565,9 +1411,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "8.12.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-8.12.2.tgz",
|
||||
"integrity": "sha512-XtuDWVdqTLr3NAxm2ly9/hU6kUsJOgGh1RASzMBbsw6aWCOaQzHdW+QQDfdERjfJ2V5vL5pRPa4RetO9UfivDg==",
|
||||
"version": "8.12.4",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-8.12.4.tgz",
|
||||
"integrity": "sha512-8Mx+uNKKER2bA4AdtedkqRbwUCIJFr2nJckXRe5yrDqzePqwLP7VNcctN8/XdmDSbF7mS+jJKIszlL+vbam1VA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
@@ -1604,9 +1450,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "17.2.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.2.0.tgz",
|
||||
"integrity": "sha512-IhLIHNcBrzIixC5XXsbr4DxdjajyA5vQ5RwGZMtrUp+GbtE5Nrsge9QKau7jXrC0aTXLYGV0xiW7Tb4YS2aomw==",
|
||||
"version": "17.3.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.3.0.tgz",
|
||||
"integrity": "sha512-KwOAJrpf+GTgBvcBQJcP38sMF0DsQKdwtG+TxM2pW44P6E5k0hDbWrDmyuJDIF0Uef0aQq0ZoJs+5oI+k8ddhg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@exodus/bytes": "^1.16.0",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Validate the Mermaid diagrams of the documentation (see check_mermaid.mjs)",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"jsdom": "30.1.3",
|
||||
"jsdom": "31.0.0",
|
||||
"mermaid": "12.3.0"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user