Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2d379fe84 | ||
|
|
6a852ab96d | ||
|
|
7757de3a58 | ||
|
|
2c7474affc | ||
|
|
6343775000 | ||
|
|
592c23b4bc | ||
|
|
c1104d20b8 | ||
|
|
181f41487f | ||
|
|
7960f33af8 | ||
|
|
8b70bf8767 | ||
|
|
4f012e9b14 | ||
|
|
7dcd213e4c | ||
|
|
788b12213b | ||
|
|
2eb9aae537 | ||
|
|
933952e095 |
No files matched your search
@@ -111,8 +111,6 @@ context which existing file needs to be extended, and only very few cases requir
|
|||||||
When fixing a bug, edit `unit-regression3.cpp` and add a section referencing the fixed issue.
|
When fixing a bug, edit `unit-regression3.cpp` and add a section referencing the fixed issue.
|
||||||
`unit-regression2.cpp` holds the older tests; the two files exist because a single one grew large enough for the
|
`unit-regression2.cpp` holds the older tests; the two files exist because a single one grew large enough for the
|
||||||
MinGW linker to fail relocating it, so please keep adding to the smaller file rather than growing the larger one.
|
MinGW linker to fail relocating it, so please keep adding to the smaller file rather than growing the larger one.
|
||||||
Tests that call `sax_parse` go into `unit-sax_parse.cpp` rather than into a large test file: every call instantiates
|
|
||||||
the parser and the binary reader that recover from errors, which grows a test file considerably.
|
|
||||||
|
|
||||||
#### Exceptions
|
#### Exceptions
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ cc_library(
|
|||||||
"include/nlohmann/detail/exceptions.hpp",
|
"include/nlohmann/detail/exceptions.hpp",
|
||||||
"include/nlohmann/detail/hash.hpp",
|
"include/nlohmann/detail/hash.hpp",
|
||||||
"include/nlohmann/detail/input/binary_reader.hpp",
|
"include/nlohmann/detail/input/binary_reader.hpp",
|
||||||
|
"include/nlohmann/detail/input/contiguous_parser.hpp",
|
||||||
"include/nlohmann/detail/input/input_adapters.hpp",
|
"include/nlohmann/detail/input/input_adapters.hpp",
|
||||||
"include/nlohmann/detail/input/json_sax.hpp",
|
"include/nlohmann/detail/input/json_sax.hpp",
|
||||||
"include/nlohmann/detail/input/lexer.hpp",
|
"include/nlohmann/detail/input/lexer.hpp",
|
||||||
|
|||||||
@@ -495,7 +495,7 @@ bool key(string_t& val);
|
|||||||
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
|
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
|
||||||
```
|
```
|
||||||
|
|
||||||
The return value of each function determines whether parsing should proceed. For `parse_error`, returning `true` [recovers from the error](https://json.nlohmann.me/features/parsing/error_recovery/): the parser repairs the input and continues.
|
The return value of each function determines whether parsing should proceed.
|
||||||
|
|
||||||
To implement your own SAX handler, proceed as follows:
|
To implement your own SAX handler, proceed as follows:
|
||||||
|
|
||||||
@@ -503,7 +503,7 @@ To implement your own SAX handler, proceed as follows:
|
|||||||
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
||||||
3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
||||||
|
|
||||||
Note the `sax_parse` function only returns a `bool` indicating whether the input was parsed without errors and no SAX event returned `false`. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
|
Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
|
||||||
|
|
||||||
### STL-like access
|
### STL-like access
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
reuse==6.2.0
|
reuse==6.3.0
|
||||||
@@ -50,7 +50,7 @@ Besides `basic_json`, the library offers a second way to hold a parsed JSON text
|
|||||||
[`basic_json_document`](../basic_json_document/index.md), which owns or borrows the text and holds a flat index of the
|
[`basic_json_document`](../basic_json_document/index.md), which owns or borrows the text and holds a flat index of the
|
||||||
values in it, and a [`basic_json_view`](../basic_json_view/index.md), a read-only handle to one value of such a document.
|
values in it, and a [`basic_json_view`](../basic_json_view/index.md), a read-only handle to one value of such a document.
|
||||||
Both are defined in the header `<nlohmann/json_view.hpp>`; see
|
Both are defined in the header `<nlohmann/json_view.hpp>`; see
|
||||||
[Zero-copy JSON views](../../features/json_view.md) for the concept and the trade-offs.
|
[Zero-copy JSON views](../../features/json_view/index.md) for the concept and the trade-offs.
|
||||||
|
|
||||||
- [**json_document**](../json_document.md) / [**ordered_json_document**](../ordered_json_document.md) - read-only
|
- [**json_document**](../json_document.md) / [**ordered_json_document**](../ordered_json_document.md) - read-only
|
||||||
documents of the `basic_json` specialization they belong to
|
documents of the `basic_json` specialization they belong to
|
||||||
@@ -363,7 +363,7 @@ Access to the JSON value
|
|||||||
specification of the JSON data interchange format
|
specification of the JSON data interchange format
|
||||||
- [`basic_json_document`](../basic_json_document/index.md) - / [`basic_json_view`](../basic_json_view/index.md) - parse a JSON
|
- [`basic_json_document`](../basic_json_document/index.md) - / [`basic_json_view`](../basic_json_view/index.md) - parse a JSON
|
||||||
text into a flat index and read it through a view
|
text into a flat index and read it through a view
|
||||||
- [Zero-copy JSON views](../../features/json_view.md) - when to use a document and a view instead of a `basic_json`
|
- [Zero-copy JSON views](../../features/json_view/index.md) - when to use a document and a view instead of a `basic_json`
|
||||||
- [Types](../../features/types/index.md) - how JSON values are stored and which types exist
|
- [Types](../../features/types/index.md) - how JSON values are stored and which types exist
|
||||||
- [Converting values](../../features/conversions.md) - get values out of a JSON value and put values in
|
- [Converting values](../../features/conversions.md) - get values out of a JSON value and put values in
|
||||||
- [Creating JSON values](../../features/creating_values.md) - all the ways to build a JSON value
|
- [Creating JSON values](../../features/creating_values.md) - all the ways to build a JSON value
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
[`basic_json_document::parse`](../basic_json_document/parse.md) accepts the same inputs and the same options, but
|
[`basic_json_document::parse`](../basic_json_document/parse.md) accepts the same inputs and the same options, but
|
||||||
keeps the result as a flat index of the parsed values instead of a tree of `basic_json` values. It is faster and uses
|
keeps the result as a flat index of the parsed values instead of a tree of `basic_json` values. It is faster and uses
|
||||||
less memory when only parts of the document are read, and it also accepts a pointer to a null-terminated wide string,
|
less memory when only parts of the document are read, and it also accepts a pointer to a null-terminated wide string,
|
||||||
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view.md).
|
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view/index.md).
|
||||||
|
|
||||||
!!! tip "Performance"
|
!!! tip "Performance"
|
||||||
|
|
||||||
|
|||||||
@@ -104,9 +104,7 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
|||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
`#!cpp true` if the input was parsed without errors and no SAX event returned `#!cpp false`; `#!cpp false` otherwise.
|
return value of the last processed SAX event
|
||||||
In particular, the result is `#!cpp false` for input with errors, even if the SAX parser recovered from all of them
|
|
||||||
(see [error recovery](../../features/parsing/error_recovery.md)).
|
|
||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
@@ -165,7 +163,6 @@ use the same file with stdio functions, but it must not wait for another thread
|
|||||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||||
- Added `tag_handler` in version 3.13.0.
|
- Added `tag_handler` in version 3.13.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
- Recovering from parse errors (see [`parse_error`](../json_sax/parse_error.md)) added in version 3.13.0.
|
|
||||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
|
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
|
||||||
after the parsed value when `strict` is `#!cpp false`.
|
after the parsed value when `strict` is `#!cpp false`.
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ compile; see [`parse`](parse.md#notes).
|
|||||||
|
|
||||||
- [`parse`](parse.md) - deserialize from a compatible input
|
- [`parse`](parse.md) - deserialize from a compatible input
|
||||||
- [`BasicJsonType::accept`](../basic_json/accept.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::accept`](../basic_json/accept.md) - the corresponding function of `basic_json`
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how parsing a document differs from, and matches, basic_json::parse
|
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how parsing a document differs from, and matches, basic_json::parse
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Constant.
|
|||||||
|
|
||||||
- [`parse`](parse.md) - deserialize from a compatible input
|
- [`parse`](parse.md) - deserialize from a compatible input
|
||||||
- [`is_discarded`](is_discarded.md) - return whether the last parse failed
|
- [`is_discarded`](is_discarded.md) - return whether the last parse failed
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
|
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ by an erase, not only those at or after the removed element, since the container
|
|||||||
- [`root`](root.md) - the view of the root value, the starting point of overload 3
|
- [`root`](root.md) - the view of the root value, the starting point of overload 3
|
||||||
- [`BasicJsonType::erase`](../basic_json/erase.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::erase`](../basic_json/erase.md) - the corresponding function of `basic_json`
|
||||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
|
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ points apply to every edit:
|
|||||||
anything `BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
anything `BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||||
- [`dump()`](../basic_json_view/dump.md) writes an edited document with members in document order, new members at
|
- [`dump()`](../basic_json_view/dump.md) writes an edited document with members in document order, new members at
|
||||||
the end, and, with [`number_format::source`](../basic_json_view/number_format.md), keeps the spelling of every
|
the end, and, with [`number_format::source`](../basic_json_view/number_format.md), keeps the spelling of every
|
||||||
number that was not itself edited — see [Editing a document](../../features/json_view.md#editing-a-document) for
|
number that was not itself edited — see [Editing a document](../../features/json_view/editing.md) for
|
||||||
why this matters.
|
why this matters.
|
||||||
- [`read()`](read.md) discards all edits, [`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once
|
- [`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.
|
there are edits, and [`memory_usage()`](memory_usage.md) includes the memory edits use.
|
||||||
@@ -118,7 +118,7 @@ points apply to every edit:
|
|||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
|
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
|
||||||
- [`basic_json_view`](../basic_json_view/index.md) - the non-owning handle to a value in a document
|
- [`basic_json_view`](../basic_json_view/index.md) - the non-owning handle to a value in a document
|
||||||
- [`json_document`](../json_document.md) - and [`json_editable_document`](../json_editable_document.md) - the default specializations
|
- [`json_document`](../json_document.md) - and [`json_editable_document`](../json_editable_document.md) - the default specializations
|
||||||
- [`materialize`](../basic_json_view/materialize.md) - build a `basic_json` value from a view
|
- [`materialize`](../basic_json_view/materialize.md) - build a `basic_json` value from a view
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ an edit in general.
|
|||||||
- [`set`](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
- [`set`](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||||
- [`BasicJsonType::insert`](../basic_json/insert.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::insert`](../basic_json/insert.md) - the corresponding function of `basic_json`
|
||||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
|
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ When the document is discarded, [`root()`](root.md) returns a discarded view (it
|
|||||||
|
|
||||||
- [`parse`](parse.md) - deserialize from a compatible input
|
- [`parse`](parse.md) - deserialize from a compatible input
|
||||||
- [`basic_json_view::is_discarded`](../basic_json_view/is_discarded.md) - return whether a view is invalid
|
- [`basic_json_view::is_discarded`](../basic_json_view/is_discarded.md) - return whether a view is invalid
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how a discarded document arises
|
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how a discarded document arises
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ static basic_json_document load(std::vector<std::uint8_t>&& image,
|
|||||||
|
|
||||||
In every overload, the node index is copied into storage the document itself owns — so that it is properly aligned,
|
In every overload, the node index is copied into storage the document itself owns — so that it is properly aligned,
|
||||||
and, for an [editable](index.md#edits) document, can be edited — while the text and the decoded strings stay in
|
and, for an [editable](index.md#edits) document, can be edited — while the text and the decoded strings stay in
|
||||||
`image`. The hash indexes [large objects](../../features/json_view.md) use for lookup are rebuilt, exactly as after
|
`image`. The hash indexes [large objects](../../features/json_view/index.md) use for lookup are rebuilt, exactly as after
|
||||||
parsing.
|
parsing.
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
@@ -172,7 +172,7 @@ returned document, and any [view](../basic_json_view/index.md) taken from it, is
|
|||||||
- [`save`](save.md) - write the document as an image
|
- [`save`](save.md) - write the document as an image
|
||||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||||
- [`parse`](parse.md) - deserialize from JSON text instead of an image
|
- [`parse`](parse.md) - deserialize from JSON text instead of an image
|
||||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
- [Images](../../features/json_view/images.md) - why and when to use images
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ to observe the effect of [`shrink_to_fit()`](shrink_to_fit.md).
|
|||||||
|
|
||||||
- [`node_count`](node_count.md) - the number of index entries
|
- [`node_count`](node_count.md) - the number of index entries
|
||||||
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
|
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
|
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ strings and, for an owned document, the text).
|
|||||||
|
|
||||||
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
|
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
|
||||||
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
|
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
|
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ document (`#!cpp owns_source() == false`) is only valid while the buffer it was
|
|||||||
- [`parse`](parse.md) - deserialize from a compatible input
|
- [`parse`](parse.md) - deserialize from a compatible input
|
||||||
- [`parse_copy`](parse_copy.md) - deserialize a copy of a compatible input, always owned
|
- [`parse_copy`](parse_copy.md) - deserialize a copy of a compatible input, always owned
|
||||||
- [`source`](source.md) - the parsed text
|
- [`source`](source.md) - the parsed text
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
|
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ told apart from other class-type iterators, so their range is read into an owned
|
|||||||
range (e.g. of a `#!cpp std::list<char>`).
|
range (e.g. of a `#!cpp std::list<char>`).
|
||||||
|
|
||||||
See [`owns_source`](owns_source.md) to check which happened after a call, and the
|
See [`owns_source`](owns_source.md) to check which happened after a call, and the
|
||||||
[feature page](../../features/json_view.md) for the reasoning.
|
[feature page](../../features/json_view/index.md) for the reasoning.
|
||||||
|
|
||||||
**Numbers.** As for [`BasicJsonType::parse()`](../basic_json/parse.md), an integer literal too large for the 64-bit
|
**Numbers.** As for [`BasicJsonType::parse()`](../basic_json/parse.md), an integer literal too large for the 64-bit
|
||||||
integer type becomes a floating-point value.
|
integer type becomes a floating-point value.
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ compile; see [`parse`](parse.md#notes).
|
|||||||
|
|
||||||
- [`parse`](parse.md) - deserialize from a compatible input, borrowing it where possible
|
- [`parse`](parse.md) - deserialize from a compatible input, borrowing it where possible
|
||||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - borrowing or copying the source text
|
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - borrowing or copying the source text
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ Like [`set`](set.md) on a member or an element, `push_back` never moves an exist
|
|||||||
- [`root`](root.md) - the view of the root value
|
- [`root`](root.md) - the view of the root value
|
||||||
- [`BasicJsonType::push_back`](../basic_json/push_back.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::push_back`](../basic_json/push_back.md) - the corresponding function of `basic_json`
|
||||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
|
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ compile; see [`parse`](parse.md#notes).
|
|||||||
- [`parse`](parse.md) - deserialize from a compatible input
|
- [`parse`](parse.md) - deserialize from a compatible input
|
||||||
- [`root`](root.md) - the view of the root value
|
- [`root`](root.md) - the view of the root value
|
||||||
- [`load`](load.md) - read a document from an image instead of parsing JSON text
|
- [`load`](load.md) - read a document from an image instead of parsing JSON text
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - reusing a document and what it invalidates
|
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - reusing a document and what it invalidates
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ auto v = doc.root();
|
|||||||
returned view is valid under the same conditions as any other view of the document — see
|
returned view is valid under the same conditions as any other view of the document — see
|
||||||
[Object inspection](../basic_json_view/index.md) — in particular, it is invalidated by the next
|
[Object inspection](../basic_json_view/index.md) — in particular, it is invalidated by the next
|
||||||
[`read()`](read.md) or [`shrink_to_fit()`](shrink_to_fit.md) on this document. A view can dangle, like a
|
[`read()`](read.md) or [`shrink_to_fit()`](shrink_to_fit.md) on this document. A view can dangle, like a
|
||||||
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view.md#ownership-and-lifetime).
|
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view/lifetime.md).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ big-endian reader could not interpret correctly.
|
|||||||
- [`load`](load.md) - read an image written by `save()`
|
- [`load`](load.md) - read an image written by `save()`
|
||||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document to JSON text instead of an image
|
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document to JSON text instead of an image
|
||||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
- [Images](../../features/json_view/images.md) - why and when to use images
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ one-node scalar.
|
|||||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document, keeping an untouched number's
|
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document, keeping an untouched number's
|
||||||
spelling with `#!cpp number_format::source`
|
spelling with `#!cpp number_format::source`
|
||||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||||
- [Editing a document](../../features/json_view.md#editing-a-document) - why editable documents keep the source
|
- [Editing a document](../../features/json_view/editing.md) - why editable documents keep the source
|
||||||
text's order and number spelling
|
text's order and number spelling
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ possible would be an internal optimization only, not a guarantee to rely on.
|
|||||||
- [`node_count`](node_count.md) - the number of index entries
|
- [`node_count`](node_count.md) - the number of index entries
|
||||||
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
|
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
|
||||||
- [`root`](root.md) - the view of the root value
|
- [`root`](root.md) - the view of the root value
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - what releasing memory invalidates
|
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - what releasing memory invalidates
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ is; see [`owns_source`](owns_source.md).
|
|||||||
|
|
||||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||||
- [`source_offset`](../basic_json_view/source_offset.md) - byte offset of a value in the source text
|
- [`source_offset`](../basic_json_view/source_offset.md) - byte offset of a value in the source text
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
|
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Pointer path of the value within the document, and with
|
|||||||
|
|
||||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||||
the last value.
|
the last value.
|
||||||
|
|
||||||
Unlike [`operator[]`](operator[].md), which returns a [discarded](is_discarded.md) view for a missing key or an
|
Unlike [`operator[]`](operator[].md), which returns a [discarded](is_discarded.md) view for a missing key or an
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ placeholder for "no value yet" and later be assigned a real view.
|
|||||||
- [`is_discarded`](is_discarded.md) - return whether the view is invalid
|
- [`is_discarded`](is_discarded.md) - return whether the view is invalid
|
||||||
- [`root`](../basic_json_document/root.md) - the view of a document's root value
|
- [`root`](../basic_json_document/root.md) - the view of a document's root value
|
||||||
- [`view_mode`](../view_mode.md) - whether a document and its views can be edited
|
- [`view_mode`](../view_mode.md) - whether a document and its views can be edited
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - how long a view stays valid
|
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - how long a view stays valid
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ No-throw guarantee: this function never throws exceptions.
|
|||||||
|
|
||||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||||
the last value.
|
the last value.
|
||||||
|
|
||||||
Overload 1 always returns `#!cpp false` when the value is not an object — including a [discarded](is_discarded.md)
|
Overload 1 always returns `#!cpp false` when the value is not an object — including a [discarded](is_discarded.md)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()
|
|||||||
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
|
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
|
||||||
for `#!cpp -0` — `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
|
for `#!cpp -0` — `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
|
||||||
digits than any number type holds (such a literal is itself classified as a float, see
|
digits than any number type holds (such a literal is itself classified as a float, see
|
||||||
[What is different](../../features/json_view.md#what-is-different)) — something `BasicJsonType` cannot do, since
|
[What is different](../../features/json_view/differences.md)) — something `BasicJsonType` cannot do, since
|
||||||
parsing already reduces every number to its parsed value.
|
parsing already reduces every number to its parsed value.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@@ -98,7 +98,7 @@ parsing already reduces every number to its parsed value.
|
|||||||
- [`operator<<`](operator_ltlt.md) - serialize this value to a stream
|
- [`operator<<`](operator_ltlt.md) - serialize this value to a stream
|
||||||
- [`materialize`](materialize.md) - build a `BasicJsonType` value, e.g. to use `BasicJsonType::dump`'s `error_handler`
|
- [`materialize`](materialize.md) - build a `BasicJsonType` value, e.g. to use `BasicJsonType::dump`'s `error_handler`
|
||||||
- [`BasicJsonType::dump`](../basic_json/dump.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::dump`](../basic_json/dump.md) - the corresponding function of `basic_json`
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
|
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ constant time on average.
|
|||||||
|
|
||||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||||
the last value.
|
the last value.
|
||||||
|
|
||||||
Unlike [`BasicJsonType::find`](../basic_json/find.md), which always returns `#!cpp end()` for a non-object type, this
|
Unlike [`BasicJsonType::find`](../basic_json/find.md), which always returns `#!cpp end()` for a non-object type, this
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ as [`BasicJsonType::parse()`](../basic_json/parse.md) does), as well as the type
|
|||||||
accept views, e.g. with [`is_json_view`](../is_json_view.md). A `from_json()` that also accepts types that are no JSON
|
accept views, e.g. with [`is_json_view`](../is_json_view.md). A `from_json()` that also accepts types that are no JSON
|
||||||
values at all, such as the unconstrained `#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType&,
|
values at all, such as the unconstrained `#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType&,
|
||||||
T&)` of [arbitrary type conversions](../../features/arbitrary_types.md), is not called with a view: it may use
|
T&)` of [arbitrary type conversions](../../features/arbitrary_types.md), is not called with a view: it may use
|
||||||
`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view.md#writing-generic-code).
|
`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view/generic_code.md).
|
||||||
|
|
||||||
!!! warning "Duplicate keys"
|
!!! warning "Duplicate keys"
|
||||||
|
|
||||||
Such a `from_json()` looks members up in the view, where a repeated key finds the **first** member, while
|
Such a `from_json()` looks members up in the view, where a repeated key finds the **first** member, while
|
||||||
`#!cpp materialize().get<T>()` sees the **last** one, as [`BasicJsonType::parse()`](../basic_json/parse.md)
|
`#!cpp materialize().get<T>()` sees the **last** one, as [`BasicJsonType::parse()`](../basic_json/parse.md)
|
||||||
keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get<point>()` reads `#!cpp x == 1`. See
|
keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get<point>()` reads `#!cpp x == 1`. See
|
||||||
[Duplicate keys](../../features/json_view.md#duplicate-keys).
|
[Duplicate keys](../../features/json_view/differences.md#duplicate-keys).
|
||||||
|
|
||||||
Every other `T` — user types whose `from_json()` is written for `BasicJsonType` only, types without a default
|
Every other `T` — user types whose `from_json()` is written for `BasicJsonType` only, types without a default
|
||||||
constructor, `#!cpp std::tuple`s holding references, ... — is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
|
constructor, `#!cpp std::tuple`s holding references, ... — is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
|
||||||
@@ -152,7 +152,7 @@ instead: its path is relative to the root of the view's subtree, because `materi
|
|||||||
- [`number_token`](number_token.md) - a number's token text, without a copy
|
- [`number_token`](number_token.md) - a number's token text, without a copy
|
||||||
- [`materialize`](materialize.md) - build the `BasicJsonType` value of this subtree
|
- [`materialize`](materialize.md) - build the `BasicJsonType` value of this subtree
|
||||||
- [`BasicJsonType::get`](../basic_json/get.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::get`](../basic_json/get.md) - the corresponding function of `basic_json`
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ since floating-point values and unescaped strings are read from the source lazil
|
|||||||
- [`get`](get.md) - convert the value to a given type (`#!cpp get<string_view_t>()` is equivalent to this function)
|
- [`get`](get.md) - convert the value to a given type (`#!cpp get<string_view_t>()` is equivalent to this function)
|
||||||
- [`number_token`](number_token.md) - a number's token text, without a copy
|
- [`number_token`](number_token.md) - a number's token text, without a copy
|
||||||
- [`BasicJsonType::get_ref`](../basic_json/get_ref.md) - the closest counterpart of `basic_json`
|
- [`BasicJsonType::get_ref`](../basic_json/get_ref.md) - the closest counterpart of `basic_json`
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - borrowing strings instead of copying them
|
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - borrowing strings instead of copying them
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ them with those of a default-constructed `T`.
|
|||||||
|
|
||||||
- [`get`](get.md) - convert the value to a given type
|
- [`get`](get.md) - convert the value to a given type
|
||||||
- [`BasicJsonType::get_to`](../basic_json/get_to.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::get_to`](../basic_json/get_to.md) - the corresponding function of `basic_json`
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ still refers to it — including ones taken before the change — reads the new
|
|||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
|
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
|
||||||
- [`basic_json_document`](../basic_json_document/index.md) - the document that owns the parsed data
|
- [`basic_json_document`](../basic_json_document/index.md) - the document that owns the parsed data
|
||||||
- [`json_view`](../json_view.md) - and [`json_editable_view`](../json_editable_view.md) - the default specializations
|
- [`json_view`](../json_view.md) - and [`json_editable_view`](../json_editable_view.md) - the default specializations
|
||||||
- [`materialize`](materialize.md) - build a `basic_json` value from a view
|
- [`materialize`](materialize.md) - build a `basic_json` value from a view
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ A `basic_json_view` is not convertible to `#!cpp bool`: such a conversion would
|
|||||||
- [`(constructor)`](basic_json_view.md) - the default constructor creates a discarded view
|
- [`(constructor)`](basic_json_view.md) - the default constructor creates a discarded view
|
||||||
- [`basic_json_document::is_discarded`](../basic_json_document/is_discarded.md) - return whether the last parse failed
|
- [`basic_json_document::is_discarded`](../basic_json_document/is_discarded.md) - return whether the last parse failed
|
||||||
- [`BasicJsonType::is_discarded`](../basic_json/is_discarded.md) - the corresponding function of `basic_json`
|
- [`BasicJsonType::is_discarded`](../basic_json/is_discarded.md) - the corresponding function of `basic_json`
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#what-is-different) - how views differ from basic_json
|
- [Zero-copy JSON views](../../features/json_view/differences.md) - how views differ from basic_json
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ never caches the result.
|
|||||||
- [`root`](../basic_json_document/root.md) - the view of a document's root value
|
- [`root`](../basic_json_document/root.md) - the view of a document's root value
|
||||||
- [`BasicJsonType::parse`](../basic_json/parse.md) - build a `BasicJsonType` value directly from a JSON text
|
- [`BasicJsonType::parse`](../basic_json/parse.md) - build a `BasicJsonType` value directly from a JSON text
|
||||||
- [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) - source positions on parsed values
|
- [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) - source positions on parsed values
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - when to build a basic_json value from a view
|
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - when to build a basic_json value from a view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ source
|
|||||||
- [`number_token`](number_token.md) - get a single number's token text without dumping the whole value
|
- [`number_token`](number_token.md) - get a single number's token text without dumping the whole value
|
||||||
- [`BasicJsonType::error_handler_t`](../basic_json/error_handler_t.md) - the analogous enumeration for
|
- [`BasicJsonType::error_handler_t`](../basic_json/error_handler_t.md) - the analogous enumeration for
|
||||||
`BasicJsonType::dump`'s decoding-error behavior
|
`BasicJsonType::dump`'s decoding-error behavior
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing numbers as they appeared in the source
|
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing numbers as they appeared in the source
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ buffer — and is valid exactly as long as that text is, see the [validity rules
|
|||||||
- [`BasicJsonType::number_integer_t`](../basic_json/number_integer_t.md),
|
- [`BasicJsonType::number_integer_t`](../basic_json/number_integer_t.md),
|
||||||
[`number_unsigned_t`](../basic_json/number_unsigned_t.md), [`number_float_t`](../basic_json/number_float_t.md) - the
|
[`number_unsigned_t`](../basic_json/number_unsigned_t.md), [`number_float_t`](../basic_json/number_float_t.md) - the
|
||||||
number types `#!cpp get<T>()` and `materialize()` convert into
|
number types `#!cpp get<T>()` and `materialize()` convert into
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - reading a number exactly as written
|
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - reading a number exactly as written
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ document.
|
|||||||
[`materialize()`](materialize.md) or iterate the members with [`items()`](items.md) and keep the last match.
|
[`materialize()`](materialize.md) or iterate the members with [`items()`](items.md) and keep the last match.
|
||||||
The key hash index of a larger object (128 members or more) leads to the first member of a key as well.
|
The key hash index of a larger object (128 members or more) leads to the first member of a key as well.
|
||||||
[`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
|
[`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
|
||||||
duplicates, in document order. See [Duplicate keys](../../features/json_view.md#duplicate-keys) and
|
duplicates, in document order. See [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) and
|
||||||
[`size()`](size.md#notes).
|
[`size()`](size.md#notes).
|
||||||
|
|
||||||
!!! info "JSON pointer resolution"
|
!!! info "JSON pointer resolution"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Neither overload builds a `BasicJsonType` value for a view to do the comparison
|
|||||||
compare by value across their types (`#!cpp 1 == 1.0`), and an object compares by its members, with duplicate keys
|
compare by value across their types (`#!cpp 1 == 1.0`), and an object compares by its members, with duplicate keys
|
||||||
resolved exactly as `parse()` resolves them — the last value, at the position of the first occurrence of the key.
|
resolved exactly as `parse()` resolves them — the last value, at the position of the first occurrence of the key.
|
||||||
This is not what [`operator[]`](operator[].md) and the other lookups return for a duplicate key: they find the *first*
|
This is not what [`operator[]`](operator[].md) and the other lookups return for a duplicate key: they find the *first*
|
||||||
member (see [Duplicate keys](../../features/json_view.md#duplicate-keys)).
|
member (see [Duplicate keys](../../features/json_view/differences.md#duplicate-keys)).
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ Linear, as [`dump`](dump.md#complexity).
|
|||||||
- [`dump`](dump.md) - serialize to a JSON-formatted string
|
- [`dump`](dump.md) - serialize to a JSON-formatted string
|
||||||
- [`operator<<(std::ostream&)`](../operator_ltlt.md) - the corresponding operator for `basic_json`
|
- [`operator<<(std::ostream&)`](../operator_ltlt.md) - the corresponding operator for `basic_json`
|
||||||
- [`JSON_NO_IO`](../macros/json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
- [`JSON_NO_IO`](../macros/json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
|
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ string, whose token length is not the same as its decoded [`size()`](size.md).
|
|||||||
|
|
||||||
- [`source`](../basic_json_document/source.md) - the parsed text
|
- [`source`](../basic_json_document/source.md) - the parsed text
|
||||||
- [`is_string`](is_string.md) - return whether the value is a string
|
- [`is_string`](is_string.md) - return whether the value is a string
|
||||||
- [Zero-copy JSON views](../../features/json_view.md#images) - images and the source text
|
- [Zero-copy JSON views](../../features/json_view/images.md) - images and the source text
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ Pointer path of the value within the document, and with
|
|||||||
|
|
||||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||||
the last value.
|
the last value.
|
||||||
|
|
||||||
!!! info "Differences to `at` and `operator[]`"
|
!!! info "Differences to `at` and `operator[]`"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ view class.
|
|||||||
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
|
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
|
||||||
- [get](basic_json_view/get.md) - which conversions read the view directly
|
- [get](basic_json_view/get.md) - which conversions read the view directly
|
||||||
- [view_mode](view_mode.md) - read-only or editable
|
- [view_mode](view_mode.md) - read-only or editable
|
||||||
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions and `from_json()` for views
|
- [Writing generic code](../features/json_view/generic_code.md) - functions and `from_json()` for views
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ specialization.
|
|||||||
|
|
||||||
- [`json_view`](json_view.md) - a view of a value of a `json_document`
|
- [`json_view`](json_view.md) - a view of a value of a `json_document`
|
||||||
- [`ordered_json_document`](ordered_json_document.md) - the corresponding document for `ordered_json`
|
- [`ordered_json_document`](ordered_json_document.md) - the corresponding document for `ordered_json`
|
||||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ This type is an **editable** [`basic_json_document`](basic_json_document/index.m
|
|||||||
[`json`](json.md) specialization: in addition to everything [`json_document`](json_document.md) offers,
|
[`json`](json.md) specialization: in addition to everything [`json_document`](json_document.md) offers,
|
||||||
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) change values after
|
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) change values after
|
||||||
parsing, without ever rewriting the source text — see [Edits](basic_json_document/index.md#edits) and
|
parsing, without ever rewriting the source text — see [Edits](basic_json_document/index.md#edits) and
|
||||||
[Editing a document](../features/json_view.md#editing-a-document).
|
[Editing a document](../features/json_view/editing.md).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ still referring to it seeing the change, see [Edits](basic_json_document/index.m
|
|||||||
- [`json_view`](json_view.md) - the corresponding read-only view
|
- [`json_view`](json_view.md) - the corresponding read-only view
|
||||||
- [`ordered_json_editable_view`](ordered_json_editable_view.md) - the corresponding view for
|
- [`ordered_json_editable_view`](ordered_json_editable_view.md) - the corresponding view for
|
||||||
`ordered_json_editable_document`
|
`ordered_json_editable_document`
|
||||||
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
|
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ struct json_sax;
|
|||||||
|
|
||||||
This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in
|
This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in
|
||||||
different situations while the input is parsed. The boolean return value informs the parser whether to continue
|
different situations while the input is parsed. The boolean return value informs the parser whether to continue
|
||||||
processing the input; for [`parse_error`](parse_error.md), it decides whether to
|
processing the input.
|
||||||
[recover from the error](../../features/parsing/error_recovery.md).
|
|
||||||
|
|
||||||
For instance, parsing the JSON text `{"a": [1, true]}` triggers the following callbacks, in order:
|
For instance, parsing the JSON text `{"a": [1, true]}` triggers the following callbacks, in order:
|
||||||
|
|
||||||
|
|||||||
@@ -21,18 +21,11 @@ A parse error occurred.
|
|||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
Whether to recover from the error:
|
Whether parsing should proceed (**must return `#!cpp false`**).
|
||||||
|
|
||||||
- `#!cpp false` stops parsing.
|
|
||||||
- `#!cpp true` recovers from the error: the error is repaired and parsing continues. If that is not possible, which
|
|
||||||
happens in the binary formats when the end of the item with the error is unknown, the value read so far is completed
|
|
||||||
and parsing stops. See [error recovery](../../features/parsing/error_recovery.md) for how errors are repaired.
|
|
||||||
|
|
||||||
Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example "Example: (1) the SAX interface"
|
??? example
|
||||||
|
|
||||||
The example below shows how the SAX interface is used.
|
The example below shows how the SAX interface is used.
|
||||||
|
|
||||||
@@ -46,30 +39,13 @@ Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
|
|||||||
--8<-- "examples/sax_parse.output"
|
--8<-- "examples/sax_parse.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Example: (2) recovering from errors"
|
|
||||||
|
|
||||||
The example below shows how a SAX parser recovers from errors.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/sax_parse__error_recovery.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```
|
|
||||||
--8<-- "examples/sax_parse__error_recovery.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [`sax_parse`](../basic_json/sax_parse.md) - SAX parser
|
- [`sax_parse`](../basic_json/sax_parse.md) - SAX parser
|
||||||
- [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without
|
- [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without
|
||||||
exceptions
|
exceptions
|
||||||
- [Error Recovery](../../features/parsing/error_recovery.md) - the article on recovering from parse errors
|
|
||||||
- [SAX Interface](../../features/parsing/sax_interface.md) - guide to event-based parsing
|
- [SAX Interface](../../features/parsing/sax_interface.md) - guide to event-based parsing
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.2.0.
|
- Added in version 3.2.0.
|
||||||
- Returning `#!cpp true` recovers from the error since version 3.13.0; before, parsing stopped, but the result of
|
|
||||||
[`sax_parse`](../basic_json/sax_parse.md) could be wrong.
|
|
||||||
@@ -28,7 +28,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`j
|
|||||||
|
|
||||||
- [`json_document`](json_document.md) - the document type this view refers into
|
- [`json_document`](json_document.md) - the document type this view refers into
|
||||||
- [`ordered_json_view`](ordered_json_view.md) - the corresponding view for `ordered_json_document`
|
- [`ordered_json_view`](ordered_json_view.md) - the corresponding view for `ordered_json_document`
|
||||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ or quotes.
|
|||||||
Note that enabling this macro increases the size of every JSON value by two `std::size_t` fields and adds slight runtime
|
Note that enabling this macro increases the size of every JSON value by two `std::size_t` fields and adds slight runtime
|
||||||
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
|
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
|
||||||
these values to be reported in those error messages.
|
these values to be reported in those error messages.
|
||||||
|
In addition, `parse()`, `accept()`, and `sax_parse()` no longer use the
|
||||||
|
[faster path for input in one block of memory](../../features/parsing/index.md#input-sources-and-performance), so
|
||||||
|
parsing a string or a buffer is slower than without the macro.
|
||||||
|
|
||||||
If [`JSON_DIAGNOSTICS`](json_diagnostics.md) is also set, the message contains the path and the byte range, such as
|
If [`JSON_DIAGNOSTICS`](json_diagnostics.md) is also set, the message contains the path and the byte range, such as
|
||||||
`(/1) (bytes 4-8)`. An exception about the root value has no path and only the range, such as `(bytes 0-4)`.
|
`(/1) (bytes 4-8)`. An exception about the root value has no path and only the range, such as `(bytes 0-4)`.
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ The default value is `0`: streams are scanned in bulk.
|
|||||||
!!! note "Performance"
|
!!! note "Performance"
|
||||||
|
|
||||||
Parsing from a `std::ifstream` or `std::istringstream` is typically 1.5 to 2 times as fast as before, and reading
|
Parsing from a `std::ifstream` or `std::istringstream` is typically 1.5 to 2 times as fast as before, and reading
|
||||||
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream is then within
|
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream then takes about
|
||||||
about 10 to 20 % of first reading the whole file into a `std::string` and parsing that.
|
1.15 to 1.45 times as long as first reading the whole file into a `std::string` and parsing that.
|
||||||
|
|
||||||
A stream buffer without a get area (one that only overrides `underflow()` and `uflow()` and hands out every
|
A stream buffer without a get area (one that only overrides `underflow()` and `uflow()` and hands out every
|
||||||
character from there) offers nothing to scan in bulk, so it is read one character at a time as before.
|
character from there) offers nothing to scan in bulk, so it is read one character at a time as before.
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ By default, `#!cpp JSON_NO_SIMD` is not defined, and vector instructions are use
|
|||||||
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance) - how input
|
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance) - how input
|
||||||
sources affect parsing performance
|
sources affect parsing performance
|
||||||
- [Serialization](../../features/serialization.md) - pretty-printing, UTF-8 handling, and number output
|
- [Serialization](../../features/serialization.md) - pretty-printing, UTF-8 handling, and number output
|
||||||
- [Zero-copy JSON views](../../features/json_view.md) - the zero-copy view, whose parser the macro also affects
|
- [Zero-copy JSON views](../../features/json_view/index.md) - the zero-copy view, whose parser the macro also affects
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ of object keys, instead of sorting them like [`json_document`](json_document.md)
|
|||||||
- [`ordered_json_view`](ordered_json_view.md) - a view of a value of an `ordered_json_document`
|
- [`ordered_json_view`](ordered_json_view.md) - a view of a value of an `ordered_json_document`
|
||||||
- [`json_document`](json_document.md) - the corresponding document for the default `json` specialization
|
- [`json_document`](json_document.md) - the corresponding document for the default `json` specialization
|
||||||
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
|
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
|
||||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ members [`set`](basic_json_document/set.md) added — instead of sorting them li
|
|||||||
specialization
|
specialization
|
||||||
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
|
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
|
||||||
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
|
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
|
||||||
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
|
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
|
|||||||
- [`ordered_json_editable_document`](ordered_json_editable_document.md) - the document type this view refers into
|
- [`ordered_json_editable_document`](ordered_json_editable_document.md) - the document type this view refers into
|
||||||
- [`ordered_json_view`](ordered_json_view.md) - the corresponding read-only view
|
- [`ordered_json_view`](ordered_json_view.md) - the corresponding read-only view
|
||||||
- [`json_editable_view`](json_editable_view.md) - the corresponding view for `json_editable_document`
|
- [`json_editable_view`](json_editable_view.md) - the corresponding view for `json_editable_document`
|
||||||
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
|
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
|
|||||||
|
|
||||||
- [`ordered_json_document`](ordered_json_document.md) - the document type this view refers into
|
- [`ordered_json_document`](ordered_json_document.md) - the document type this view refers into
|
||||||
- [`json_view`](json_view.md) - the corresponding view for `json_document`
|
- [`json_view`](json_view.md) - the corresponding view for `json_document`
|
||||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ nor a [`json_editable_view`](json_editable_view.md). Views of the two modes comp
|
|||||||
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
|
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
|
||||||
- [json_editable_document](json_editable_document.md) - an editable document of `json`
|
- [json_editable_document](json_editable_document.md) - an editable document of `json`
|
||||||
- [(constructor)](basic_json_view/basic_json_view.md) - the conversion from a read-only to an editable view
|
- [(constructor)](basic_json_view/basic_json_view.md) - the conversion from a read-only to an editable view
|
||||||
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions that accept views
|
- [Writing generic code](../features/json_view/generic_code.md) - functions that accept views
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -759,9 +759,14 @@ body:has(.nj-hero) .md-content {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* blog posts: author and date right below the title */
|
||||||
|
.md-typeset .nj-byline { margin: -.8rem 0 1.6rem; font-size: .75rem; color: var(--md-default-fg-color--light); }
|
||||||
|
.md-typeset .nj-byline a { color: inherit; }
|
||||||
|
.md-typeset .nj-byline a:hover { color: var(--md-accent-fg-color); }
|
||||||
|
|
||||||
/* figure captions: "Figure N." in bold, close to the figure, set in from the text column; figures and tables are
|
/* figure captions: "Figure N." in bold, close to the figure, set in from the text column; figures and tables are
|
||||||
numbered per page, each with its own counter */
|
numbered per page, each with its own counter */
|
||||||
.md-content__inner { counter-reset: nj-figure nj-table; }
|
.md-content__inner { counter-reset: nj-figure nj-table nj-listing; }
|
||||||
.md-typeset :is(.nj-caption, .nj-charts-caption) {
|
.md-typeset :is(.nj-caption, .nj-charts-caption) {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
margin: .3rem 2rem 1.5rem;
|
margin: .3rem 2rem 1.5rem;
|
||||||
@@ -782,6 +787,25 @@ body:has(.nj-hero) .md-content {
|
|||||||
.md-typeset :is(.nj-caption, .nj-charts-caption) { margin-inline: 1rem; }
|
.md-typeset :is(.nj-caption, .nj-charts-caption) { margin-inline: 1rem; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* code captions: "Listing N." in bold, directly below the code block, in the figure caption style */
|
||||||
|
.md-typeset .nj-code-caption {
|
||||||
|
max-width: none;
|
||||||
|
margin: -.4rem 2rem 1.5rem;
|
||||||
|
font-size: .75rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: left;
|
||||||
|
color: var(--md-default-fg-color--light);
|
||||||
|
}
|
||||||
|
.md-typeset .nj-code-caption::before {
|
||||||
|
counter-increment: nj-listing;
|
||||||
|
content: "Listing " counter(nj-listing) ". ";
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--md-default-fg-color);
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 44.9375em) {
|
||||||
|
.md-typeset .nj-code-caption { margin-inline: 1rem; }
|
||||||
|
}
|
||||||
|
|
||||||
/* table captions: "Table N." in bold, directly below the table, in the figure caption style */
|
/* table captions: "Table N." in bold, directly below the table, in the figure caption style */
|
||||||
.md-typeset .nj-table-caption {
|
.md-typeset .nj-table-caption {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
|
|
||||||
.md-typeset svg.nj-charts--compile, .md-typeset svg.nj-charts--scaling { max-width: 26rem; }
|
.md-typeset svg.nj-charts--compile, .md-typeset svg.nj-charts--scaling { max-width: 26rem; }
|
||||||
.md-typeset svg.nj-charts--sizes { max-width: 26rem; }
|
.md-typeset svg.nj-charts--sizes { max-width: 26rem; }
|
||||||
|
/* home/blog/how_3_13_0_got_faster.md (scripts/figures/perf_blog.py) */
|
||||||
|
.md-typeset svg.nj-charts--blog { max-width: 34rem; }
|
||||||
|
.md-typeset svg.nj-charts--swar { max-width: 34rem; }
|
||||||
|
.md-typeset svg.nj-charts--timeline { max-width: 22rem; }
|
||||||
|
|
||||||
.nj-charts text { fill: currentColor; font-size: 13px; }
|
.nj-charts text { fill: currentColor; font-size: 13px; }
|
||||||
.nj-charts .nj-charts-leg { font-size: 13.5px; }
|
.nj-charts .nj-charts-leg { font-size: 13.5px; }
|
||||||
@@ -43,10 +47,34 @@
|
|||||||
paint-order: stroke; stroke: var(--md-default-bg-color); stroke-width: 4px; stroke-linejoin: round; }
|
paint-order: stroke; stroke: var(--md-default-bg-color); stroke-width: 4px; stroke-linejoin: round; }
|
||||||
.nj-charts .nj-charts-factor { fill: var(--nj-charts-ink2); font-size: 12px; font-weight: 600; text-anchor: middle; }
|
.nj-charts .nj-charts-factor { fill: var(--nj-charts-ink2); font-size: 12px; font-weight: 600; text-anchor: middle; }
|
||||||
.nj-charts .nj-charts-note { fill: var(--nj-charts-ink2); font-size: 12px; }
|
.nj-charts .nj-charts-note { fill: var(--nj-charts-ink2); font-size: 12px; }
|
||||||
|
/* heatmap cells (perf_blog_usecases_*.svg): green for the fastest, gray around twice the time, red for the slowest;
|
||||||
|
adjacent steps checked for CVD separation on the light and the slate surface; the numbers are in every cell */
|
||||||
|
.md-typeset svg.nj-charts--heat { max-width: 49rem; }
|
||||||
|
.nj-charts--heat { --nj-heat-0: #3fa557; --nj-heat-1: #8bcc95; --nj-heat-2: #ebeae6; --nj-heat-3: #f2ae9b;
|
||||||
|
--nj-heat-4: #dc5a43; --nj-heat-ink: #1b1b1b; --nj-heat-ink-strong: #1b1b1b; }
|
||||||
|
[data-md-color-scheme="slate"] .nj-charts--heat { --nj-heat-0: #3a9d50; --nj-heat-1: #2c6b3a; --nj-heat-2: #33353a;
|
||||||
|
--nj-heat-3: #8a3f2e; --nj-heat-4: #d95b3a; --nj-heat-ink: #e8e8e6; --nj-heat-ink-strong: #141414; }
|
||||||
|
.nj-charts .nj-heat-0 { fill: var(--nj-heat-0); }
|
||||||
|
.nj-charts .nj-heat-1 { fill: var(--nj-heat-1); }
|
||||||
|
.nj-charts .nj-heat-2 { fill: var(--nj-heat-2); }
|
||||||
|
.nj-charts .nj-heat-3 { fill: var(--nj-heat-3); }
|
||||||
|
.nj-charts .nj-heat-4 { fill: var(--nj-heat-4); }
|
||||||
|
.nj-charts .nj-heat-na { fill: none; stroke: var(--nj-charts-grid); stroke-width: 1; }
|
||||||
|
.nj-charts .nj-charts-cell { fill: var(--nj-heat-ink); font-size: 12px; font-variant-numeric: tabular-nums; }
|
||||||
|
.nj-charts :is(.nj-heat-0, .nj-heat-4) ~ .nj-charts-cell { fill: var(--nj-heat-ink-strong); }
|
||||||
|
.nj-charts .nj-charts-cell--small { font-size: 10.5px; }
|
||||||
|
.nj-charts .nj-heat-cell { cursor: default; outline: none; }
|
||||||
|
.nj-charts .nj-heat-cell:is(:hover, :focus-visible, .nj-heat-on) rect { stroke: var(--md-default-fg-color); stroke-width: 1.5; }
|
||||||
|
.nj-heat-tip { position: absolute; z-index: 5; max-width: 24rem; padding: .5rem .7rem; border-radius: 6px;
|
||||||
|
background: var(--md-default-bg-color); color: var(--md-default-fg-color);
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, .25); font-size: .68rem; line-height: 1.45; pointer-events: none; }
|
||||||
|
.nj-heat-tip strong { display: block; margin-bottom: .2rem; }
|
||||||
|
.nj-heat-tip[hidden] { display: none; }
|
||||||
|
|
||||||
.nj-charts .nj-charts-grid { stroke: var(--nj-charts-grid); stroke-width: 1; }
|
.nj-charts .nj-charts-grid { stroke: var(--nj-charts-grid); stroke-width: 1; }
|
||||||
.nj-charts .nj-charts-zero, .nj-charts .nj-charts-axis { stroke: var(--nj-charts-base); stroke-width: 1.5; }
|
.nj-charts .nj-charts-zero, .nj-charts .nj-charts-axis { stroke: var(--nj-charts-base); stroke-width: 1.5; }
|
||||||
.nj-charts .nj-charts-ref { stroke: currentColor; stroke-width: 1.5; }
|
.nj-charts .nj-charts-ref { stroke: currentColor; stroke-width: 1.5; }
|
||||||
|
.nj-charts .nj-charts-head { fill: var(--nj-charts-base); }
|
||||||
|
|
||||||
.nj-charts .nj-charts-c1 { fill: var(--nj-charts-c1); }
|
.nj-charts .nj-charts-c1 { fill: var(--nj-charts-c1); }
|
||||||
.nj-charts .nj-charts-c2 { fill: var(--nj-charts-c2); }
|
.nj-charts .nj-charts-c2 { fill: var(--nj-charts-c2); }
|
||||||
@@ -140,3 +168,19 @@
|
|||||||
/* direct line labels replace the legend */
|
/* direct line labels replace the legend */
|
||||||
.nj-charts .nj-charts-name-json { fill: var(--nj-charts-json-c); font-weight: 600; }
|
.nj-charts .nj-charts-name-json { fill: var(--nj-charts-json-c); font-weight: 600; }
|
||||||
.nj-charts .nj-charts-name-ojson { fill: var(--nj-charts-ojson-c); font-weight: 600; }
|
.nj-charts .nj-charts-name-ojson { fill: var(--nj-charts-ojson-c); font-weight: 600; }
|
||||||
|
|
||||||
|
/* theme badges of home/blog/how_3_13_0_got_faster.md: the number of one of the eight themes, as a digit in a blue
|
||||||
|
circle, in headings, text, tables and the timeline figures (nj-theme-dot); "U" and other marks are gray */
|
||||||
|
.md-typeset .nj-theme { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
|
||||||
|
min-width: 1.45em; height: 1.45em; padding: 0 .3em; border-radius: .725em; vertical-align: .08em;
|
||||||
|
background: var(--nj-blue); color: var(--md-default-bg-color); font-size: .78em; font-weight: 700; line-height: 1;
|
||||||
|
font-variant-numeric: tabular-nums; text-decoration: none; }
|
||||||
|
.md-typeset a.nj-theme:is(:hover, :focus-visible) { background: var(--nj-blue-deep); color: var(--md-default-bg-color); }
|
||||||
|
.md-typeset .nj-theme--other { background: var(--md-default-fg-color--light); }
|
||||||
|
.md-typeset h2 .nj-theme { margin-right: .35em; vertical-align: .12em; }
|
||||||
|
.md-typeset .nj-theme-list > ul { list-style: none; margin-left: 0; }
|
||||||
|
.md-typeset .nj-theme-list > ul > li { margin-left: 0; padding-left: 2em; text-indent: -2em; }
|
||||||
|
.md-typeset .nj-theme-list > ul > li > .nj-theme { margin-right: .55em; text-indent: 0; }
|
||||||
|
.nj-charts .nj-theme-dot { fill: var(--nj-blue); }
|
||||||
|
.nj-charts .nj-theme-dot--other { fill: var(--nj-charts-ink2); }
|
||||||
|
.nj-charts .nj-theme-num { fill: var(--md-default-bg-color); font-size: 11px; font-weight: 700; }
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* figures: jsonview (features/json_view.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
|
/* figures: jsonview (features/json_view/*.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
|
||||||
|
|
||||||
/* one colour per concept, the same in every figure: structure (object/array, views) blue, keys navy, strings green,
|
/* one colour per concept, the same in every figure: structure (object/array, views) blue, keys navy, strings green,
|
||||||
numbers orange, decoded strings purple, owned edit storage teal, dangling/invalid red */
|
numbers orange, decoded strings purple, owned edit storage teal, dangling/invalid red */
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
// a SAX parser that creates a JSON value like json::parse does, but that
|
|
||||||
// recovers from parse errors instead of stopping at the first one
|
|
||||||
class recovering_parser : public nlohmann::detail::json_sax_dom_parser<json>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit recovering_parser(json& result)
|
|
||||||
: nlohmann::detail::json_sax_dom_parser<json>(result, false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool parse_error(std::size_t position,
|
|
||||||
const std::string& /*last_token*/,
|
|
||||||
const json::exception& ex)
|
|
||||||
{
|
|
||||||
std::cout << "byte " << position << ": " << ex.what() << '\n';
|
|
||||||
|
|
||||||
// repair the input and continue
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// JSON text with several mistakes that ends too early
|
|
||||||
const std::string text = R"({
|
|
||||||
"name": "Hello World",
|
|
||||||
"tags": ["a" "b",],
|
|
||||||
"valid": tru,
|
|
||||||
"size": 1.,
|
|
||||||
"nested": {"x": 1)";
|
|
||||||
|
|
||||||
json result;
|
|
||||||
recovering_parser sax(result);
|
|
||||||
const bool valid = json::sax_parse(text, &sax);
|
|
||||||
|
|
||||||
std::cout << "\nvalid JSON: " << std::boolalpha << valid << '\n'
|
|
||||||
<< std::setw(4) << result << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
byte 49: [json.exception.parse_error.101] parse error at line 3, column 20: syntax error while parsing array - unexpected string literal; expected ']'
|
|
||||||
byte 51: [json.exception.parse_error.101] parse error at line 3, column 22: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
|
|
||||||
byte 70: [json.exception.parse_error.101] parse error at line 4, column 17: syntax error while parsing value - invalid literal; last read: '"valid": tru,'
|
|
||||||
byte 86: [json.exception.parse_error.101] parse error at line 5, column 15: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.,'
|
|
||||||
byte 109: [json.exception.parse_error.101] parse error at line 6, column 22: syntax error while parsing object - unexpected end of input; expected '}'
|
|
||||||
|
|
||||||
valid JSON: false
|
|
||||||
{
|
|
||||||
"name": "Hello World",
|
|
||||||
"nested": {
|
|
||||||
"x": 1
|
|
||||||
},
|
|
||||||
"size": 1,
|
|
||||||
"tags": [
|
|
||||||
"a",
|
|
||||||
"b"
|
|
||||||
],
|
|
||||||
"valid": null
|
|
||||||
}
|
|
||||||
@@ -48,7 +48,7 @@ which avoids repeating it:
|
|||||||
[`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other
|
[`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other
|
||||||
type is obtained from a subtree that is built on demand with
|
type is obtained from a subtree that is built on demand with
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md). See
|
[`materialize()`](../api/basic_json_view/materialize.md). See
|
||||||
[Zero-copy JSON views](json_view.md#getting-values-out-without-copying).
|
[Zero-copy JSON views](json_view/reading.md#getting-values-out-without-copying).
|
||||||
|
|
||||||
The library already knows how to convert to and from the scalar types and the STL containers (such as
|
The library already knows how to convert to and from the scalar types and the STL containers (such as
|
||||||
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
|
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
|
||||||
@@ -269,7 +269,7 @@ The conversions above are built in for standard types. To make the same syntax w
|
|||||||
- [`get_ref`](../api/basic_json/get_ref.md) - / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
|
- [`get_ref`](../api/basic_json/get_ref.md) - / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
|
||||||
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
|
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
|
||||||
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
|
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
|
||||||
- [Zero-copy JSON views](json_view.md) - get values out of a document or a view
|
- [Zero-copy JSON views](json_view/index.md) - get values out of a document or a view
|
||||||
- [`adl_serializer`](../api/adl_serializer/index.md) - the default serializer behind the conversions
|
- [`adl_serializer`](../api/adl_serializer/index.md) - the default serializer behind the conversions
|
||||||
- [`type`](../api/basic_json/type.md) - query the type of a value
|
- [`type`](../api/basic_json/type.md) - query the type of a value
|
||||||
- [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) - toggle conversion to tuples of references
|
- [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) - toggle conversion to tuples of references
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
There are several ways to create a JSON value in memory. This page gives an overview; to read a value from JSON text
|
There are several ways to create a JSON value in memory. This page gives an overview; to read a value from JSON text
|
||||||
instead, see [parsing](parsing/index.md). A JSON text can also be parsed into a
|
instead, see [parsing](parsing/index.md). A JSON text can also be parsed into a
|
||||||
[document](../api/basic_json_document/index.md) and read through a [view](../api/basic_json_view/index.md) instead of
|
[document](../api/basic_json_document/index.md) and read through a [view](../api/basic_json_view/index.md) instead of
|
||||||
into a value; see [Zero-copy JSON views](json_view.md).
|
into a value; see [Zero-copy JSON views](json_view/index.md).
|
||||||
|
|
||||||
## From C++ values
|
## From C++ values
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ string `#!json "42"`.
|
|||||||
- [`operator""_json`](../api/operator_literal_json.md) - the `_json` literal
|
- [`operator""_json`](../api/operator_literal_json.md) - the `_json` literal
|
||||||
- [Converting values](conversions.md) - which C++ types can be used
|
- [Converting values](conversions.md) - which C++ types can be used
|
||||||
- [Parsing](parsing/index.md) - create a value from JSON text
|
- [Parsing](parsing/index.md) - create a value from JSON text
|
||||||
- [Zero-copy JSON views](json_view.md) - create a document and a view from JSON text instead of a value
|
- [Zero-copy JSON views](json_view/index.md) - create a document and a view from JSON text instead of a value
|
||||||
- [`basic_json`](../api/basic_json/index.md) - the class template and its member functions
|
- [`basic_json`](../api/basic_json/index.md) - the class template and its member functions
|
||||||
- [Types](types/index.md) - the JSON value types
|
- [Types](types/index.md) - the JSON value types
|
||||||
- [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) - change how a single braced value is interpreted
|
- [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) - change how a single braced value is interpreted
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ json read_json_file(const std::string& path)
|
|||||||
column 1 instead of saying that the file is missing. A relative path is resolved against the working directory of the
|
column 1 instead of saying that the file is missing. A relative path is resolved against the working directory of the
|
||||||
running program, which is often not the directory of the source file (see the
|
running program, which is often not the directory of the source file (see the
|
||||||
[FAQ](../home/faq.md#parsing-a-file-fails-with-unexpected-end-of-input)).
|
[FAQ](../home/faq.md#parsing-a-file-fails-with-unexpected-end-of-input)).
|
||||||
- **Don't read the file into a string first.** Parsing from a `#!cpp std::ifstream` is only about 10 to 20 % slower
|
- **Read the file into a string only if the memory is no concern.** Parsing from a `#!cpp std::ifstream` takes about
|
||||||
than reading the whole file into a string with one `read()` and parsing that, and it needs no memory for the copy (see
|
1.15 to 1.45 times as long as reading the whole file into a string with one `read()` and parsing that, but it needs no
|
||||||
[input sources and performance](parsing/index.md#input-sources-and-performance)). The common way to read a file with
|
memory for the copy (see [input sources and performance](parsing/index.md#input-sources-and-performance)). The common
|
||||||
`#!cpp std::istreambuf_iterator<char>` is so slow that it takes about twice as long as parsing from the stream. Read
|
way to read a file with `#!cpp std::istreambuf_iterator<char>` is so slow that it takes about twice as long as parsing
|
||||||
the file into a string only if you need the text itself, for example for an error message. This advice is for
|
from the stream. This advice is for
|
||||||
`#!cpp json::parse`. [`json_document`](json_view.md) reads a stream into a buffer that it owns anyway, so there is no
|
`#!cpp json::parse`. [`json_document`](json_view/index.md) reads a stream into a buffer that it owns anyway, so there is no
|
||||||
copy to save: reading the file into a `#!cpp std::string` yourself and passing that as an lvalue, which the document
|
copy to save: reading the file into a `#!cpp std::string` yourself and passing that as an lvalue, which the document
|
||||||
borrows, was about 6 to 12 % faster than parsing from the stream, with the same single buffer.
|
borrows, was about 6 to 12 % faster than parsing from the stream, with the same single buffer.
|
||||||
- `parse` requires the file to hold exactly one JSON value. Files with one value per line are read with
|
- `parse` requires the file to hold exactly one JSON value. Files with one value per line are read with
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ C++ types, and finally serialize it again.
|
|||||||
[SAX interface](parsing/sax_interface.md), [error handling](parsing/parse_exceptions.md),
|
[SAX interface](parsing/sax_interface.md), [error handling](parsing/parse_exceptions.md),
|
||||||
[large and partial input](parsing/large_and_partial_input.md), and
|
[large and partial input](parsing/large_and_partial_input.md), and
|
||||||
[parsing untrusted input](parsing/untrusted_input.md).
|
[parsing untrusted input](parsing/untrusted_input.md).
|
||||||
- [Zero-copy JSON views](json_view.md) — read a JSON text through a flat index instead of building a `json` tree;
|
- [Zero-copy JSON views](json_view/index.md) — read a JSON text through a flat index instead of building a `json` tree;
|
||||||
strings and numbers stay in the input and are only decoded when needed.
|
strings and numbers stay in the input and are only decoded when needed.
|
||||||
- [Editable documents](json_view.md#editing-a-document) — modify a parsed document without building a `json` tree.
|
- [Editable documents](json_view/editing.md) — modify a parsed document without building a `json` tree.
|
||||||
- [Images](json_view.md#images) — save a parsed document and load it again without parsing it.
|
- [Images](json_view/images.md) — save a parsed document and load it again without parsing it.
|
||||||
- [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar.
|
- [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar.
|
||||||
|
|
||||||
## Accessing and modifying values
|
## Accessing and modifying values
|
||||||
|
|||||||
@@ -1,591 +0,0 @@
|
|||||||
# Zero-copy JSON views
|
|
||||||
|
|
||||||
`#!cpp <nlohmann/json_view.hpp>` adds a read-only, non-owning way to look at a parsed JSON text, as an alternative to
|
|
||||||
building a [`basic_json`](../api/basic_json/index.md) tree with [`parse()`](../api/basic_json/parse.md).
|
|
||||||
|
|
||||||
## The problem
|
|
||||||
|
|
||||||
[`basic_json::parse()`](../api/basic_json/parse.md) builds a tree of `basic_json` values: one allocation for every
|
|
||||||
array and object, and every string copied into its own `std::string`. That is the right trade-off when the program
|
|
||||||
goes on to read and write the value freely, but it does more work than necessary when only a small part of a large
|
|
||||||
JSON text is actually needed, or when the same text is parsed over and over (many small messages, for instance) and
|
|
||||||
most of the resulting tree is thrown away almost immediately.
|
|
||||||
|
|
||||||
## The idea
|
|
||||||
|
|
||||||
[`basic_json_document::parse()`](../api/basic_json_document/parse.md) parses the same JSON grammar, with the same
|
|
||||||
options, but instead of a tree it builds a flat index of the values it found: one
|
|
||||||
[16-byte entry](../home/architecture.md#node-index-of-json-views) per value (and one per object key), in document
|
|
||||||
order. Integers are converted while parsing and stored in their entry. Floating-point numbers and strings are
|
|
||||||
referenced in place in the source text: a float is converted when it is read, and only strings that contain escape
|
|
||||||
sequences are decoded, into one shared buffer owned by the document.
|
|
||||||
|
|
||||||
[`basic_json_view`](../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
|
||||||
index. It gives you the read-only, type-inspection part of the `basic_json` interface —
|
|
||||||
[`type()`](../api/basic_json_view/type.md) and the `is_*()` predicates,
|
|
||||||
[`size()`](../api/basic_json_view/size.md)/[`empty()`](../api/basic_json_view/empty.md) — as well as element access
|
|
||||||
([`operator[]`](../api/basic_json_view/operator%5B%5D.md), [`at`](../api/basic_json_view/at.md),
|
|
||||||
[`front`](../api/basic_json_view/front.md)/[`back`](../api/basic_json_view/back.md)), lookup
|
|
||||||
([`find`](../api/basic_json_view/find.md), [`contains`](../api/basic_json_view/contains.md),
|
|
||||||
[`count`](../api/basic_json_view/count.md)), and iteration
|
|
||||||
([`begin`](../api/basic_json_view/begin.md)/[`end`](../api/basic_json_view/end.md),
|
|
||||||
[`items`](../api/basic_json_view/items.md)) — without ever allocating a `basic_json` value. When you do need an
|
|
||||||
actual `basic_json` value for a subtree, [`materialize()`](../api/basic_json_view/materialize.md) builds exactly the
|
|
||||||
one [`parse()`](../api/basic_json/parse.md) would have produced for it.
|
|
||||||
|
|
||||||
The same small text, parsed both ways:
|
|
||||||
|
|
||||||
<div class="nj-fig-wrap" markdown>
|
|
||||||
--8<-- "images/json_view_layout.svg"
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="nj-caption" markdown>Top: the tree of separate heap allocations that `basic_json::parse()` builds. Bottom: `json_document::parse()` keeps the source text and builds one flat index of 16-byte nodes that point back into it, plus a buffer of decoded strings.</p>
|
|
||||||
|
|
||||||
## How to use it
|
|
||||||
|
|
||||||
Include `<nlohmann/json_view.hpp>` in addition to (or instead of) `<nlohmann/json.hpp>`. Parse into a
|
|
||||||
[`json_document`](../api/json_document.md), inspect its [`root()`](../api/basic_json_document/root.md), and
|
|
||||||
`materialize()` when you need a real value. `materialize()` works on any view, not just the root, so you can turn just
|
|
||||||
the subtree you need into a value:
|
|
||||||
|
|
||||||
??? example "Example: parse a document, inspect its root, and materialize it"
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/json_document.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
--8<-- "examples/json_document.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
[`ordered_json_document`](../api/ordered_json_document.md) is the equivalent for
|
|
||||||
[`ordered_json`](../api/ordered_json.md), just as [`ordered_json`](../api/ordered_json.md) is to
|
|
||||||
[`json`](../api/json.md).
|
|
||||||
|
|
||||||
## Ownership and lifetime
|
|
||||||
|
|
||||||
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call
|
|
||||||
[`owns_source()`](../api/basic_json_document/owns_source.md) to find out which happened.
|
|
||||||
[`parse()`](../api/basic_json_document/parse.md) decides this from the value category and type of its argument (an
|
|
||||||
lvalue `#!cpp std::string` is borrowed; an rvalue `#!cpp std::string` is moved in, owned without a copy; a stream is
|
|
||||||
read into an owned buffer; and so on — see [`parse`'s Notes](../api/basic_json_document/parse.md#notes) for the full
|
|
||||||
table). [`parse_copy()`](../api/basic_json_document/parse_copy.md) always owns a copy, regardless of the input.
|
|
||||||
|
|
||||||
!!! warning "A borrowed document depends on your buffer"
|
|
||||||
|
|
||||||
If a document borrows its text, that text **must outlive the document** (and every view taken from it) and must
|
|
||||||
stay **unmodified** for as long as the document exists, since floating-point values and unescaped strings are
|
|
||||||
read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior,
|
|
||||||
exactly as it would be for a dangling `#!cpp std::string_view`.
|
|
||||||
|
|
||||||
A view is valid only while all of the following hold:
|
|
||||||
|
|
||||||
- the document is alive,
|
|
||||||
- the document has not been re-parsed since the view was taken (with [`read()`](../api/basic_json_document/read.md)
|
|
||||||
or [`parse()`](../api/basic_json_document/parse.md) into it),
|
|
||||||
- the document has not had [`shrink_to_fit()`](../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
|
||||||
- if the document borrows its source text, that text is still alive and unmodified.
|
|
||||||
|
|
||||||
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap
|
|
||||||
allocation that keeps its address across the move. Take a fresh view from [`root()`](../api/basic_json_document/root.md)
|
|
||||||
whenever any of the other conditions above was not met.
|
|
||||||
|
|
||||||
<div class="nj-fig-wrap" markdown>
|
|
||||||
--8<-- "images/json_view_lifetime.svg"
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="nj-caption" markdown>Four timelines of a document and its views, each bar showing when an object is alive or valid and when it dangles. Destroying the borrowed text, re-parsing, or shrinking the document invalidates the views taken before; moving the document does not.</p>
|
|
||||||
|
|
||||||
A view can dangle, exactly like a `#!cpp std::string_view`: nothing stops you from using it after its document is
|
|
||||||
gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `#!cpp std::shared_ptr` in
|
|
||||||
the document and a `#!cpp std::weak_ptr` in each view) would make it larger, add reference counting to every copy,
|
|
||||||
and a `lock()` to every access — exactly the costs the view exists to avoid. What is caught is the obvious case:
|
|
||||||
because a view dies with its document, [`root()`](../api/basic_json_document/root.md) is not callable on a temporary
|
|
||||||
document: `#!cpp auto v = json_document::parse(text).root();` does not compile. Give the document a name first.
|
|
||||||
|
|
||||||
??? example "Example: borrowed and owned documents, and when views become invalid"
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/json_view_ownership.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
--8<-- "examples/json_view_ownership.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
## What is the same as `parse()`
|
|
||||||
|
|
||||||
- **Accept/reject.** [`accept()`](../api/basic_json_document/accept.md) and
|
|
||||||
[`parse()`](../api/basic_json_document/parse.md) accept and reject exactly the same inputs as
|
|
||||||
[`basic_json::accept()`](../api/basic_json/accept.md)/[`basic_json::parse()`](../api/basic_json/parse.md), with the
|
|
||||||
same `ignore_comments` and `ignore_trailing_commas` options.
|
|
||||||
- **Errors.** A failing parse throws the same exception — the same id, message, and position — because on failure
|
|
||||||
the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and
|
|
||||||
the replay builds no values: it costs about a lexing pass up to the error.
|
|
||||||
`#!cpp allow_exceptions == false` skips the replay and gives a
|
|
||||||
[discarded](../api/basic_json_document/is_discarded.md) document instead of throwing, just as it gives a discarded
|
|
||||||
value for `#!cpp basic_json::parse()`.
|
|
||||||
- **Number classification.** An integer literal that does not fit into the 64-bit integer type becomes a
|
|
||||||
floating-point value, exactly as it does for `#!cpp basic_json::parse()`.
|
|
||||||
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) and
|
|
||||||
[`JSON_NOEXCEPTION`](../api/macros/json_noexception.md)/[`JSON_THROW_USER`](../api/macros/json_throw_user.md)
|
|
||||||
behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or
|
|
||||||
how it reports errors. Two more macros add context to the exceptions of views, as they do for those of
|
|
||||||
`#!cpp basic_json`:
|
|
||||||
|
|
||||||
- With [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md), an exception names the JSON Pointer path of the
|
|
||||||
value within the document.
|
|
||||||
- With [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md), an exception names the bytes of
|
|
||||||
the value in the source text.
|
|
||||||
|
|
||||||
A view has neither parent pointers nor stored positions, so both are found from the document's index when the
|
|
||||||
exception is thrown. This takes time linear in the size of the document. One exception has neither:
|
|
||||||
`key()` of an iterator over an array or a primitive value
|
|
||||||
([`invalid_iterator.207`](../home/exceptions.md#jsonexceptioninvalid_iterator207)), because a view's iterator does
|
|
||||||
not know the value it iterates over.
|
|
||||||
|
|
||||||
The other macros only affect the values `materialize()` produces, through `BasicJsonType`.
|
|
||||||
|
|
||||||
## What is different
|
|
||||||
|
|
||||||
- **Only 64-bit integers.** `basic_json_document<BasicJsonType>` requires `BasicJsonType::number_integer_t` and
|
|
||||||
`number_unsigned_t` to both be 64 bits wide; this is a compile-time `#!cpp static_assert`.
|
|
||||||
- **A 4 GiB input limit.** An input of 4294967280 bytes (4 GiB minus 16 bytes) or more throws
|
|
||||||
[`out_of_range.416`](../home/exceptions.md#jsonexceptionout_of_range416), a limit
|
|
||||||
`#!cpp basic_json::parse()` does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits
|
|
||||||
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
|
||||||
end must still fit.
|
|
||||||
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
|
||||||
is read to its end into a buffer the document owns, and parsing starts after that. For a file, reading it into a
|
|
||||||
`#!cpp std::string` yourself and parsing that (an lvalue is borrowed) is therefore not more expensive in memory, and
|
|
||||||
was about 6 to 12 % faster than parsing from an `#!cpp std::ifstream`.
|
|
||||||
- **Fewer source positions after edits.** With
|
|
||||||
[`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) enabled,
|
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md) sets the same positions as `#!cpp basic_json::parse()`.
|
|
||||||
In an [editable document](../api/json_editable_document.md), a value written by an edit has none, and neither has
|
|
||||||
an array or object whose elements changed or whose last element has none; a `basic_json` value keeps its positions
|
|
||||||
when it is changed. See [`materialize()`](../api/basic_json_view/materialize.md#notes).
|
|
||||||
- **Objects iterate in document order.** [`begin()`](../api/basic_json_view/begin.md)/
|
|
||||||
[`end()`](../api/basic_json_view/end.md) and [`items()`](../api/basic_json_view/items.md) visit an object's
|
|
||||||
members in the order they appear in the source text. `basic_json`'s default `object_t` is a `std::map`, which
|
|
||||||
sorts by key, so iterating a [`materialize()`](../api/basic_json_view/materialize.md)d value can print members in
|
|
||||||
a different order than iterating the view they came from.
|
|
||||||
- **Chained access is safe.** [`operator[]`](../api/basic_json_view/operator%5B%5D.md) with a missing key, an index
|
|
||||||
out of range, or an unresolvable JSON pointer returns a [discarded](../api/basic_json_view/is_discarded.md) view, and
|
|
||||||
`operator[]` on a discarded view returns a discarded view without throwing: `#!cpp v["a"]["b"][0]` can be tested
|
|
||||||
once at the end. Type errors on values that exist (a key on an array, an index on an object) still throw, and
|
|
||||||
[`at`](../api/basic_json_view/at.md) throws for every missing value.
|
|
||||||
- **Duplicate keys: lookups find the first member, `parse()` keeps the last.** See [Duplicate keys](#duplicate-keys)
|
|
||||||
below.
|
|
||||||
- **Ordering comparisons are not provided** by `basic_json_view` — there is no `#!cpp operator<`.
|
|
||||||
[`operator==`](../api/basic_json_view/operator_eq.md) and [`operator!=`](../api/basic_json_view/operator_ne.md) are
|
|
||||||
provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when
|
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md) or [`parse()`](../api/basic_json/parse.md) would produce
|
|
||||||
equal values for them, without ever building a tree to do it. For ordering, too,
|
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
|
||||||
|
|
||||||
## Duplicate keys
|
|
||||||
|
|
||||||
!!! warning "A lookup in a view and in the parsed value can give different answers"
|
|
||||||
|
|
||||||
If an object in the source text repeats a key, [`operator[]`](../api/basic_json_view/operator%5B%5D.md),
|
|
||||||
[`at`](../api/basic_json_view/at.md), [`find`](../api/basic_json_view/find.md),
|
|
||||||
[`value`](../api/basic_json_view/value.md), [`contains`](../api/basic_json_view/contains.md), and JSON pointer
|
|
||||||
resolution all return the **first** member with that key. `basic_json::parse()` instead keeps the **last** value
|
|
||||||
of a repeated key, so for `#!cpp {"a":1,"a":2}`, `#!cpp view["a"]` is `#!cpp 1` while `#!cpp parse(text)["a"]` is
|
|
||||||
`#!cpp 2`. [`count`](../api/basic_json_view/count.md) counts every member with the key, so it is `#!cpp 2` here.
|
|
||||||
|
|
||||||
[`begin()`](../api/basic_json_view/begin.md)/[`end()`](../api/basic_json_view/end.md) and
|
|
||||||
[`items()`](../api/basic_json_view/items.md) visit *every* occurrence, in document order, and
|
|
||||||
[`size()`](../api/basic_json_view/size.md) counts all of them, so `#!cpp view.size()` can be larger than
|
|
||||||
`#!cpp view.materialize().size()`.
|
|
||||||
|
|
||||||
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup
|
|
||||||
to scan all members of the object, even when the key is found at the very first one: this made lookups in small
|
|
||||||
objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first member of a
|
|
||||||
key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return the first
|
|
||||||
member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees
|
|
||||||
duplicates is unpredictable, so neither choice is wrong.
|
|
||||||
|
|
||||||
The same holds for [`get<T>()`](../api/basic_json_view/get.md) of a type whose `from_json()` reads the view, such as
|
|
||||||
the types of the `NLOHMANN_DEFINE_TYPE_*` macros: their members are looked up, so `#!cpp get<point>()` of
|
|
||||||
`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get<point>()` reads `#!cpp 2`.
|
|
||||||
|
|
||||||
**What stays the same as `parse()`?** [`materialize()`](../api/basic_json_view/materialize.md) and
|
|
||||||
[`get<std::map<...>>()`](../api/basic_json_view/get.md) replay every member in order, so they keep the *last* value
|
|
||||||
exactly like `#!cpp basic_json::parse()` (at the position of the first occurrence of the key, for an
|
|
||||||
[`ordered_json`](../api/ordered_json.md)).
|
|
||||||
|
|
||||||
**How do I get the value `parse()` would give?** Either call `#!cpp view.materialize()` and look the key up in the
|
|
||||||
result, or iterate the members and keep the last match:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// the last member with the key "a", as parse() would keep it
|
|
||||||
json_view last;
|
|
||||||
for (const auto item : view.items())
|
|
||||||
{
|
|
||||||
if (item.key() == "a")
|
|
||||||
{
|
|
||||||
last = item.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you cannot trust the source text to have unique keys, check [`size()`](../api/basic_json_view/size.md) against the
|
|
||||||
number of distinct keys, or reject duplicates when iterating.
|
|
||||||
|
|
||||||
## Getting values out without copying
|
|
||||||
|
|
||||||
[`get<T>()`](../api/basic_json_view/get.md) converts most `T` directly from the flat index, without ever building a
|
|
||||||
`basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`,
|
|
||||||
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json_view` itself, the standard containers
|
|
||||||
and other types the library converts (`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::list`, `#!cpp std::array`,
|
|
||||||
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, ...), and the types of the `NLOHMANN_DEFINE_TYPE_*` and
|
|
||||||
`NLOHMANN_JSON_SERIALIZE_ENUM` macros. Types whose `from_json()` is written for `basic_json` only go through
|
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real
|
|
||||||
`basic_json` value first, exactly as [`parse()`](../api/basic_json/parse.md) would, and converted from there. See
|
|
||||||
[Writing generic code](#writing-generic-code) for how to write a `from_json()` that reads views.
|
|
||||||
|
|
||||||
Two conversions never copy at all:
|
|
||||||
|
|
||||||
- [`get_string()`](../api/basic_json_view/get_string.md) (equivalently, `#!cpp get<string_view_t>()`) returns a
|
|
||||||
string as a `string_view_t` pointing into the document's [`source()`](../api/basic_json_document/source.md) text —
|
|
||||||
or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of
|
|
||||||
allocating a new `#!cpp std::string`.
|
|
||||||
- [`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
|
|
||||||
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
|
|
||||||
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
|
|
||||||
[`basic_json::parse()`](../api/basic_json/parse.md)) would.
|
|
||||||
|
|
||||||
Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.
|
|
||||||
|
|
||||||
### `string_view` members
|
|
||||||
|
|
||||||
A struct whose member is a `#!cpp std::string_view` (C++17) can be decoded from a view with the
|
|
||||||
`NLOHMANN_DEFINE_TYPE_*` macros, and the member is not copied either: for a string without escape sequences it points
|
|
||||||
into the document's source text, and for a string with escape sequences into the document's buffer of decoded strings.
|
|
||||||
The member is valid as long as the document is alive and, for a document that
|
|
||||||
[borrows its source text](#ownership-and-lifetime) (as [`parse()`](../api/basic_json_document/parse.md) does for
|
|
||||||
an lvalue `#!cpp std::string`, a `#!cpp std::string_view`, or a C string), as long as that text is.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct record
|
|
||||||
{
|
|
||||||
std::string_view name;
|
|
||||||
int count;
|
|
||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string text = R"({"name": "widget", "count": 3})";
|
|
||||||
const json_document doc = json_document::parse(text); // borrows text
|
|
||||||
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Decoding into a `string_view` from a `json` value dangles at once"
|
|
||||||
|
|
||||||
The same struct also compiles with `#!cpp json::parse(text).get<record>()`, but there `name` points into the
|
|
||||||
temporary `json` value, which is destroyed at the end of the statement. Use a named
|
|
||||||
[`json_document`](../api/json_document.md), as above, to decode such a struct.
|
|
||||||
|
|
||||||
## Writing generic code
|
|
||||||
|
|
||||||
A view's type names its `BasicJsonType` and its [`view_mode`](../api/view_mode.md):
|
|
||||||
[`json_view`](../api/json_view.md) is `#!cpp basic_json_view<json, view_mode::read_only>`, and
|
|
||||||
[`json_editable_view`](../api/json_editable_view.md) is `#!cpp basic_json_view<json, view_mode::editable>`. These are
|
|
||||||
different types. The only conversion between them turns a view of a read-only document into a view of an editable
|
|
||||||
document of the same `BasicJsonType`, so a function written for one shortcut accepts few other views:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
void print_names(json_view v); // accepts neither an ordered_json_view nor a json_editable_view
|
|
||||||
void print_names(json_editable_view v); // also accepts a json_view, but no ordered_json_view
|
|
||||||
```
|
|
||||||
|
|
||||||
This is the same pitfall as a `to_json`/`from_json` written for `json` instead of any `BasicJsonType`, which then does
|
|
||||||
not work for `ordered_json`. The shortcuts are for declaring documents and views; a function that takes a view names
|
|
||||||
the views it accepts with a template:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// views of editable documents only
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
|
|
||||||
|
|
||||||
// views of read-only documents only
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
|
|
||||||
|
|
||||||
// views of either document type
|
|
||||||
template<typename BasicJsonType, view_mode Mode>
|
|
||||||
void f(basic_json_view<BasicJsonType, Mode> v);
|
|
||||||
```
|
|
||||||
|
|
||||||
Views are passed by value: they are two pointers, cheap to copy, like a `#!cpp std::string_view`. A `const&` works as
|
|
||||||
well, but a non-`const` reference does not bind to the views that [`root()`](../api/basic_json_document/root.md),
|
|
||||||
[`operator[]`](../api/basic_json_view/operator%5B%5D.md), and [`at()`](../api/basic_json_view/at.md) return, so
|
|
||||||
`#!cpp f(doc.root())` would not compile.
|
|
||||||
|
|
||||||
There is no conversion from an editable view to a read-only one, by design: the mode decides how a view walks the
|
|
||||||
index. A read-only view reads a container in place, while a view of an editable document follows the links that edits
|
|
||||||
leave behind, so a read-only view of an edited document would read stale values. The other direction is safe: a
|
|
||||||
read-only document has no edits, so a view of it [converts](../api/basic_json_view/basic_json_view.md) to an
|
|
||||||
editable view, which reads it like an editable document before its first edit. A template deduces the mode without
|
|
||||||
conversions, so the template forms above still accept only the modes they name. Views of both modes compare with
|
|
||||||
[`operator==`](../api/basic_json_view/operator_eq.md).
|
|
||||||
|
|
||||||
[`is_json_view<T>`](../api/is_json_view.md) tells views from other types, for constraints that cannot be written as a
|
|
||||||
`basic_json_view<...>` parameter.
|
|
||||||
|
|
||||||
### A `from_json()` that reads views
|
|
||||||
|
|
||||||
[`get<T>()`](../api/basic_json_view/get.md) on a view calls `T`'s `from_json()` with the view itself if that
|
|
||||||
`from_json()` accepts views, and with [`materialize()`](../api/basic_json_view/materialize.md) otherwise. The
|
|
||||||
`from_json()` functions of the [`NLOHMANN_DEFINE_TYPE_*`](../api/macros/nlohmann_define_type_intrusive.md),
|
|
||||||
[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../api/macros/nlohmann_define_derived_type.md), and
|
|
||||||
[`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md) macros, and those of the library
|
|
||||||
for the standard types, accept views, so these types need nothing else. For decoding the statuses of
|
|
||||||
twitter.json into structs, reading the view is about 5 times faster than `#!cpp materialize().get<T>()`.
|
|
||||||
|
|
||||||
A hand-written `from_json()` reads views if it is constrained to `json` values and views:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
template < typename J, typename std::enable_if <
|
|
||||||
nlohmann::is_json_view<J>::value || std::is_same<J, nlohmann::json>::value, int >::type = 0 >
|
|
||||||
void from_json(const J& j, person& p)
|
|
||||||
{
|
|
||||||
p.name = j.at("name").template get<std::string>();
|
|
||||||
p.age = j.at("age").template get<int>();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Its body may only use what views and `basic_json` have in common: the type checks, `at()`, `operator[]`, `contains()`,
|
|
||||||
`value()`, `size()`, iteration and `items()`, and `get<T>()`/`get_to()`. An unconstrained
|
|
||||||
`#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType& j, person& p)`, as in
|
|
||||||
[arbitrary type conversions](arbitrary_types.md), is never called with a view, as it might use `basic_json` functions
|
|
||||||
a view does not have; such a type is converted through `materialize()`. A `from_json()` for `json` only is
|
|
||||||
converted through `materialize()` as well.
|
|
||||||
|
|
||||||
One thing differs when a `from_json()` reads the view: members looked up by name (`at()`, `value()`, ... and thus
|
|
||||||
the macros) find the **first** member with a repeated key, see [Duplicate keys](#duplicate-keys);
|
|
||||||
`#!cpp materialize().get<T>()` would see the last one. Containers built from an object, such as `#!cpp std::map`,
|
|
||||||
keep the **last** value either way, as [`parse()`](../api/basic_json/parse.md) does.
|
|
||||||
|
|
||||||
`to_json()` is unaffected: it writes `basic_json` values, never views.
|
|
||||||
|
|
||||||
## Writing a view back
|
|
||||||
|
|
||||||
[`dump()`](../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
|
|
||||||
`basic_json` value. An object's members are written in document order, not sorted by key, and *every* occurrence of a
|
|
||||||
repeated key is written, not only the last one — the same two ways [iteration](#what-is-different) already differs
|
|
||||||
from a [`materialize()`](../api/basic_json_view/materialize.md)d value, see [Duplicate keys](#duplicate-keys).
|
|
||||||
`#!cpp materialize().dump()` gives a different result in both respects for a `json_view`.
|
|
||||||
|
|
||||||
By default, numbers are written the way [`basic_json::dump()`](../api/basic_json/dump.md) would.
|
|
||||||
[`number_format::source`](../api/basic_json_view/number_format.md) instead copies every number exactly as it was
|
|
||||||
written in the source text — a price like `#!cpp 19.90`, a long order or account ID with more digits than any number
|
|
||||||
type holds, or a high-precision coordinate — something `basic_json` cannot do at all, since parsing already reduces
|
|
||||||
a number to its parsed `#!cpp double`/`#!cpp int64_t` value.
|
|
||||||
|
|
||||||
[`operator<<`](../api/basic_json_view/operator_ltlt.md) writes a view to a stream the way `basic_json`'s does, using
|
|
||||||
the stream's `width`/`fill` for indentation.
|
|
||||||
|
|
||||||
## Editing a document
|
|
||||||
|
|
||||||
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but
|
|
||||||
not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](../api/basic_json_document/index.md) — more conveniently
|
|
||||||
spelled [`json_editable_document`](../api/json_editable_document.md) or
|
|
||||||
[`ordered_json_editable_document`](../api/ordered_json_editable_document.md) — also lets you
|
|
||||||
[`set`](../api/basic_json_document/set.md) a value, [`push_back`](../api/basic_json_document/push_back.md) onto or
|
|
||||||
[`insert`](../api/basic_json_document/insert.md) into an array, and [`erase`](../api/basic_json_document/erase.md)
|
|
||||||
an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
|
|
||||||
|
|
||||||
The [mode](../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so `json_document`/`ordered_json_document` are unaffected — they carry
|
|
||||||
none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a
|
|
||||||
runtime one.
|
|
||||||
|
|
||||||
### Why: editing without reformatting
|
|
||||||
|
|
||||||
The `#!cpp 19.90` price from [above](#writing-a-view-back) is exactly the kind of value that makes editing a `json`
|
|
||||||
or `ordered_json` value in place lossy. Say you parse a configuration file, patch one field, and write it back:
|
|
||||||
|
|
||||||
- **`json`** re-sorts every key on the way in (`object_t` is a `#!cpp std::map`) and rewrites every number to its
|
|
||||||
shortest round-trip form on the way out — a one-field patch turns into a diff that reorders the whole file and
|
|
||||||
rewrites `#!cpp 19.90` to `#!cpp 19.9`.
|
|
||||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced
|
|
||||||
it to a `#!cpp double`/`#!cpp int64_t`, and there is no way back to how it was spelled in the source text.
|
|
||||||
|
|
||||||
The patch of the example below (set `"host"`, add `"retries"`) written back by each of them:
|
|
||||||
|
|
||||||
<div class="nj-jsonview-cmp" role="group" aria-label="The same patch written back by json, ordered_json, and json_editable_document">
|
|
||||||
<div><p class="nj-jsonview-cmp-h"><code>json</code></p><p class="nj-jsonview-cmp-v">keys sorted, numbers rewritten</p><pre>{<span class="nj-jsonview-bad">"host"</span>:"db2",
|
|
||||||
"name":"cache",
|
|
||||||
"port":6379,
|
|
||||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
|
||||||
"retries":3}</pre></div>
|
|
||||||
<div><p class="nj-jsonview-cmp-h"><code>ordered_json</code></p><p class="nj-jsonview-cmp-v">key order kept, numbers rewritten</p><pre>{"name":"cache",
|
|
||||||
"host":"db2",
|
|
||||||
"port":6379,
|
|
||||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
|
||||||
"retries":3}</pre></div>
|
|
||||||
<div class="nj-jsonview-cmp--best"><p class="nj-jsonview-cmp-h"><code>json_editable_document</code></p><p class="nj-jsonview-cmp-v">order and spelling kept</p><pre>{"name":"cache",
|
|
||||||
"host":"db2",
|
|
||||||
"port":6379,
|
|
||||||
"price":<span class="nj-jsonview-ok">19.90</span>,
|
|
||||||
"retries":3}</pre></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
An editable document keeps both. [`dump()`](../api/basic_json_view/dump.md) of an edited document writes members in
|
|
||||||
document order — a member [`set`](../api/basic_json_document/set.md) added goes at the end, exactly where it was
|
|
||||||
inserted, and an [`erase`](../api/basic_json_document/erase.md)d member simply leaves a gap: everything around it
|
|
||||||
keeps its place — and [`number_format::source`](../api/basic_json_view/number_format.md) keeps the exact spelling
|
|
||||||
of every number an edit did not itself touch; a number an edit *did* touch is written the way
|
|
||||||
[`BasicJsonType::dump()`](../api/basic_json/dump.md) would write it, since there is no source spelling for a brand
|
|
||||||
new value.
|
|
||||||
|
|
||||||
??? example "Example: patch a configuration, keeping member order and number spellings"
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/json_editable_document.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
--8<-- "examples/json_editable_document.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
### What stays valid, and what an edit costs
|
|
||||||
|
|
||||||
The source text itself is **never written**, and the parsed index never moves — a value keeps the node it was
|
|
||||||
parsed into for as long as it is not itself replaced. So every [view](../api/basic_json_view/index.md) taken before
|
|
||||||
an edit, including a previously obtained [`root()`](../api/basic_json_document/root.md), stays valid and, if it
|
|
||||||
still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing
|
|
||||||
what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does
|
|
||||||
invalidate is the **iterators** taken over an edited array or object: the first time an element is added to it,
|
|
||||||
inserted into it, erased from it, or replaced by a value that spans a different number of nodes, its elements move from the parsed, fixed layout to a growable block of links
|
|
||||||
so that [`push_back`](../api/basic_json_document/push_back.md) can later grow it in amortized constant time —
|
|
||||||
existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string
|
|
||||||
obtained with
|
|
||||||
[`get_string()`](../api/basic_json_view/get_string.md) is unaffected either way and stays valid across further
|
|
||||||
edits. See [`basic_json_document`'s Edits](../api/basic_json_document/index.md#edits) for the details, and
|
|
||||||
[`set`'s Exception safety](../api/basic_json_document/set.md#exception-safety) for what an edit guarantees if it
|
|
||||||
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
|
|
||||||
the index is described in the [architecture overview](../home/architecture.md#node-index-of-json-views).
|
|
||||||
|
|
||||||
Step through the edits of a small document (the node fields follow the [architecture overview](../home/architecture.md#node-index-of-json-views)):
|
|
||||||
|
|
||||||
<div class="nj-jsonview-steps" data-nj-jsonview-steps>
|
|
||||||
<figure class="nj-jsonview-step" data-label="After parsing">
|
|
||||||
<div class="nj-jsonview-scroll">
|
|
||||||
--8<-- "images/json_view_edit_0.svg"
|
|
||||||
</div>
|
|
||||||
<figcaption>Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.</figcaption>
|
|
||||||
</figure>
|
|
||||||
<figure class="nj-jsonview-step" data-label="set an existing member">
|
|
||||||
<div class="nj-jsonview-scroll">
|
|
||||||
--8<-- "images/json_view_edit_1.svg"
|
|
||||||
</div>
|
|
||||||
<figcaption>Step 2 of 5. <code>doc.set(doc.root(), "port", 6380)</code> overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.</figcaption>
|
|
||||||
</figure>
|
|
||||||
<figure class="nj-jsonview-step" data-label="erase an array element">
|
|
||||||
<div class="nj-jsonview-scroll">
|
|
||||||
--8<-- "images/json_view_edit_2.svg"
|
|
||||||
</div>
|
|
||||||
<figcaption>Step 3 of 5. <code>doc.erase(doc.root()["tags"], 0)</code>: the array is edited for the first time, so its elements move to a block of links in owned storage. The node of <code>"eu"</code> stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.</figcaption>
|
|
||||||
</figure>
|
|
||||||
<figure class="nj-jsonview-step" data-label="push_back onto the array">
|
|
||||||
<div class="nj-jsonview-scroll">
|
|
||||||
--8<-- "images/json_view_edit_3.svg"
|
|
||||||
</div>
|
|
||||||
<figcaption>Step 4 of 5. <code>doc.push_back(doc.root()["tags"], "new")</code>: the new string gets its own node, its text goes to the buffer of edited text, and a link to it is appended to the block of the array.</figcaption>
|
|
||||||
</figure>
|
|
||||||
<figure class="nj-jsonview-step" data-label="set a new member">
|
|
||||||
<div class="nj-jsonview-scroll">
|
|
||||||
--8<-- "images/json_view_edit_4.svg"
|
|
||||||
</div>
|
|
||||||
<figcaption>Step 5 of 5. <code>doc.set(doc.root(), "retries", 3)</code>: a new member, so the object moves to a block too: copies of its keys, links to the values it already had, then the new key and a link to the new integer.</figcaption>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="nj-caption" markdown>The node index and the owned storage of the document after each of five edits. Use *Back*, *Next*, or the numbered buttons to switch between the steps; without JavaScript all steps are shown one below the other.</p>
|
|
||||||
|
|
||||||
## Images
|
|
||||||
|
|
||||||
[`save()`](../api/basic_json_document/save.md) writes a document as an *image*: a byte buffer that
|
|
||||||
[`load()`](../api/basic_json_document/load.md) reads back into a document without parsing — no lexing, no building
|
|
||||||
the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where
|
|
||||||
[`parse_copy()`](../api/basic_json_document/parse_copy.md) still has to scan the whole input,
|
|
||||||
[`load()`](../api/basic_json_document/load.md) turns that scan into a copy of the node index alone.
|
|
||||||
|
|
||||||
**Why.** A document that is parsed once and then read many times — a configuration loaded at startup, a template
|
|
||||||
rendered on every request, a large reference dataset a worker process needs in memory — pays for parsing once but
|
|
||||||
can amortize [`save()`](../api/basic_json_document/save.md)'s cost across every later load. That makes images useful
|
|
||||||
for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache),
|
|
||||||
and [`load()`](../api/basic_json_document/load.md) it on every later use instead of parsing the source text again.
|
|
||||||
They are just as useful for handing a parsed document to another process (or a forked worker) running the same build
|
|
||||||
of the library, since [`load()`](../api/basic_json_document/load.md) turns the transfer into a copy of the node index
|
|
||||||
plus pointers into the received bytes, not a re-parse.
|
|
||||||
|
|
||||||
**Choosing a check.** [`load()`](../api/basic_json_document/load.md) takes an
|
|
||||||
[`image_check`](../api/basic_json_document/load.md#image_check) that trades validation against speed:
|
|
||||||
`image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as
|
|
||||||
safe to read and serialize as a freshly parsed document — the right choice whenever the image did not come straight
|
|
||||||
from this process's own [`save()`](../api/basic_json_document/save.md), such as a file or a network peer.
|
|
||||||
`image_check::bounds` only checks structure and bounds — cheaper, since it skips scanning the text and the decoded
|
|
||||||
strings — and fits a cache the process trusts, one it wrote and reads back itself. `image_check::none` skips
|
|
||||||
validation entirely, for an image trusted as much as the process's own memory. See
|
|
||||||
[`load()`'s Notes](../api/basic_json_document/load.md#notes) for exactly what each level does and does not guarantee.
|
|
||||||
|
|
||||||
??? example "Example: cache a parsed configuration as an image"
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/basic_json_document__save.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
--8<-- "examples/basic_json_document__save.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Experimental"
|
|
||||||
|
|
||||||
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
|
||||||
stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the
|
|
||||||
library build that wrote it — use it to cache a document or to hand one to another process running the *same*
|
|
||||||
build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by
|
|
||||||
a future library version.
|
|
||||||
|
|
||||||
The idea of a document you can read without parsing comes from zero-copy formats such as
|
|
||||||
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
|
|
||||||
[`load()`](../api/basic_json_document/load.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from
|
|
||||||
either.
|
|
||||||
|
|
||||||
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
|
||||||
|
|
||||||
| | [`json`](../api/json.md) / [`ordered_json`](../api/ordered_json.md) | [SAX interface](parsing/sax_interface.md) | [`json_document`](../api/json_document.md) / [`json_view`](../api/json_view.md) | [`json_editable_document`](../api/json_editable_document.md) / [`json_editable_view`](../api/json_editable_view.md) |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
|
|
||||||
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](../api/basic_json_document/set.md)/[`push_back`](../api/basic_json_document/push_back.md)/[`insert`](../api/basic_json_document/insert.md)/[`erase`](../api/basic_json_document/erase.md) edit in place; the source text is never rewritten |
|
|
||||||
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](../api/basic_json_view/dump.md) of an edited document that keeps the member order and, with [`number_format::source`](../api/basic_json_view/number_format.md), the spelling of every untouched number |
|
|
||||||
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back — a configuration file, for instance — where the rest of it should come back exactly as it was |
|
|
||||||
| **Caching/reload** | not applicable — re-parse, or roll your own serialization | not applicable | [`save()`](../api/basic_json_document/save.md)/[`load()`](../api/basic_json_document/load.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current — possibly edited — state |
|
|
||||||
|
|
||||||
<p class="nj-table-caption" markdown>Each column is one way to work with JSON in this library, and each row compares one property of them.</p>
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [`basic_json_document`](../api/basic_json_document/index.md) - the document that owns the parsed index (parse, access, images, edits)
|
|
||||||
- [`basic_json_view`](../api/basic_json_view/index.md) - the non-owning handle to a value in a document
|
|
||||||
- [`json_document`](../api/json_document.md) - and [`ordered_json_document`](../api/ordered_json_document.md) - the read-only documents
|
|
||||||
- [`json_editable_document`](../api/json_editable_document.md) - and [`ordered_json_editable_document`](../api/ordered_json_editable_document.md) - the documents that can be edited in place
|
|
||||||
- [Architecture](../home/architecture.md#node-index-of-json-views) - the layout of the node index
|
|
||||||
- [SAX interface](parsing/sax_interface.md) - event-based parsing without building a tree
|
|
||||||
- [Performance](performance.md) - benchmarks and tuning of parsing and serialization
|
|
||||||
- [Large, Streamed, and Partial Input](parsing/large_and_partial_input.md) - reading only the part of a text you need
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
# Differences from `basic_json`
|
||||||
|
|
||||||
|
A [`basic_json_view`](../../api/basic_json_view/index.md) offers the read-only part of the
|
||||||
|
[`basic_json`](../../api/basic_json/index.md) interface, and in most respects it behaves exactly like the value
|
||||||
|
[`basic_json::parse()`](../../api/basic_json/parse.md) would have built from the same text. This page lists where it
|
||||||
|
does not: what a document parses differently, what a view's functions do differently, and which parts of the
|
||||||
|
`basic_json` interface a view does not have.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
| | `basic_json` value from `parse()` | view of a `json_document` | to get the `basic_json` behavior |
|
||||||
|
|---|---|---|---|
|
||||||
|
| [Integer types](#parsing) | any `number_integer_t`/`number_unsigned_t` | 64-bit only (compile-time check) | — |
|
||||||
|
| [Input size](#parsing) | unlimited | less than 4 GiB | — |
|
||||||
|
| [Parser callbacks](#parsing) | `parser_callback_t` | none | filter while iterating |
|
||||||
|
| [`operator[]` on a missing key or index](#element-access) | inserts (non-`const`) or undefined behavior (`const`) | returns a discarded view | — |
|
||||||
|
| [Object member order](#object-member-order) | sorted by key (`json`), insertion order (`ordered_json`) | document order | `materialize()` |
|
||||||
|
| [Repeated keys](#duplicate-keys) | the last value is kept | lookups find the first member; iteration, `size()`, and `dump()` see all of them | `materialize()` |
|
||||||
|
| [Conversions](#conversions) | `get<T>()`, implicit conversions, `get_ptr()`, `get_ref()` | `get<T>()`, `get_string()`, `number_token()` | `materialize()` |
|
||||||
|
| [Number spelling](#serialization) | lost when parsing | kept: `number_token()`, `number_format::source` | — |
|
||||||
|
| [Comparison](#comparison) | `==`, `!=`, `<`, `<=>`, ... | `==` and `!=` only | `materialize()` |
|
||||||
|
| [Modification](#what-a-view-does-not-have) | member functions of the value | only through an [editable document](editing.md) | `materialize()` |
|
||||||
|
| [Iterators](#what-a-view-does-not-have) | bidirectional (`rbegin()`/`rend()`) | forward only | `materialize()` |
|
||||||
|
|
||||||
|
<p class="nj-table-caption" markdown>Each row is one difference; the last column names the way to get `basic_json`'s behavior from a view, where there is one.</p>
|
||||||
|
|
||||||
|
## Parsing
|
||||||
|
|
||||||
|
### What is the same
|
||||||
|
|
||||||
|
- **Accept/reject.** [`accept()`](../../api/basic_json_document/accept.md) and
|
||||||
|
[`parse()`](../../api/basic_json_document/parse.md) accept and reject exactly the same inputs as
|
||||||
|
[`basic_json::accept()`](../../api/basic_json/accept.md)/[`basic_json::parse()`](../../api/basic_json/parse.md),
|
||||||
|
with the same `ignore_comments` and `ignore_trailing_commas` options.
|
||||||
|
- **Errors.** A failing parse throws the same exception — the same id, message, and position — because on failure
|
||||||
|
the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and
|
||||||
|
the replay builds no values: it costs about a lexing pass up to the error.
|
||||||
|
`#!cpp allow_exceptions == false` skips the replay and gives a
|
||||||
|
[discarded](../../api/basic_json_document/is_discarded.md) document instead of throwing, just as it gives a
|
||||||
|
discarded value for `#!cpp basic_json::parse()`.
|
||||||
|
- **Number classification.** An integer literal that does not fit into the 64-bit integer type becomes a
|
||||||
|
floating-point value, exactly as it does for `#!cpp basic_json::parse()`.
|
||||||
|
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](../../api/macros/json_strict_nul_handling.md) and
|
||||||
|
[`JSON_NOEXCEPTION`](../../api/macros/json_noexception.md)/[`JSON_THROW_USER`](../../api/macros/json_throw_user.md)
|
||||||
|
behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or
|
||||||
|
how it reports errors. The other macros only affect the values `materialize()` produces, through `BasicJsonType`.
|
||||||
|
Two of them also add context to the exceptions of views, see [Exception context](#exception-context).
|
||||||
|
|
||||||
|
### What is different
|
||||||
|
|
||||||
|
- **Only 64-bit integers.** `basic_json_document<BasicJsonType>` requires `BasicJsonType::number_integer_t` and
|
||||||
|
`number_unsigned_t` to both be 64 bits wide; this is a compile-time `#!cpp static_assert`.
|
||||||
|
- **A 4 GiB input limit.** An input of 4294967280 bytes (4 GiB minus 16 bytes) or more throws
|
||||||
|
[`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416), a limit
|
||||||
|
`#!cpp basic_json::parse()` does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits
|
||||||
|
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
||||||
|
end must still fit.
|
||||||
|
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
||||||
|
is read to its end into a buffer the document owns, and parsing starts after that. For a file, reading it into a
|
||||||
|
`#!cpp std::string` yourself and parsing that (an lvalue is borrowed) is therefore not more expensive in memory, and
|
||||||
|
was about 6 to 12 % faster than parsing from an `#!cpp std::ifstream`.
|
||||||
|
- **No parser callbacks.** [`parse()`](../../api/basic_json_document/parse.md) takes no
|
||||||
|
[`parser_callback_t`](../../api/basic_json/parser_callback_t.md): a document always indexes the whole text. Skip
|
||||||
|
the values you do not need when reading the view instead.
|
||||||
|
- **JSON text only.** A document is parsed from JSON text; there is no counterpart of
|
||||||
|
[`from_cbor()`](../../api/basic_json/from_cbor.md) and the other binary formats, and
|
||||||
|
[`is_binary()`](../../api/basic_json_view/is_binary.md) is always `#!cpp false`.
|
||||||
|
|
||||||
|
## Element access
|
||||||
|
|
||||||
|
**Chained access is safe.** [`operator[]`](../../api/basic_json_view/operator%5B%5D.md) with a missing key, an index
|
||||||
|
out of range, or an unresolvable JSON pointer returns a [discarded](../../api/basic_json_view/is_discarded.md) view,
|
||||||
|
and `operator[]` on a discarded view returns a discarded view without throwing: `#!cpp v["a"]["b"][0]` can be tested
|
||||||
|
once at the end. Type errors on values that exist (a key on an array, an index on an object) still throw, and
|
||||||
|
[`at`](../../api/basic_json_view/at.md) throws for every missing value.
|
||||||
|
|
||||||
|
`basic_json`'s non-`const` `operator[]` instead inserts a missing key, and its `const` `operator[]` has undefined
|
||||||
|
behavior for one.
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
|
||||||
|
### Object member order
|
||||||
|
|
||||||
|
[`begin()`](../../api/basic_json_view/begin.md)/[`end()`](../../api/basic_json_view/end.md) and
|
||||||
|
[`items()`](../../api/basic_json_view/items.md) visit an object's members in the order they appear in the source text.
|
||||||
|
`basic_json`'s default `object_t` is a `std::map`, which sorts by key, so iterating a
|
||||||
|
[`materialize()`](../../api/basic_json_view/materialize.md)d value can print members in a different order than
|
||||||
|
iterating the view they came from. [`dump()`](../../api/basic_json_view/dump.md) of a view writes members in document
|
||||||
|
order as well.
|
||||||
|
|
||||||
|
### Duplicate keys
|
||||||
|
|
||||||
|
!!! warning "A lookup in a view and in the parsed value can give different answers"
|
||||||
|
|
||||||
|
If an object in the source text repeats a key, [`operator[]`](../../api/basic_json_view/operator%5B%5D.md),
|
||||||
|
[`at`](../../api/basic_json_view/at.md), [`find`](../../api/basic_json_view/find.md),
|
||||||
|
[`value`](../../api/basic_json_view/value.md), [`contains`](../../api/basic_json_view/contains.md), and JSON
|
||||||
|
pointer resolution all return the **first** member with that key. `basic_json::parse()` instead keeps the **last**
|
||||||
|
value of a repeated key, so for `#!cpp {"a":1,"a":2}`, `#!cpp view["a"]` is `#!cpp 1` while
|
||||||
|
`#!cpp parse(text)["a"]` is `#!cpp 2`. [`count`](../../api/basic_json_view/count.md) counts every member with the
|
||||||
|
key, so it is `#!cpp 2` here.
|
||||||
|
|
||||||
|
[`begin()`](../../api/basic_json_view/begin.md)/[`end()`](../../api/basic_json_view/end.md) and
|
||||||
|
[`items()`](../../api/basic_json_view/items.md) visit *every* occurrence, in document order, and
|
||||||
|
[`size()`](../../api/basic_json_view/size.md) counts all of them, so `#!cpp view.size()` can be larger than
|
||||||
|
`#!cpp view.materialize().size()`. [`dump()`](../../api/basic_json_view/dump.md) writes every occurrence, too, so
|
||||||
|
`#!cpp materialize().dump()` gives a different result for such an object.
|
||||||
|
|
||||||
|
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup
|
||||||
|
to scan all members of the object, even when the key is found at the very first one: this made lookups in small
|
||||||
|
objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first
|
||||||
|
member of a key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return
|
||||||
|
the first member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver
|
||||||
|
that sees duplicates is unpredictable, so neither choice is wrong.
|
||||||
|
|
||||||
|
The same holds for [`get<T>()`](../../api/basic_json_view/get.md) of a type whose `from_json()` reads the view, such
|
||||||
|
as the types of the `NLOHMANN_DEFINE_TYPE_*` macros: their members are looked up, so `#!cpp get<point>()` of
|
||||||
|
`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get<point>()` reads `#!cpp 2`.
|
||||||
|
|
||||||
|
**What stays the same as `parse()`?** [`materialize()`](../../api/basic_json_view/materialize.md) and
|
||||||
|
[`get<std::map<...>>()`](../../api/basic_json_view/get.md) (and the other containers built from an object) replay
|
||||||
|
every member in order, so they keep the *last* value exactly like `#!cpp basic_json::parse()` (at the position of the
|
||||||
|
first occurrence of the key, for an [`ordered_json`](../../api/ordered_json.md)).
|
||||||
|
|
||||||
|
**How do I get the value `parse()` would give?** Either call `#!cpp view.materialize()` and look the key up in the
|
||||||
|
result, or iterate the members and keep the last match:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// the last member with the key "a", as parse() would keep it
|
||||||
|
json_view last;
|
||||||
|
for (const auto item : view.items())
|
||||||
|
{
|
||||||
|
if (item.key() == "a")
|
||||||
|
{
|
||||||
|
last = item.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you cannot trust the source text to have unique keys, check [`size()`](../../api/basic_json_view/size.md) against
|
||||||
|
the number of distinct keys, or reject duplicates when iterating.
|
||||||
|
|
||||||
|
## Conversions
|
||||||
|
|
||||||
|
- **No implicit conversions.** A view has no `#!cpp operator ValueType()`, no `#!cpp get_ptr()`, and no
|
||||||
|
`#!cpp get_ref()`; use [`get<T>()`](../../api/basic_json_view/get.md), or
|
||||||
|
[`get_string()`](../../api/basic_json_view/get_string.md) for a string without a copy.
|
||||||
|
- **`from_json()` overloads.** [`get<T>()`](../../api/basic_json_view/get.md) calls `T`'s `from_json()` with the view
|
||||||
|
only if that `from_json()` accepts views, and with [`materialize()`](../../api/basic_json_view/materialize.md)
|
||||||
|
otherwise; see [A `from_json()` that reads views](generic_code.md#a-from_json-that-reads-views). Both give the same
|
||||||
|
result except for [repeated keys](#duplicate-keys).
|
||||||
|
- **`string_view` members.** A struct with a `#!cpp std::string_view` member decoded from a view points into the
|
||||||
|
document, see [`string_view` members](reading.md#string_view-members). Decoding the same struct from a `json` value
|
||||||
|
compiles, too, but dangles at once if the value is a temporary.
|
||||||
|
|
||||||
|
## Serialization
|
||||||
|
|
||||||
|
[`dump()`](../../api/basic_json_view/dump.md) writes numbers the way
|
||||||
|
[`basic_json::dump()`](../../api/basic_json/dump.md) would by default, but writes object members in
|
||||||
|
[document order](#object-member-order) and writes [every occurrence of a repeated key](#duplicate-keys).
|
||||||
|
|
||||||
|
A view can also do what a `basic_json` value cannot: [`number_token()`](../../api/basic_json_view/number_token.md)
|
||||||
|
and [`number_format::source`](../../api/basic_json_view/number_format.md) give every number exactly as it was spelled
|
||||||
|
in the source text, while parsing into `basic_json` already reduces a number to its `#!cpp double`/`#!cpp int64_t`
|
||||||
|
value. See [Writing a view back](reading.md#writing-a-view-back).
|
||||||
|
|
||||||
|
## Comparison
|
||||||
|
|
||||||
|
**Ordering comparisons are not provided** by `basic_json_view` — there is no `#!cpp operator<`.
|
||||||
|
[`operator==`](../../api/basic_json_view/operator_eq.md) and [`operator!=`](../../api/basic_json_view/operator_ne.md)
|
||||||
|
are provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when
|
||||||
|
[`materialize()`](../../api/basic_json_view/materialize.md) or [`parse()`](../../api/basic_json/parse.md) would produce
|
||||||
|
equal values for them, without ever building a tree to do it. For ordering,
|
||||||
|
[`materialize()`](../../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
||||||
|
|
||||||
|
## Exception context
|
||||||
|
|
||||||
|
With [`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md), an exception of a view names the JSON Pointer path of
|
||||||
|
the value within the document, and with
|
||||||
|
[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md), it names the bytes of the value in the
|
||||||
|
source text, as they do for the exceptions of `#!cpp basic_json`.
|
||||||
|
|
||||||
|
A view has neither parent pointers nor stored positions, so both are found from the document's index when the
|
||||||
|
exception is thrown. This takes time linear in the size of the document. One exception has neither: `key()` of an
|
||||||
|
iterator over an array or a primitive value
|
||||||
|
([`invalid_iterator.207`](../../home/exceptions.md#jsonexceptioninvalid_iterator207)), because a view's iterator does
|
||||||
|
not know the value it iterates over.
|
||||||
|
|
||||||
|
**Fewer source positions after edits.** With
|
||||||
|
[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) enabled,
|
||||||
|
[`materialize()`](../../api/basic_json_view/materialize.md) sets the same positions as `#!cpp basic_json::parse()`. In
|
||||||
|
an [editable document](editing.md), a value written by an edit has none, and neither has an array or object whose
|
||||||
|
elements changed or whose last element has none; a `basic_json` value keeps its positions when it is changed. See
|
||||||
|
[`materialize()`](../../api/basic_json_view/materialize.md#notes).
|
||||||
|
|
||||||
|
## What a view does not have
|
||||||
|
|
||||||
|
A view is a read-only handle. These parts of the `basic_json` interface have no counterpart on a view;
|
||||||
|
[`materialize()`](../../api/basic_json_view/materialize.md) the subtree to use them:
|
||||||
|
|
||||||
|
- **Modifiers** such as `push_back`, `insert`, `erase`, `update`, or `operator=`. A view never changes a value
|
||||||
|
itself; an [editable document](editing.md) changes the values its views refer to through
|
||||||
|
[`set`](../../api/basic_json_document/set.md), [`push_back`](../../api/basic_json_document/push_back.md),
|
||||||
|
[`insert`](../../api/basic_json_document/insert.md), and [`erase`](../../api/basic_json_document/erase.md).
|
||||||
|
- **Reverse iteration.** A view's iterator is a forward iterator; there is no `rbegin()`/`rend()`.
|
||||||
|
- **JSON Patch and friends**: [`patch()`](../../api/basic_json/patch.md),
|
||||||
|
[`merge_patch()`](../../api/basic_json/merge_patch.md), [`diff()`](../../api/basic_json/diff.md),
|
||||||
|
[`flatten()`](../../api/basic_json/flatten.md).
|
||||||
|
- **Binary formats**: [`to_cbor()`](../../api/basic_json/to_cbor.md) and the other writers, and binary values
|
||||||
|
([`get_binary()`](../../api/basic_json/get_binary.md)).
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Zero-copy JSON views](index.md) - overview
|
||||||
|
- [`basic_json_view`](../../api/basic_json_view/index.md) - the full interface of a view
|
||||||
|
- [`materialize()`](../../api/basic_json_view/materialize.md) - build the `basic_json` value of a view
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# Editing a document
|
||||||
|
|
||||||
|
A [`json_document`](../../api/json_document.md)/[`json_view`](../../api/json_view.md) lets you look at a parsed text
|
||||||
|
without copying it, but not change it.
|
||||||
|
[`basic_json_document<BasicJsonType, view_mode::editable>`](../../api/basic_json_document/index.md) — more
|
||||||
|
conveniently spelled [`json_editable_document`](../../api/json_editable_document.md) or
|
||||||
|
[`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) — also lets you
|
||||||
|
[`set`](../../api/basic_json_document/set.md) a value, [`push_back`](../../api/basic_json_document/push_back.md) onto
|
||||||
|
or [`insert`](../../api/basic_json_document/insert.md) into an array, and
|
||||||
|
[`erase`](../../api/basic_json_document/erase.md) an object member or an array element, still without ever building a
|
||||||
|
`basic_json` tree for parts you do not touch.
|
||||||
|
|
||||||
|
The [mode](../../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so
|
||||||
|
`json_document`/`ordered_json_document` are unaffected — they carry none of the bookkeeping edits need, and calling
|
||||||
|
`set`/`push_back`/`insert`/`erase` on one is a compile error, not a runtime one.
|
||||||
|
|
||||||
|
## Why: editing without reformatting
|
||||||
|
|
||||||
|
A price like `#!cpp 19.90` is exactly the kind of value that makes editing a `json` or `ordered_json` value in place
|
||||||
|
lossy. Say you parse a configuration file, patch one field, and write it back:
|
||||||
|
|
||||||
|
- **`json`** re-sorts every key on the way in (`object_t` is a `#!cpp std::map`) and rewrites every number to its
|
||||||
|
shortest round-trip form on the way out — a one-field patch turns into a diff that reorders the whole file and
|
||||||
|
rewrites `#!cpp 19.90` to `#!cpp 19.9`.
|
||||||
|
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced
|
||||||
|
it to a `#!cpp double`/`#!cpp int64_t`, and there is no way back to how it was spelled in the source text.
|
||||||
|
|
||||||
|
The patch of the example below (set `"host"`, add `"retries"`) written back by each of them:
|
||||||
|
|
||||||
|
<div class="nj-jsonview-cmp" role="group" aria-label="The same patch written back by json, ordered_json, and json_editable_document">
|
||||||
|
<div><p class="nj-jsonview-cmp-h"><code>json</code></p><p class="nj-jsonview-cmp-v">keys sorted, numbers rewritten</p><pre>{<span class="nj-jsonview-bad">"host"</span>:"db2",
|
||||||
|
"name":"cache",
|
||||||
|
"port":6379,
|
||||||
|
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||||
|
"retries":3}</pre></div>
|
||||||
|
<div><p class="nj-jsonview-cmp-h"><code>ordered_json</code></p><p class="nj-jsonview-cmp-v">key order kept, numbers rewritten</p><pre>{"name":"cache",
|
||||||
|
"host":"db2",
|
||||||
|
"port":6379,
|
||||||
|
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||||
|
"retries":3}</pre></div>
|
||||||
|
<div class="nj-jsonview-cmp--best"><p class="nj-jsonview-cmp-h"><code>json_editable_document</code></p><p class="nj-jsonview-cmp-v">order and spelling kept</p><pre>{"name":"cache",
|
||||||
|
"host":"db2",
|
||||||
|
"port":6379,
|
||||||
|
"price":<span class="nj-jsonview-ok">19.90</span>,
|
||||||
|
"retries":3}</pre></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
An editable document keeps both. [`dump()`](../../api/basic_json_view/dump.md) of an edited document writes members in
|
||||||
|
document order — a member [`set`](../../api/basic_json_document/set.md) added goes at the end, exactly where it was
|
||||||
|
inserted, and an [`erase`](../../api/basic_json_document/erase.md)d member simply leaves a gap: everything around it
|
||||||
|
keeps its place — and [`number_format::source`](../../api/basic_json_view/number_format.md) keeps the exact spelling
|
||||||
|
of every number an edit did not itself touch; a number an edit *did* touch is written the way
|
||||||
|
[`BasicJsonType::dump()`](../../api/basic_json/dump.md) would write it, since there is no source spelling for a brand
|
||||||
|
new value. See [Writing a view back](reading.md#writing-a-view-back).
|
||||||
|
|
||||||
|
??? example "Example: patch a configuration, keeping member order and number spellings"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/json_editable_document.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/json_editable_document.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## What stays valid, and what an edit costs
|
||||||
|
|
||||||
|
The source text itself is **never written**, and the parsed index never moves — a value keeps the node it was
|
||||||
|
parsed into for as long as it is not itself replaced. So every [view](../../api/basic_json_view/index.md) taken before
|
||||||
|
an edit, including a previously obtained [`root()`](../../api/basic_json_document/root.md), stays valid and, if it
|
||||||
|
still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing
|
||||||
|
what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does
|
||||||
|
invalidate is the **iterators** taken over an edited array or object: the first time an element is added to it,
|
||||||
|
inserted into it, erased from it, or replaced by a value that spans a different number of nodes, its elements move
|
||||||
|
from the parsed, fixed layout to a growable block of links so that
|
||||||
|
[`push_back`](../../api/basic_json_document/push_back.md) can later grow it in amortized constant time — existing
|
||||||
|
elements are not touched, but an iterator that was walking the old layout no longer matches. A string obtained with
|
||||||
|
[`get_string()`](../../api/basic_json_view/get_string.md) is unaffected either way and stays valid across further
|
||||||
|
edits. See [`basic_json_document`'s Edits](../../api/basic_json_document/index.md#edits) for the details, and
|
||||||
|
[`set`'s Exception safety](../../api/basic_json_document/set.md#exception-safety) for what an edit guarantees if it
|
||||||
|
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
|
||||||
|
the index is described in the [architecture overview](../../home/architecture.md#node-index-of-json-views).
|
||||||
|
|
||||||
|
Step through the edits of a small document (the node fields follow the
|
||||||
|
[architecture overview](../../home/architecture.md#node-index-of-json-views)):
|
||||||
|
|
||||||
|
<div class="nj-jsonview-steps" data-nj-jsonview-steps>
|
||||||
|
<figure class="nj-jsonview-step" data-label="After parsing">
|
||||||
|
<div class="nj-jsonview-scroll">
|
||||||
|
--8<-- "images/json_view_edit_0.svg"
|
||||||
|
</div>
|
||||||
|
<figcaption>Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure class="nj-jsonview-step" data-label="set an existing member">
|
||||||
|
<div class="nj-jsonview-scroll">
|
||||||
|
--8<-- "images/json_view_edit_1.svg"
|
||||||
|
</div>
|
||||||
|
<figcaption>Step 2 of 5. <code>doc.set(doc.root(), "port", 6380)</code> overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure class="nj-jsonview-step" data-label="erase an array element">
|
||||||
|
<div class="nj-jsonview-scroll">
|
||||||
|
--8<-- "images/json_view_edit_2.svg"
|
||||||
|
</div>
|
||||||
|
<figcaption>Step 3 of 5. <code>doc.erase(doc.root()["tags"], 0)</code>: the array is edited for the first time, so its elements move to a block of links in owned storage. The node of <code>"eu"</code> stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure class="nj-jsonview-step" data-label="push_back onto the array">
|
||||||
|
<div class="nj-jsonview-scroll">
|
||||||
|
--8<-- "images/json_view_edit_3.svg"
|
||||||
|
</div>
|
||||||
|
<figcaption>Step 4 of 5. <code>doc.push_back(doc.root()["tags"], "new")</code>: the new string gets its own node, its text goes to the buffer of edited text, and a link to it is appended to the block of the array.</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure class="nj-jsonview-step" data-label="set a new member">
|
||||||
|
<div class="nj-jsonview-scroll">
|
||||||
|
--8<-- "images/json_view_edit_4.svg"
|
||||||
|
</div>
|
||||||
|
<figcaption>Step 5 of 5. <code>doc.set(doc.root(), "retries", 3)</code>: a new member, so the object moves to a block too: copies of its keys, links to the values it already had, then the new key and a link to the new integer.</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="nj-caption" markdown>The node index and the owned storage of the document after each of five edits. Use *Back*, *Next*, or the numbered buttons to switch between the steps; without JavaScript all steps are shown one below the other.</p>
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Zero-copy JSON views](index.md) - overview
|
||||||
|
- [`json_editable_document`](../../api/json_editable_document.md) - and [`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) - the documents that can be edited in place
|
||||||
|
- [`basic_json_document`'s Edits](../../api/basic_json_document/index.md#edits) - the rules of edits in detail
|
||||||
|
- [Differences from `basic_json`](differences.md#exception-context) - source positions after edits
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Writing generic code
|
||||||
|
|
||||||
|
## View types
|
||||||
|
|
||||||
|
A view's type names its `BasicJsonType` and its [`view_mode`](../../api/view_mode.md):
|
||||||
|
[`json_view`](../../api/json_view.md) is `#!cpp basic_json_view<json, view_mode::read_only>`, and
|
||||||
|
[`json_editable_view`](../../api/json_editable_view.md) is `#!cpp basic_json_view<json, view_mode::editable>`. These
|
||||||
|
are different types. The only conversion between them turns a view of a read-only document into a view of an
|
||||||
|
editable document of the same `BasicJsonType`, so a function written for one shortcut accepts few other views:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void print_names(json_view v); // accepts neither an ordered_json_view nor a json_editable_view
|
||||||
|
void print_names(json_editable_view v); // also accepts a json_view, but no ordered_json_view
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the same pitfall as a `to_json`/`from_json` written for `json` instead of any `BasicJsonType`, which then does
|
||||||
|
not work for `ordered_json`. The shortcuts are for declaring documents and views; a function that takes a view names
|
||||||
|
the views it accepts with a template:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// views of editable documents only
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
|
||||||
|
|
||||||
|
// views of read-only documents only
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
|
||||||
|
|
||||||
|
// views of either document type
|
||||||
|
template<typename BasicJsonType, view_mode Mode>
|
||||||
|
void f(basic_json_view<BasicJsonType, Mode> v);
|
||||||
|
```
|
||||||
|
|
||||||
|
Views are passed by value: they are two pointers, cheap to copy, like a `#!cpp std::string_view`. A `const&` works as
|
||||||
|
well, but a non-`const` reference does not bind to the views that
|
||||||
|
[`root()`](../../api/basic_json_document/root.md), [`operator[]`](../../api/basic_json_view/operator%5B%5D.md), and
|
||||||
|
[`at()`](../../api/basic_json_view/at.md) return, so `#!cpp f(doc.root())` would not compile.
|
||||||
|
|
||||||
|
There is no conversion from an editable view to a read-only one, by design: the mode decides how a view walks the
|
||||||
|
index. A read-only view reads a container in place, while a view of an editable document follows the links that edits
|
||||||
|
leave behind, so a read-only view of an edited document would read stale values. The other direction is safe: a
|
||||||
|
read-only document has no edits, so a view of it [converts](../../api/basic_json_view/basic_json_view.md) to an
|
||||||
|
editable view, which reads it like an editable document before its first edit. A template deduces the mode without
|
||||||
|
conversions, so the template forms above still accept only the modes they name. Views of both modes compare with
|
||||||
|
[`operator==`](../../api/basic_json_view/operator_eq.md).
|
||||||
|
|
||||||
|
[`is_json_view<T>`](../../api/is_json_view.md) tells views from other types, for constraints that cannot be written as
|
||||||
|
a `basic_json_view<...>` parameter.
|
||||||
|
|
||||||
|
## A `from_json()` that reads views
|
||||||
|
|
||||||
|
[`get<T>()`](../../api/basic_json_view/get.md) on a view calls `T`'s `from_json()` with the view itself if that
|
||||||
|
`from_json()` accepts views, and with [`materialize()`](../../api/basic_json_view/materialize.md) otherwise. The
|
||||||
|
`from_json()` functions of the [`NLOHMANN_DEFINE_TYPE_*`](../../api/macros/nlohmann_define_type_intrusive.md),
|
||||||
|
[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../../api/macros/nlohmann_define_derived_type.md), and
|
||||||
|
[`NLOHMANN_JSON_SERIALIZE_ENUM`](../../api/macros/nlohmann_json_serialize_enum.md) macros, and those of the library
|
||||||
|
for the standard types, accept views, so these types need nothing else. For decoding the statuses of
|
||||||
|
twitter.json into structs, reading the view is about 5 times faster than `#!cpp materialize().get<T>()`.
|
||||||
|
|
||||||
|
A hand-written `from_json()` reads views if it is constrained to `json` values and views:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template < typename J, typename std::enable_if <
|
||||||
|
nlohmann::is_json_view<J>::value || std::is_same<J, nlohmann::json>::value, int >::type = 0 >
|
||||||
|
void from_json(const J& j, person& p)
|
||||||
|
{
|
||||||
|
p.name = j.at("name").template get<std::string>();
|
||||||
|
p.age = j.at("age").template get<int>();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Its body may only use what views and `basic_json` have in common: the type checks, `at()`, `operator[]`,
|
||||||
|
`contains()`, `value()`, `size()`, iteration and `items()`, and `get<T>()`/`get_to()`. An unconstrained
|
||||||
|
`#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType& j, person& p)`, as in
|
||||||
|
[arbitrary type conversions](../arbitrary_types.md), is never called with a view, as it might use `basic_json`
|
||||||
|
functions a view does not have; such a type is converted through `materialize()`. A `from_json()` for `json` only is
|
||||||
|
converted through `materialize()` as well.
|
||||||
|
|
||||||
|
Reading the view and `#!cpp materialize().get<T>()` give the same result, except for an object with a repeated key;
|
||||||
|
see [Duplicate keys](differences.md#duplicate-keys).
|
||||||
|
|
||||||
|
`to_json()` is unaffected: it writes `basic_json` values, never views.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Zero-copy JSON views](index.md) - overview
|
||||||
|
- [`is_json_view`](../../api/is_json_view.md) - type trait to detect views
|
||||||
|
- [`view_mode`](../../api/view_mode.md) - read-only and editable views
|
||||||
|
- [Reading values](reading.md) - what `get<T>()` converts without materializing
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# Images
|
||||||
|
|
||||||
|
[`save()`](../../api/basic_json_document/save.md) writes a document as an *image*: a byte buffer that
|
||||||
|
[`load()`](../../api/basic_json_document/load.md) reads back into a document without parsing — no lexing, no building
|
||||||
|
the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where
|
||||||
|
[`parse_copy()`](../../api/basic_json_document/parse_copy.md) still has to scan the whole input,
|
||||||
|
[`load()`](../../api/basic_json_document/load.md) turns that scan into a copy of the node index alone.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
A document that is parsed once and then read many times — a configuration loaded at startup, a template rendered on
|
||||||
|
every request, a large reference dataset a worker process needs in memory — pays for parsing once but can amortize
|
||||||
|
[`save()`](../../api/basic_json_document/save.md)'s cost across every later load. That makes images useful for a
|
||||||
|
cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache), and
|
||||||
|
[`load()`](../../api/basic_json_document/load.md) it on every later use instead of parsing the source text again.
|
||||||
|
They are just as useful for handing a parsed document to another process (or a forked worker) running the same build
|
||||||
|
of the library, since [`load()`](../../api/basic_json_document/load.md) turns the transfer into a copy of the node
|
||||||
|
index plus pointers into the received bytes, not a re-parse.
|
||||||
|
|
||||||
|
## Choosing a check
|
||||||
|
|
||||||
|
[`load()`](../../api/basic_json_document/load.md) takes an
|
||||||
|
[`image_check`](../../api/basic_json_document/load.md#image_check) that trades validation against speed:
|
||||||
|
|
||||||
|
- `image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as
|
||||||
|
safe to read and serialize as a freshly parsed document — the right choice whenever the image did not come straight
|
||||||
|
from this process's own [`save()`](../../api/basic_json_document/save.md), such as a file or a network peer.
|
||||||
|
- `image_check::bounds` only checks structure and bounds — cheaper, since it skips scanning the text and the decoded
|
||||||
|
strings — and fits a cache the process trusts, one it wrote and reads back itself.
|
||||||
|
- `image_check::none` skips validation entirely, for an image trusted as much as the process's own memory.
|
||||||
|
|
||||||
|
See [`load()`'s Notes](../../api/basic_json_document/load.md#notes) for exactly what each level does and does not
|
||||||
|
guarantee.
|
||||||
|
|
||||||
|
??? example "Example: cache a parsed configuration as an image"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_document__save.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_document__save.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "Experimental"
|
||||||
|
|
||||||
|
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
||||||
|
stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the
|
||||||
|
library build that wrote it — use it to cache a document or to hand one to another process running the *same*
|
||||||
|
build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by
|
||||||
|
a future library version.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
The idea of a document you can read without parsing comes from zero-copy formats such as
|
||||||
|
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
|
||||||
|
[`load()`](../../api/basic_json_document/load.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from
|
||||||
|
either.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Zero-copy JSON views](index.md) - overview
|
||||||
|
- [`save()`](../../api/basic_json_document/save.md) - write a document as an image
|
||||||
|
- [`load()`](../../api/basic_json_document/load.md) - read an image back into a document
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# Zero-copy JSON views
|
||||||
|
|
||||||
|
`#!cpp <nlohmann/json_view.hpp>` adds a read-only, non-owning way to look at a parsed JSON text, as an alternative to
|
||||||
|
building a [`basic_json`](../../api/basic_json/index.md) tree with [`parse()`](../../api/basic_json/parse.md).
|
||||||
|
|
||||||
|
## The problem
|
||||||
|
|
||||||
|
[`basic_json::parse()`](../../api/basic_json/parse.md) builds a tree of `basic_json` values: one allocation for every
|
||||||
|
array and object, and every string copied into its own `std::string`. That is the right trade-off when the program
|
||||||
|
goes on to read and write the value freely, but it does more work than necessary when only a small part of a large
|
||||||
|
JSON text is actually needed, or when the same text is parsed over and over (many small messages, for instance) and
|
||||||
|
most of the resulting tree is thrown away almost immediately.
|
||||||
|
|
||||||
|
## The idea
|
||||||
|
|
||||||
|
[`basic_json_document::parse()`](../../api/basic_json_document/parse.md) parses the same JSON grammar, with the same
|
||||||
|
options, but instead of a tree it builds a flat index of the values it found: one
|
||||||
|
[16-byte entry](../../home/architecture.md#node-index-of-json-views) per value (and one per object key), in document
|
||||||
|
order. Integers are converted while parsing and stored in their entry. Floating-point numbers and strings are
|
||||||
|
referenced in place in the source text: a float is converted when it is read, and only strings that contain escape
|
||||||
|
sequences are decoded, into one shared buffer owned by the document.
|
||||||
|
|
||||||
|
[`basic_json_view`](../../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
||||||
|
index. It gives you the read-only, type-inspection part of the `basic_json` interface —
|
||||||
|
[`type()`](../../api/basic_json_view/type.md) and the `is_*()` predicates,
|
||||||
|
[`size()`](../../api/basic_json_view/size.md)/[`empty()`](../../api/basic_json_view/empty.md) — as well as element
|
||||||
|
access ([`operator[]`](../../api/basic_json_view/operator%5B%5D.md), [`at`](../../api/basic_json_view/at.md),
|
||||||
|
[`front`](../../api/basic_json_view/front.md)/[`back`](../../api/basic_json_view/back.md)), lookup
|
||||||
|
([`find`](../../api/basic_json_view/find.md), [`contains`](../../api/basic_json_view/contains.md),
|
||||||
|
[`count`](../../api/basic_json_view/count.md)), and iteration
|
||||||
|
([`begin`](../../api/basic_json_view/begin.md)/[`end`](../../api/basic_json_view/end.md),
|
||||||
|
[`items`](../../api/basic_json_view/items.md)) — without ever allocating a `basic_json` value. When you do need an
|
||||||
|
actual `basic_json` value for a subtree, [`materialize()`](../../api/basic_json_view/materialize.md) builds exactly the
|
||||||
|
one [`parse()`](../../api/basic_json/parse.md) would have produced for it.
|
||||||
|
|
||||||
|
The same small text, parsed both ways:
|
||||||
|
|
||||||
|
<div class="nj-fig-wrap" markdown>
|
||||||
|
--8<-- "images/json_view_layout.svg"
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="nj-caption" markdown>Top: the tree of separate heap allocations that `basic_json::parse()` builds. Bottom: `json_document::parse()` keeps the source text and builds one flat index of 16-byte nodes that point back into it, plus a buffer of decoded strings.</p>
|
||||||
|
|
||||||
|
## How to use it
|
||||||
|
|
||||||
|
Include `<nlohmann/json_view.hpp>` in addition to (or instead of) `<nlohmann/json.hpp>`. Parse into a
|
||||||
|
[`json_document`](../../api/json_document.md), inspect its [`root()`](../../api/basic_json_document/root.md), and
|
||||||
|
`materialize()` when you need a real value. `materialize()` works on any view, not just the root, so you can turn just
|
||||||
|
the subtree you need into a value:
|
||||||
|
|
||||||
|
??? example "Example: parse a document, inspect its root, and materialize it"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/json_document.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/json_document.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
[`ordered_json_document`](../../api/ordered_json_document.md) is the equivalent for
|
||||||
|
[`ordered_json`](../../api/ordered_json.md), just as [`ordered_json`](../../api/ordered_json.md) is to
|
||||||
|
[`json`](../../api/json.md).
|
||||||
|
|
||||||
|
A view behaves like a `basic_json` value in most respects, but not in all: lookups of a repeated key, the order in
|
||||||
|
which an object's members are visited, and what a missing key does to chained `operator[]` are the most visible
|
||||||
|
differences. [Differences from `basic_json`](differences.md) lists all of them.
|
||||||
|
|
||||||
|
## Where to go next
|
||||||
|
|
||||||
|
- [Ownership and lifetime](lifetime.md) - when a document borrows its source text, and how long a view stays valid
|
||||||
|
- [Differences from `basic_json`](differences.md) - everything a view does differently from a parsed `basic_json` value
|
||||||
|
- [Reading values](reading.md) - converting with `get<T>()`, reading strings and numbers without copying, and writing a
|
||||||
|
view back with `dump()`
|
||||||
|
- [Writing generic code](generic_code.md) - functions and `from_json()` overloads that accept views
|
||||||
|
- [Editing a document](editing.md) - patching a document in place, keeping its member order and number spellings
|
||||||
|
- [Images](images.md) - saving a parsed document and loading it back without parsing
|
||||||
|
|
||||||
|
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
||||||
|
|
||||||
|
| | [`json`](../../api/json.md) / [`ordered_json`](../../api/ordered_json.md) | [SAX interface](../parsing/sax_interface.md) | [`json_document`](../../api/json_document.md) / [`json_view`](../../api/json_view.md) | [`json_editable_document`](../../api/json_editable_document.md) / [`json_editable_view`](../../api/json_editable_view.md) |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
|
||||||
|
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](../../api/basic_json_document/set.md)/[`push_back`](../../api/basic_json_document/push_back.md)/[`insert`](../../api/basic_json_document/insert.md)/[`erase`](../../api/basic_json_document/erase.md) edit in place; the source text is never rewritten |
|
||||||
|
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](../../api/basic_json_view/dump.md) of an edited document that keeps the member order and, with [`number_format::source`](../../api/basic_json_view/number_format.md), the spelling of every untouched number |
|
||||||
|
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back — a configuration file, for instance — where the rest of it should come back exactly as it was |
|
||||||
|
| **Caching/reload** | not applicable — re-parse, or roll your own serialization | not applicable | [`save()`](../../api/basic_json_document/save.md)/[`load()`](../../api/basic_json_document/load.md): cache the parsed index as an [image](images.md) and reload it without parsing | same, saving the document's current — possibly edited — state |
|
||||||
|
|
||||||
|
<p class="nj-table-caption" markdown>Each column is one way to work with JSON in this library, and each row compares one property of them.</p>
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [`basic_json_document`](../../api/basic_json_document/index.md) - the document that owns the parsed index (parse, access, images, edits)
|
||||||
|
- [`basic_json_view`](../../api/basic_json_view/index.md) - the non-owning handle to a value in a document
|
||||||
|
- [`json_document`](../../api/json_document.md) - and [`ordered_json_document`](../../api/ordered_json_document.md) - the read-only documents
|
||||||
|
- [`json_editable_document`](../../api/json_editable_document.md) - and [`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) - the documents that can be edited in place
|
||||||
|
- [Architecture](../../home/architecture.md#node-index-of-json-views) - the layout of the node index
|
||||||
|
- [SAX interface](../parsing/sax_interface.md) - event-based parsing without building a tree
|
||||||
|
- [Performance](../performance.md) - benchmarks and tuning of parsing and serialization
|
||||||
|
- [Large, Streamed, and Partial Input](../parsing/large_and_partial_input.md) - reading only the part of a text you need
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Ownership and lifetime
|
||||||
|
|
||||||
|
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call
|
||||||
|
[`owns_source()`](../../api/basic_json_document/owns_source.md) to find out which happened.
|
||||||
|
[`parse()`](../../api/basic_json_document/parse.md) decides this from the value category and type of its argument (an
|
||||||
|
lvalue `#!cpp std::string` is borrowed; an rvalue `#!cpp std::string` is moved in, owned without a copy; a stream is
|
||||||
|
read into an owned buffer; and so on — see [`parse`'s Notes](../../api/basic_json_document/parse.md#notes) for the
|
||||||
|
full table). [`parse_copy()`](../../api/basic_json_document/parse_copy.md) always owns a copy, regardless of the input.
|
||||||
|
|
||||||
|
!!! warning "A borrowed document depends on your buffer"
|
||||||
|
|
||||||
|
If a document borrows its text, that text **must outlive the document** (and every view taken from it) and must
|
||||||
|
stay **unmodified** for as long as the document exists, since floating-point values and unescaped strings are
|
||||||
|
read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior,
|
||||||
|
exactly as it would be for a dangling `#!cpp std::string_view`.
|
||||||
|
|
||||||
|
## When a view is valid
|
||||||
|
|
||||||
|
A view is valid only while all of the following hold:
|
||||||
|
|
||||||
|
- the document is alive,
|
||||||
|
- the document has not been re-parsed since the view was taken (with
|
||||||
|
[`read()`](../../api/basic_json_document/read.md) or [`parse()`](../../api/basic_json_document/parse.md) into it),
|
||||||
|
- the document has not had [`shrink_to_fit()`](../../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
||||||
|
- if the document borrows its source text, that text is still alive and unmodified.
|
||||||
|
|
||||||
|
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap
|
||||||
|
allocation that keeps its address across the move. Take a fresh view from
|
||||||
|
[`root()`](../../api/basic_json_document/root.md) whenever any of the other conditions above was not met. Edits of an
|
||||||
|
[editable document](editing.md) keep views valid, too; see [What stays valid](editing.md#what-stays-valid-and-what-an-edit-costs).
|
||||||
|
|
||||||
|
<div class="nj-fig-wrap" markdown>
|
||||||
|
--8<-- "images/json_view_lifetime.svg"
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="nj-caption" markdown>Four timelines of a document and its views, each bar showing when an object is alive or valid and when it dangles. Destroying the borrowed text, re-parsing, or shrinking the document invalidates the views taken before; moving the document does not.</p>
|
||||||
|
|
||||||
|
## Why views can dangle
|
||||||
|
|
||||||
|
A view can dangle, exactly like a `#!cpp std::string_view`: nothing stops you from using it after its document is
|
||||||
|
gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `#!cpp std::shared_ptr` in
|
||||||
|
the document and a `#!cpp std::weak_ptr` in each view) would make it larger, add reference counting to every copy,
|
||||||
|
and a `lock()` to every access — exactly the costs the view exists to avoid. What is caught is the obvious case:
|
||||||
|
because a view dies with its document, [`root()`](../../api/basic_json_document/root.md) is not callable on a
|
||||||
|
temporary document: `#!cpp auto v = json_document::parse(text).root();` does not compile. Give the document a name
|
||||||
|
first.
|
||||||
|
|
||||||
|
The same holds for what a view hands out without copying: a string from
|
||||||
|
[`get_string()`](../../api/basic_json_view/get_string.md), a token 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.
|
||||||
|
|
||||||
|
??? example "Example: borrowed and owned documents, and when views become invalid"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/json_view_ownership.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/json_view_ownership.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Zero-copy JSON views](index.md) - overview
|
||||||
|
- [`basic_json_document`](../../api/basic_json_document/index.md) - the document that owns the parsed index
|
||||||
|
- [`owns_source()`](../../api/basic_json_document/owns_source.md) - whether a document borrows its source text
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Reading values
|
||||||
|
|
||||||
|
## Getting values out without copying
|
||||||
|
|
||||||
|
[`get<T>()`](../../api/basic_json_view/get.md) converts most `T` directly from the flat index, without ever building a
|
||||||
|
`basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`,
|
||||||
|
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json_view` itself, the standard containers
|
||||||
|
and other types the library converts (`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::list`, `#!cpp std::array`,
|
||||||
|
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, ...), and the types of the `NLOHMANN_DEFINE_TYPE_*` and
|
||||||
|
`NLOHMANN_JSON_SERIALIZE_ENUM` macros. Types whose `from_json()` is written for `basic_json` only go through
|
||||||
|
[`materialize()`](../../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real
|
||||||
|
`basic_json` value first, exactly as [`parse()`](../../api/basic_json/parse.md) would, and converted from there. See
|
||||||
|
[A `from_json()` that reads views](generic_code.md#a-from_json-that-reads-views) for how to write a `from_json()` that
|
||||||
|
reads views, and [Duplicate keys](differences.md#duplicate-keys) for the one case in which the two ways give different
|
||||||
|
results.
|
||||||
|
|
||||||
|
Two conversions never copy at all:
|
||||||
|
|
||||||
|
- [`get_string()`](../../api/basic_json_view/get_string.md) (equivalently, `#!cpp get<string_view_t>()`) returns a
|
||||||
|
string as a `string_view_t` pointing into the document's [`source()`](../../api/basic_json_document/source.md) text
|
||||||
|
— or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of
|
||||||
|
allocating a new `#!cpp std::string`.
|
||||||
|
- [`number_token()`](../../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
|
||||||
|
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
|
||||||
|
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
|
||||||
|
[`basic_json::parse()`](../../api/basic_json/parse.md)) would.
|
||||||
|
|
||||||
|
Both results are only valid as long as the document and, if it [borrows its source text](lifetime.md), that text.
|
||||||
|
|
||||||
|
### `string_view` members
|
||||||
|
|
||||||
|
A struct whose member is a `#!cpp std::string_view` (C++17) can be decoded from a view with the
|
||||||
|
`NLOHMANN_DEFINE_TYPE_*` macros, and the member is not copied either: for a string without escape sequences it points
|
||||||
|
into the document's source text, and for a string with escape sequences into the document's buffer of decoded
|
||||||
|
strings. The member is valid as long as the document is alive and, for a document that
|
||||||
|
[borrows its source text](lifetime.md) (as [`parse()`](../../api/basic_json_document/parse.md) does for an lvalue
|
||||||
|
`#!cpp std::string`, a `#!cpp std::string_view`, or a C string), as long as that text is.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct record
|
||||||
|
{
|
||||||
|
std::string_view name;
|
||||||
|
int count;
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string text = R"({"name": "widget", "count": 3})";
|
||||||
|
const json_document doc = json_document::parse(text); // borrows text
|
||||||
|
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "Decoding into a `string_view` from a `json` value dangles at once"
|
||||||
|
|
||||||
|
The same struct also compiles with `#!cpp json::parse(text).get<record>()`, but there `name` points into the
|
||||||
|
temporary `json` value, which is destroyed at the end of the statement. Use a named
|
||||||
|
[`json_document`](../../api/json_document.md), as above, to decode such a struct.
|
||||||
|
|
||||||
|
## Writing a view back
|
||||||
|
|
||||||
|
[`dump()`](../../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
|
||||||
|
`basic_json` value. It writes object members in document order and every occurrence of a repeated key, so it can
|
||||||
|
differ from `#!cpp materialize().dump()`; see [Serialization](differences.md#serialization).
|
||||||
|
|
||||||
|
By default, numbers are written the way [`basic_json::dump()`](../../api/basic_json/dump.md) would.
|
||||||
|
[`number_format::source`](../../api/basic_json_view/number_format.md) instead copies every number exactly as it was
|
||||||
|
written in the source text — a price like `#!cpp 19.90`, a long order or account ID with more digits than any number
|
||||||
|
type holds, or a high-precision coordinate — something `basic_json` cannot do at all, since parsing already reduces
|
||||||
|
a number to its parsed `#!cpp double`/`#!cpp int64_t` value. Together with an [editable document](editing.md), this
|
||||||
|
lets you patch a file without reformatting the rest of it.
|
||||||
|
|
||||||
|
[`operator<<`](../../api/basic_json_view/operator_ltlt.md) writes a view to a stream the way `basic_json`'s does, using
|
||||||
|
the stream's `width`/`fill` for indentation.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Zero-copy JSON views](index.md) - overview
|
||||||
|
- [`get`](../../api/basic_json_view/get.md) - convert a view
|
||||||
|
- [`dump`](../../api/basic_json_view/dump.md) - serialize a view
|
||||||
|
- [Writing generic code](generic_code.md) - functions and `from_json()` overloads that accept views
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -88,7 +88,7 @@ a.erase(1); // [1,3,4] (erase by index)
|
|||||||
## Editing a document
|
## Editing a document
|
||||||
|
|
||||||
The operations above work on a `basic_json` value. A JSON text that was parsed into a
|
The operations above work on a `basic_json` value. A JSON text that was parsed into a
|
||||||
[document](json_view.md#editing-a-document) can also be changed in place, without building a `basic_json` tree for the
|
[document](json_view/editing.md) can also be changed in place, without building a `basic_json` tree for the
|
||||||
parts that are not touched: [`set`](../api/basic_json_document/set.md) replaces a value, adds or replaces an object
|
parts that are not touched: [`set`](../api/basic_json_document/set.md) replaces a value, adds or replaces an object
|
||||||
member, or replaces an array element (also through a JSON pointer),
|
member, or replaces an array element (also through a JSON pointer),
|
||||||
[`push_back`](../api/basic_json_document/push_back.md) appends to an array,
|
[`push_back`](../api/basic_json_document/push_back.md) appends to an array,
|
||||||
@@ -105,7 +105,7 @@ editable document types, such as [`json_editable_document`](../api/json_editable
|
|||||||
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7396 merge patch
|
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7396 merge patch
|
||||||
- [`erase`](../api/basic_json/erase.md) - / [`clear`](../api/basic_json/clear.md) - remove elements
|
- [`erase`](../api/basic_json/erase.md) - / [`clear`](../api/basic_json/clear.md) - remove elements
|
||||||
- [JSON Patch and Diff](json_patch.md) - and [JSON Merge Patch](merge_patch.md) - structured modifications
|
- [JSON Patch and Diff](json_patch.md) - and [JSON Merge Patch](merge_patch.md) - structured modifications
|
||||||
- [Editing a document](json_view.md#editing-a-document) - change a parsed document in place
|
- [Editing a document](json_view/editing.md) - change a parsed document in place
|
||||||
- [`insert`](../api/basic_json/insert.md) - insert elements at an iterator position or merge a range
|
- [`insert`](../api/basic_json/insert.md) - insert elements at an iterator position or merge a range
|
||||||
- [`operator+=`](../api/basic_json/operator+=.md) - shorthand for `push_back`
|
- [`operator+=`](../api/basic_json/operator+=.md) - shorthand for `push_back`
|
||||||
- [Element Access](element_access/index.md) - read elements
|
- [Element Access](element_access/index.md) - read elements
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
# Error Recovery
|
|
||||||
|
|
||||||
By default, parsing stops at the first error. With the [SAX interface](sax_interface.md), you can instead ask the
|
|
||||||
parser to *recover*: to repair the error and continue, so that you get as much as possible out of malformed input, for
|
|
||||||
instance a file that was cut off, JSON edited by hand, or the output of a language model.
|
|
||||||
|
|
||||||
## Recovering from errors
|
|
||||||
|
|
||||||
The SAX parser's [`parse_error`](../../api/json_sax/parse_error.md) function is called for every error. Its return value
|
|
||||||
decides what happens next:
|
|
||||||
|
|
||||||
- `#!cpp false` stops parsing. This is what the SAX parsers of the library do, so [`parse`](../../api/basic_json/parse.md)
|
|
||||||
and [`accept`](../../api/basic_json/accept.md) never recover.
|
|
||||||
- `#!cpp true` repairs the error and continues parsing.
|
|
||||||
|
|
||||||
When recovering, the SAX parser still receives well-formed events: every `start_object` or `start_array` is followed by
|
|
||||||
the matching `end_object` or `end_array`, and every `key` is followed by exactly one value. A SAX parser that creates a
|
|
||||||
JSON value, such as the one in the example below, therefore gets a complete value. Parsing always ends, and
|
|
||||||
[`sax_parse`](../../api/basic_json/sax_parse.md) returns `#!cpp false` for input that is not valid JSON, even if every
|
|
||||||
error was repaired. Each token is reported at most once, and the SAX parser can stop at any error by returning
|
|
||||||
`#!cpp false`.
|
|
||||||
|
|
||||||
!!! example
|
|
||||||
|
|
||||||
The example below derives a SAX parser from the library's parser for `json` values (`json_sax_dom_parser`),
|
|
||||||
and recovers from all errors.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/sax_parse__error_recovery.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```
|
|
||||||
--8<-- "examples/sax_parse__error_recovery.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
## How errors are repaired
|
|
||||||
|
|
||||||
Each error is repaired with the smallest local edit: a missing separator is inserted, a stray token is removed, what can
|
|
||||||
be read of a broken string or number is kept, and a value that cannot be read at all becomes `#!json null`.
|
|
||||||
|
|
||||||
| Mistake | Repair | Example | Result |
|
|
||||||
|---------------------------|--------------------------------------------------------------------------------|------------------------------------------|----------------------------|
|
|
||||||
| missing `,` or `:` | inserted | `#!json [1 2]`, `#!json {"a" 1}` | `[1,2]`, `{"a":1}` |
|
|
||||||
| missing value | `#!json null` for an object key or between commas in an array | `#!json {"a":}`, `#!json [1,,2]` | `{"a":null}`, `[1,null,2]` |
|
|
||||||
| trailing comma | removed | `#!json [1,2,]` | `[1,2]` |
|
|
||||||
| broken string | invalid escapes and bytes are replaced (see below); a line break ends the string | `#!json ["a\qb"]` | `["aqb"]` |
|
|
||||||
| broken number | the longest valid beginning is kept | `#!json [1., 2e+]` | `[1,2]` |
|
|
||||||
| unreadable value | `#!json null` | `#!json [1, NaN, tru]` | `[1,null,null]` |
|
|
||||||
| number too large | passed as infinity, together with its text | `#!json [1e999]` | infinity (see below) |
|
|
||||||
| stray `:` | removed | `#!json ["a":1]` | `["a",1]` |
|
|
||||||
| member without a key | skipped up to the next `,` or `}` | `#!json {1:2, "b":3}` | `{"b":3}` |
|
|
||||||
| wrong closing bracket | closes the innermost array or object | `#!json {"a":[1,2}, "b":3}` | `{"a":[1,2],"b":3}` |
|
|
||||||
| input ends too early | all open arrays and objects are closed | `#!json {"a":[1,2` | `{"a":[1,2]}` |
|
|
||||||
| text before the value | skipped | `#!json )]}'{"a":1}` | `{"a":1}` |
|
|
||||||
|
|
||||||
In a string, an unknown escape like `\q` stands for the escaped character (`q`), as in JavaScript. An invalid `\u`
|
|
||||||
escape, a lone surrogate, and ill-formed UTF-8 are each replaced by U+FFFD (REPLACEMENT CHARACTER), and control
|
|
||||||
characters are kept. A string without its closing quote ends at the next line break or at the end of the input.
|
|
||||||
|
|
||||||
The input after the top-level value is not repaired: as without recovery, it is reported as an error, and parsing stops.
|
|
||||||
|
|
||||||
## Binary formats
|
|
||||||
|
|
||||||
The binary formats ([BJData](../binary_formats/bjdata.md), [BON8](../binary_formats/bon8.md),
|
|
||||||
[BSON](../binary_formats/bson.md), [CBOR](../binary_formats/cbor.md), [MessagePack](../binary_formats/messagepack.md),
|
|
||||||
and [UBJSON](../binary_formats/ubjson.md)) have no delimiters to find the next value by. So what can be repaired depends
|
|
||||||
on whether the end of the item with the error is known, a distinction that
|
|
||||||
[RFC 8949, Section 5.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3) makes for CBOR, too.
|
|
||||||
|
|
||||||
If the item is complete, but cannot be passed on as it is, it is replaced, and parsing continues after it:
|
|
||||||
|
|
||||||
| Mistake | Formats | Repair |
|
|
||||||
|---------------------------------------------------------------------|-------------------------|-------------------------------------------------------------------------|
|
|
||||||
| tag, if `tag_handler` is `cbor_tag_handler_t::error` (the default) | CBOR | ignored |
|
|
||||||
| simple value other than `false`, `true`, and `null`, like undefined | CBOR | `#!json null` |
|
|
||||||
| number too large for a custom `number_float_t`, like `float` | all | infinity |
|
|
||||||
| character (`C`) that is not ASCII | BJData, UBJSON | U+FFFD |
|
|
||||||
| invalid high-precision number (`H`) | BJData, UBJSON | the longest valid beginning is kept, as for JSON text, or `#!json null` |
|
|
||||||
| high-precision number too large | BJData, UBJSON | passed as infinity, together with its text |
|
|
||||||
| object key that is not a string | BON8, CBOR, MessagePack | the member is skipped |
|
|
||||||
| element of a type the library does not read, like ObjectId or date | BSON | `#!json null` |
|
|
||||||
| string without its terminator | BSON | kept |
|
|
||||||
| document whose size does not match its content | BSON | kept |
|
|
||||||
|
|
||||||
CBOR tags and simple values are repaired as [RFC 8949, Section 6.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-6.1)
|
|
||||||
suggests for converting CBOR to JSON. By default, [`sax_parse`](../../api/basic_json/sax_parse.md) reports every tag as
|
|
||||||
an error; when recovering, tags are then ignored like with
|
|
||||||
[`cbor_tag_handler_t::ignore`](../../api/basic_json/cbor_tag_handler_t.md). Strings that are not valid UTF-8 are no
|
|
||||||
error: like [`from_cbor`](../../api/basic_json/from_cbor.md) and the other functions by default, `sax_parse` passes
|
|
||||||
them on as they are.
|
|
||||||
|
|
||||||
After any other error, the end of the item is unknown: the input ended, a byte is not a valid type marker, or a size
|
|
||||||
cannot be right. Parsing then stops, and the value read so far is completed: a key that waits for its value gets
|
|
||||||
`#!json null`, and all open arrays and objects are closed. This keeps everything before the error of an input that was
|
|
||||||
cut off. The exception is BSON, which stores the size of every document: an element whose end is unknown gets
|
|
||||||
`#!json null`, the rest of its document is skipped, and parsing continues after the document.
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- A repair is a guess. For example, `#!json {"a" "b": 1}` could be meant as `#!json {"a": "b"}` or as
|
|
||||||
`#!json {"a": null, "b": 1}`; it is repaired to the former. Treat recovered values as a best effort, and check the
|
|
||||||
reported errors.
|
|
||||||
- A closing bracket always closes the innermost array or object. If a bracket is missing rather than wrong, the
|
|
||||||
repair differs from the intention: `#!json {"a": {"b": [1, 2}, "c": 3}` is repaired to
|
|
||||||
`#!json {"a": {"b": [1, 2], "c": 3}}`, although `#!json {"a": {"b": [1, 2]}, "c": 3}` may have been meant.
|
|
||||||
- Keys without quotes, and strings in single quotes, are not supported; such members are skipped.
|
|
||||||
- In the binary formats, a member that is skipped because its key is not a string is lost, and so are the elements of a
|
|
||||||
BSON document after one whose end is unknown.
|
|
||||||
- A number that is too large for `number_float_t` is passed as positive or negative infinity. The SAX parser's
|
|
||||||
`number_float` also gets the number's text, but a JSON value cannot store it, and
|
|
||||||
[`dump`](../../api/basic_json/dump.md) serializes infinity as `#!json null`.
|
|
||||||
- When parsing is not strict (see [`sax_parse`](../../api/basic_json/sax_parse.md)), a repair may read parts of the
|
|
||||||
input after the value, for instance of the next value in a stream of concatenated values.
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
|
||||||
- [`parse_error`](../../api/json_sax/parse_error.md) - the SAX event for parse errors
|
|
||||||
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
|
|
||||||
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
|
||||||
@@ -80,13 +80,21 @@ them faster than others:
|
|||||||
| `#!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; 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 |
|
| other iterator ranges (e.g., `#!cpp std::list<char>`, `#!cpp std::istreambuf_iterator<char>`), wide strings | one character at a time |
|
||||||
|
|
||||||
Parsing a file from a `#!cpp std::ifstream` is therefore only about 10 to 20 % slower than reading the whole file into a
|
Input in one block of memory is read even faster with the default options (no
|
||||||
`#!cpp std::string` first and parsing that, and it needs no memory for a copy of the file. The stream is never read
|
[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
|
||||||
|
memory. Only after an error do `parse()` and `sax_parse()` read the input again, to report the error exactly as before.
|
||||||
|
This faster path is not used when [`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) is
|
||||||
|
enabled, because the positions are recorded by the default parser: the input is then parsed as before, at the same speed
|
||||||
|
as before.
|
||||||
|
|
||||||
|
Parsing a file from a `#!cpp std::ifstream` therefore takes about 1.15 to 1.45 times as long as reading the whole file
|
||||||
|
into a `#!cpp std::string` first and parsing that, but it needs no memory for a copy of the file. The stream is never read
|
||||||
ahead: after parsing, it is positioned exactly as if every character had been read one at a time, so
|
ahead: after parsing, it is positioned exactly as if every character had been read one at a time, so
|
||||||
[`operator>>`](../../api/operator_gtgt.md) can still read several values from one stream (see also
|
[`operator>>`](../../api/operator_gtgt.md) can still read several values from one stream (see also
|
||||||
[JSON Lines](json_lines.md)).
|
[JSON Lines](json_lines.md)).
|
||||||
|
|
||||||
This does not hold for [`json_document`](../json_view.md): it reads a stream into a buffer it owns, so the stream saves
|
This does not hold for [`json_document`](../json_view/index.md): it reads a stream into a buffer it owns, so the stream saves
|
||||||
no memory, and reading the file into a `#!cpp std::string` yourself and borrowing it is about 6 to 12 % faster.
|
no memory, and reading the file into a `#!cpp std::string` yourself and borrowing it is about 6 to 12 % faster.
|
||||||
|
|
||||||
Two macros switch these optimizations off without changing any result:
|
Two macros switch these optimizations off without changing any result:
|
||||||
@@ -115,7 +123,7 @@ The library offers two parsing models, and a third, read-only alternative:
|
|||||||
reads the same JSON grammar but builds a flat, read-only index of the values instead of a `basic_json` tree. Strings and
|
reads the same JSON grammar but builds a flat, read-only index of the values instead of a `basic_json` tree. Strings and
|
||||||
floating-point numbers are read from the text on demand, and only the parts you
|
floating-point numbers are read from the text on demand, and only the parts you
|
||||||
[`materialize()`](../../api/basic_json_view/materialize.md) become `basic_json` values. This avoids most of the work when you
|
[`materialize()`](../../api/basic_json_view/materialize.md) become `basic_json` values. This avoids most of the work when you
|
||||||
need only a few values from a large or frequently parsed text; see [Zero-copy JSON views](../json_view.md). It is read-only, so use
|
need only a few values from a large or frequently parsed text; see [Zero-copy JSON views](../json_view/index.md). It is read-only, so use
|
||||||
`parse` when you need to modify the value.
|
`parse` when you need to modify the value.
|
||||||
|
|
||||||
You can influence a DOM parse without switching to the SAX interface by passing a
|
You can influence a DOM parse without switching to the SAX interface by passing a
|
||||||
@@ -139,14 +147,14 @@ json_document doc = json_document::parse(R"({"happy": true, "pi": 3.141})");
|
|||||||
std::cout << doc.root()["happy"].get<bool>() << '\n';
|
std::cout << doc.root()["happy"].get<bool>() << '\n';
|
||||||
```
|
```
|
||||||
|
|
||||||
See [Zero-copy JSON views](../json_view.md) for the concept and the trade-offs.
|
See [Zero-copy JSON views](../json_view/index.md) for the concept and the trade-offs.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
|
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
|
||||||
unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used
|
unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used
|
||||||
to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available
|
to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available
|
||||||
options. To get as much as possible out of malformed input, a SAX parser can [recover from errors](error_recovery.md).
|
options.
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
@@ -158,9 +166,8 @@ options. To get as much as possible out of malformed input, a SAX parser can [re
|
|||||||
- [Large, streamed, and partial input](large_and_partial_input.md) - chunked, concatenated, and very large input
|
- [Large, streamed, and partial input](large_and_partial_input.md) - chunked, concatenated, and very large input
|
||||||
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
|
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
|
||||||
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
||||||
- [Zero-copy JSON views](../json_view.md) - parse into a read-only document and read it through views
|
- [Zero-copy JSON views](../json_view/index.md) - parse into a read-only document and read it through views
|
||||||
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
||||||
- [error recovery](error_recovery.md) - get as much as possible out of malformed input
|
|
||||||
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
|
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
|
||||||
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
|
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
|
||||||
- [`json_sax`](../../api/json_sax/index.md) - documentation of the SAX interface
|
- [`json_sax`](../../api/json_sax/index.md) - documentation of the SAX interface
|
||||||
|
|||||||
@@ -156,11 +156,11 @@ record the reason in the handler (here: `id`) if you need to tell the two apart.
|
|||||||
|
|
||||||
### Read parts of a large document with a view
|
### Read parts of a large document with a view
|
||||||
|
|
||||||
If the whole text is available and you only need to read some of it, a [`json_document`](../json_view.md) from
|
If the whole text is available and you only need to read some of it, a [`json_document`](../json_view/index.md) from
|
||||||
`<nlohmann/json_view.hpp>` (added in version 3.13.0) avoids the `basic_json` tree altogether: it keeps the text, builds a flat index of the values,
|
`<nlohmann/json_view.hpp>` (added in version 3.13.0) avoids the `basic_json` tree altogether: it keeps the text, builds a flat index of the values,
|
||||||
and gives read-only [views](../../api/basic_json_view/index.md) of the parts you look at. Only what you
|
and gives read-only [views](../../api/basic_json_view/index.md) of the parts you look at. Only what you
|
||||||
[`materialize()`](../../api/basic_json_view/materialize.md) becomes a `basic_json` value. See
|
[`materialize()`](../../api/basic_json_view/materialize.md) becomes a `basic_json` value. See
|
||||||
[Zero-copy JSON views](../json_view.md) for details and for the lifetime rules.
|
[Zero-copy JSON views](../json_view/index.md) for details and for the lifetime rules.
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ and gives read-only [views](../../api/basic_json_view/index.md) of the parts you
|
|||||||
- [JSON Lines](json_lines.md) - newline-delimited JSON
|
- [JSON Lines](json_lines.md) - newline-delimited JSON
|
||||||
- [Parser callbacks](parser_callbacks.md) - discard values while parsing
|
- [Parser callbacks](parser_callbacks.md) - discard values while parsing
|
||||||
- [SAX interface](sax_interface.md) - event-based parsing
|
- [SAX interface](sax_interface.md) - event-based parsing
|
||||||
- [Zero-copy JSON views](../json_view.md) - read parts of a large document without building a tree
|
- [Zero-copy JSON views](../json_view/index.md) - read parts of a large document without building a tree
|
||||||
- [Untrusted input](untrusted_input.md) - limits for input you do not control
|
- [Untrusted input](untrusted_input.md) - limits for input you do not control
|
||||||
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
|
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
|
||||||
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
|
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
|
||||||
|
|||||||
@@ -68,8 +68,7 @@ bool parse_error(std::size_t position,
|
|||||||
const json::exception& ex);
|
const json::exception& ex);
|
||||||
```
|
```
|
||||||
|
|
||||||
The return value decides whether to stop parsing (`#!cpp false`) or to repair the error and continue
|
The return value indicates whether the parsing should continue, so the function should usually return `#!cpp false`.
|
||||||
(`#!cpp true`); see [error recovery](error_recovery.md) for the latter.
|
|
||||||
|
|
||||||
??? example "Example: report parse errors without exceptions"
|
??? example "Example: report parse errors without exceptions"
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ bool key(string_t& val);
|
|||||||
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex);
|
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex);
|
||||||
```
|
```
|
||||||
|
|
||||||
The return value of each function determines whether parsing should proceed. For `parse_error`, returning
|
The return value of each function determines whether parsing should proceed.
|
||||||
`#!cpp true` [recovers from the error](error_recovery.md).
|
|
||||||
|
|
||||||
Object keys are passed to `key` decoded and validated: escape sequences are already resolved, so different spellings of
|
Object keys are passed to `key` decoded and validated: escape sequences are already resolved, so different spellings of
|
||||||
the same name yield the same string. See [`key`](../../api/json_sax/key.md#notes) for the exact guarantees.
|
the same name yield the same string. See [`key`](../../api/json_sax/key.md#notes) for the exact guarantees.
|
||||||
@@ -102,7 +101,7 @@ To implement your own SAX handler, proceed as follows:
|
|||||||
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
||||||
3. Call `#!cpp bool json::sax_parse(input, &my_sax);` where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
3. Call `#!cpp bool json::sax_parse(input, &my_sax);` where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
||||||
|
|
||||||
Note the `sax_parse` function only returns a `#!cpp bool` indicating whether the input was parsed without errors and no SAX event returned `#!cpp false`. It does not return `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file `json_sax.hpp`.
|
Note the `sax_parse` function only returns a `#!cpp bool` indicating the result of the last executed SAX event. It does not return `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file `json_sax.hpp`.
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ up. Nothing is read ahead, so the stream is left exactly where it would be after
|
|||||||
[`operator>>`](../api/operator_gtgt.md) can still read several values from one stream. A stream buffer without a get
|
[`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
|
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
|
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. According to the
|
[input sources and performance](parsing/index.md#input-sources-and-performance) for the full table. Parsing from a
|
||||||
description of [#5828](https://github.com/nlohmann/json/pull/5828), parsing from a stream is within about 10 to 20 % of
|
stream takes about 1.15 to 1.45 times as long as parsing an equivalent `#!cpp std::string`, which `parse()` reads with
|
||||||
parsing an equivalent `#!cpp std::string`.
|
a parser of its own (see there).
|
||||||
|
|
||||||
Inputs that are still read one character at a time: iterator ranges that are not contiguous (e.g.
|
Inputs that are still read one character at a time: iterator ranges that are not contiguous (e.g.
|
||||||
`#!cpp std::list<char>` or `#!cpp std::istreambuf_iterator<char>`), wide strings, and user-defined adapters. An
|
`#!cpp std::list<char>` or `#!cpp std::istreambuf_iterator<char>`), wide strings, and user-defined adapters. An
|
||||||
@@ -40,7 +40,7 @@ optimization but not the second, since the byte-scanning fast path requires cont
|
|||||||
|
|
||||||
Practically: a file can be passed directly to `parse`, which also avoids the memory for a copy of the file. Reading it
|
Practically: a file can be passed directly to `parse`, which also avoids the memory for a copy of the file. Reading it
|
||||||
into a string first is no longer necessary for speed (this is for `#!cpp json::parse`; a
|
into a string first is no longer necessary for speed (this is for `#!cpp json::parse`; a
|
||||||
[`json_document`](json_view.md) reads a stream into an owned buffer anyway, so for it reading the file into a string
|
[`json_document`](json_view/index.md) reads a stream into an owned buffer anyway, so for it reading the file into a string
|
||||||
and borrowing that is the faster way):
|
and borrowing that is the faster way):
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -73,12 +73,12 @@ a DOM, three alternatives avoid building it:
|
|||||||
discard finished elements as soon as they are handled, so memory usage stays bounded by one element (plus the
|
discard finished elements as soon as they are handled, so memory usage stays bounded by one element (plus the
|
||||||
unparsed remainder of the input) instead of the whole document — see the [recipe for streaming a large homogeneous
|
unparsed remainder of the input) instead of the whole document — see the [recipe for streaming a large homogeneous
|
||||||
array](parsing/parser_callbacks.md#recipe-streaming-a-large-homogeneous-array).
|
array](parsing/parser_callbacks.md#recipe-streaming-a-large-homogeneous-array).
|
||||||
- Parse into a [`json_document`](json_view.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
|
- Parse into a [`json_document`](json_view/index.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
|
||||||
the input text and builds a flat index of 16 bytes per value; strings and floating-point numbers are not copied, but
|
the input text and builds a flat index of 16 bytes per value; strings and floating-point numbers are not copied, but
|
||||||
read from the text when needed. Read-only [views](../api/basic_json_view/index.md) give the familiar element access, and only the
|
read from the text when needed. Read-only [views](../api/basic_json_view/index.md) give the familiar element access, and only the
|
||||||
parts you [`materialize()`](../api/basic_json_view/materialize.md) become `basic_json` values. A document that
|
parts you [`materialize()`](../api/basic_json_view/materialize.md) become `basic_json` values. A document that
|
||||||
borrows the text instead of owning a copy needs the text to outlive it; see
|
borrows the text instead of owning a copy needs the text to outlive it; see
|
||||||
[choosing between `json`, the SAX interface, and `json_view`](json_view.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view).
|
[choosing between `json`, the SAX interface, and `json_view`](json_view/index.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view).
|
||||||
|
|
||||||
If the data is naturally record-oriented, consider [JSON Lines](parsing/json_lines.md) instead of one large JSON
|
If the data is naturally record-oriented, consider [JSON Lines](parsing/json_lines.md) instead of one large JSON
|
||||||
document: reading and parsing it line by line with `#!cpp std::getline` means only one line's value is ever in memory
|
document: reading and parsing it line by line with `#!cpp std::getline` means only one line's value is ever in memory
|
||||||
@@ -182,11 +182,11 @@ an adapter, since their template argument order does not match what `basic_json`
|
|||||||
large value therefore doubles its memory; see [Avoiding copies](#avoiding-copies). `#!cpp auto x = j["key"];` is a
|
large value therefore doubles its memory; see [Avoiding copies](#avoiding-copies). `#!cpp auto x = j["key"];` is a
|
||||||
copy, `#!cpp auto& x = j["key"];` is not.
|
copy, `#!cpp auto& x = j["key"];` is not.
|
||||||
- **A moved-from value is `null`.** After `#!cpp json c = std::move(a);` the value `a` is `#!json null` and owns nothing.
|
- **A moved-from value is `null`.** After `#!cpp json c = std::move(a);` the value `a` is `#!json null` and owns nothing.
|
||||||
- **Reading does not require a tree.** If you only read the data, parse it into a [`json_document`](json_view.md)
|
- **Reading does not require a tree.** If you only read the data, parse it into a [`json_document`](json_view/index.md)
|
||||||
(`#!cpp <nlohmann/json_view.hpp>`, added in version 3.13.0) instead of a `basic_json`. It stores 16 bytes per value, does not copy floating-point
|
(`#!cpp <nlohmann/json_view.hpp>`, added in version 3.13.0) instead of a `basic_json`. It stores 16 bytes per value, does not copy floating-point
|
||||||
numbers or strings without escape sequences out of the text, and creates no `basic_json` value except for the parts
|
numbers or strings without escape sequences out of the text, and creates no `basic_json` value except for the parts
|
||||||
you [`materialize()`](../api/basic_json_view/materialize.md). A document that borrows the text needs the text to
|
you [`materialize()`](../api/basic_json_view/materialize.md). A document that borrows the text needs the text to
|
||||||
outlive it; see [Zero-copy JSON views](json_view.md).
|
outlive it; see [Zero-copy JSON views](json_view/index.md).
|
||||||
- **Containers keep their capacity.** `clear()` and `erase()` on an array remove the elements, but the underlying
|
- **Containers keep their capacity.** `clear()` and `erase()` on an array remove the elements, but the underlying
|
||||||
`#!cpp std::vector` keeps its allocated capacity, like `#!cpp std::vector::clear` does. To give the memory back,
|
`#!cpp std::vector` keeps its allocated capacity, like `#!cpp std::vector::clear` does. To give the memory back,
|
||||||
assign a new value (`#!cpp j = json::array();`) or destroy the value.
|
assign a new value (`#!cpp j = json::array();`) or destroy the value.
|
||||||
@@ -267,7 +267,7 @@ those headers are then never processed by the compiler at all.
|
|||||||
- [Architecture](../home/architecture.md) - how input adapters, the lexer, and the serializer fit together
|
- [Architecture](../home/architecture.md) - how input adapters, the lexer, and the serializer fit together
|
||||||
- [Parsing](parsing/index.md) - the available parsing functions and inputs
|
- [Parsing](parsing/index.md) - the available parsing functions and inputs
|
||||||
- [SAX interface](parsing/sax_interface.md) - parse without building a DOM
|
- [SAX interface](parsing/sax_interface.md) - parse without building a DOM
|
||||||
- [Zero-copy JSON views](json_view.md) - parse into a flat index of the text and read it without building a DOM
|
- [Zero-copy JSON views](json_view/index.md) - parse into a flat index of the text and read it without building a DOM
|
||||||
- [Binary formats](binary_formats/index.md) - compact alternatives to JSON text
|
- [Binary formats](binary_formats/index.md) - compact alternatives to JSON text
|
||||||
- [Object Order](object_order.md) - `json` vs. `ordered_json` and other `ObjectType` choices
|
- [Object Order](object_order.md) - `json` vs. `ordered_json` and other `ObjectType` choices
|
||||||
- [Template Parameter Requirements](types/template_parameters.md) - custom container and allocator types
|
- [Template Parameter Requirements](types/template_parameters.md) - custom container and allocator types
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ The public headers are in [`include/nlohmann`](https://github.com/nlohmann/json/
|
|||||||
- [`json_view.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_view.hpp) is a separate,
|
- [`json_view.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_view.hpp) is a separate,
|
||||||
optional header that defines [`basic_json_document`](../api/basic_json_document/index.md) and
|
optional header that defines [`basic_json_document`](../api/basic_json_document/index.md) and
|
||||||
[`basic_json_view`](../api/basic_json_view/index.md), a flat-index, read-only, non-owning way to look at a parsed
|
[`basic_json_view`](../api/basic_json_view/index.md), a flat-index, read-only, non-owning way to look at a parsed
|
||||||
JSON text; see [Zero-copy JSON views](../features/json_view.md). It builds on `json.hpp` internals (it requires the
|
JSON text; see [Zero-copy JSON views](../features/json_view/index.md). It builds on `json.hpp` internals (it requires the
|
||||||
same library version) and has its own `detail/view/` subdirectory.
|
same library version) and has its own `detail/view/` subdirectory.
|
||||||
|
|
||||||
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
|
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
|
||||||
@@ -175,7 +175,7 @@ maintains the invariant that the pointer matching `m_type` is never null; `asser
|
|||||||
|
|
||||||
## Node index of JSON views
|
## Node index of JSON views
|
||||||
|
|
||||||
A [`basic_json_document`](../api/basic_json_document/index.md) (see [Zero-copy JSON views](../features/json_view.md))
|
A [`basic_json_document`](../api/basic_json_document/index.md) (see [Zero-copy JSON views](../features/json_view/index.md))
|
||||||
does not build a tree of values. Its parser
|
does not build a tree of values. Its parser
|
||||||
([`detail/view/builder.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/builder.hpp))
|
([`detail/view/builder.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/builder.hpp))
|
||||||
writes a flat array of 16-byte nodes, one per value and one per object key, in document order. A
|
writes a flat array of 16-byte nodes, one per value and one per object key, in document order. A
|
||||||
@@ -466,7 +466,7 @@ release. Besides the components above, it contains:
|
|||||||
- [`adl_serializer`](../api/adl_serializer/index.md) - customization point for type conversions
|
- [`adl_serializer`](../api/adl_serializer/index.md) - customization point for type conversions
|
||||||
- [`json_sax`](../api/json_sax/index.md) - interface of the SAX parser events
|
- [`json_sax`](../api/json_sax/index.md) - interface of the SAX parser events
|
||||||
- [SAX Interface](../features/parsing/sax_interface.md) - how to consume parser events without building a DOM
|
- [SAX Interface](../features/parsing/sax_interface.md) - how to consume parser events without building a DOM
|
||||||
- [Zero-copy JSON views](../features/json_view.md) - the flat node index that backs views and documents
|
- [Zero-copy JSON views](../features/json_view/index.md) - the flat node index that backs views and documents
|
||||||
- [Template Parameter Requirements](../features/types/template_parameters.md) - the template parameters of basic_json
|
- [Template Parameter Requirements](../features/types/template_parameters.md) - the template parameters of basic_json
|
||||||
- [Serialization](../features/serialization.md) - the output side of the library
|
- [Serialization](../features/serialization.md) - the output side of the library
|
||||||
- [`nlohmann` Namespace](../features/namespace.md) - how the library namespace is structured
|
- [`nlohmann` Namespace](../features/namespace.md) - how the library namespace is structured
|
||||||
@@ -438,7 +438,7 @@ A UBJSON or BJData high-precision number could not be parsed.
|
|||||||
### json.exception.parse_error.116
|
### json.exception.parse_error.116
|
||||||
|
|
||||||
[`basic_json_document::load()`](../api/basic_json_document/load.md) rejected an
|
[`basic_json_document::load()`](../api/basic_json_document/load.md) rejected an
|
||||||
[image](../features/json_view.md#images): either the bytes are not one [`save()`](../api/basic_json_document/save.md)
|
[image](../features/json_view/images.md): either the bytes are not one [`save()`](../api/basic_json_document/save.md)
|
||||||
could have written (too short, an unknown magic number or format version, or sizes that do not fit the buffer), or
|
could have written (too short, an unknown magic number or format version, or sizes that do not fit the buffer), or
|
||||||
they are, but fail the requested [`image_check`](../api/basic_json_document/load.md#image_check).
|
they are, but fail the requested [`image_check`](../api/basic_json_document/load.md#image_check).
|
||||||
|
|
||||||
@@ -450,7 +450,7 @@ they are, but fail the requested [`image_check`](../api/basic_json_document/load
|
|||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
This exception was added in version 3.13.0, together with [images](../features/json_view/images.md).
|
||||||
|
|
||||||
## Iterator errors
|
## Iterator errors
|
||||||
|
|
||||||
@@ -889,12 +889,12 @@ from JSON text.
|
|||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
This exception was added in version 3.13.0, together with editable [`json_document`s](../features/json_view.md).
|
This exception was added in version 3.13.0, together with editable [`json_document`s](../features/json_view/editing.md).
|
||||||
|
|
||||||
### json.exception.type_error.320
|
### json.exception.type_error.320
|
||||||
|
|
||||||
[`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an
|
[`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an
|
||||||
[image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document.
|
[image](../features/json_view/images.md) of a [discarded](../api/basic_json_document/is_discarded.md) document.
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
@@ -904,7 +904,7 @@ from JSON text.
|
|||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
This exception was added in version 3.13.0, together with [images](../features/json_view/images.md).
|
||||||
|
|
||||||
### json.exception.type_error.321
|
### json.exception.type_error.321
|
||||||
|
|
||||||
@@ -1192,7 +1192,7 @@ so they do not support an input of 4294967280 bytes (4 GiB minus 16 bytes) or mo
|
|||||||
storage: [`set`](../api/basic_json_document/set.md) and [`push_back`](../api/basic_json_document/push_back.md) throw
|
storage: [`set`](../api/basic_json_document/set.md) and [`push_back`](../api/basic_json_document/push_back.md) throw
|
||||||
this exception once the strings and number tokens written by edits reach 4 GiB in total, or once more than
|
this exception once the strings and number tokens written by edits reach 4 GiB in total, or once more than
|
||||||
4294967295 arrays/objects have had an element set or appended to them. The same limit applies to an
|
4294967295 arrays/objects have had an element set or appended to them. The same limit applies to an
|
||||||
[image](../features/json_view.md#images): [`save()`](../api/basic_json_document/save.md) throws it if the node
|
[image](../features/json_view/images.md): [`save()`](../api/basic_json_document/save.md) throws it if the node
|
||||||
count, the text, or the decoded strings it would write would individually reach 4 GiB.
|
count, the text, or the decoded strings it would write would individually reach 4 GiB.
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example messages"
|
||||||
@@ -1209,7 +1209,7 @@ count, the text, or the decoded strings it would write would individually reach
|
|||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
This exception was added in version 3.13.0, together with [`<nlohmann/json_view.hpp>`](../features/json_view.md).
|
This exception was added in version 3.13.0, together with [`<nlohmann/json_view.hpp>`](../features/json_view/index.md).
|
||||||
[`basic_json::parse()`](../api/basic_json/parse.md) has no such limit.
|
[`basic_json::parse()`](../api/basic_json/parse.md) has no such limit.
|
||||||
|
|
||||||
## Further exceptions
|
## Further exceptions
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ Read newline-delimited input line by line ([JSON Lines](../features/parsing/json
|
|||||||
|
|
||||||
Handle and discard the records of a large array with a [parser callback](../features/parsing/parser_callbacks.md), stop
|
Handle and discard the records of a large array with a [parser callback](../features/parsing/parser_callbacks.md), stop
|
||||||
early with the [SAX interface](../features/parsing/sax_interface.md), or read parts of the text through a
|
early with the [SAX interface](../features/parsing/sax_interface.md), or read parts of the text through a
|
||||||
[`json_document`](../features/json_view.md) (added in version 3.13.0). See
|
[`json_document`](../features/json_view/index.md) (added in version 3.13.0). See
|
||||||
[Input too large for memory](../features/parsing/large_and_partial_input.md#input-too-large-for-memory).
|
[Input too large for memory](../features/parsing/large_and_partial_input.md#input-too-large-for-memory).
|
||||||
|
|
||||||
### Position of a parse error
|
### Position of a parse error
|
||||||
|
|||||||
@@ -17,16 +17,16 @@
|
|||||||
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M76 115 L106 115" marker-end="url(#jvl-line)"/>
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M76 115 L106 115" marker-end="url(#jvl-line)"/>
|
||||||
<rect class="nj-jsonview-heap" x="108" y="98" width="84" height="34" rx="5"/>
|
<rect class="nj-jsonview-heap" x="108" y="98" width="84" height="34" rx="5"/>
|
||||||
<text class="nj-jsonview-mono" x="150" y="120" text-anchor="middle" xml:space="preserve">std::map</text>
|
<text class="nj-jsonview-mono" x="150" y="120" text-anchor="middle" xml:space="preserve">std::map</text>
|
||||||
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 C192 115 226 67 226 67" marker-end="url(#jvl-line)"/>
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 L226 67" marker-end="url(#jvl-line)"/>
|
||||||
<rect class="nj-jsonview-heap" x="228" y="50" width="180" height="34" rx="5"/>
|
<rect class="nj-jsonview-heap" x="228" y="50" width="180" height="34" rx="5"/>
|
||||||
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="72" xml:space="preserve">"port"</text>
|
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="72" xml:space="preserve">"port"</text>
|
||||||
<text class="nj-jsonview-mono nj-jsonview-c-num" x="396" y="72" text-anchor="end" xml:space="preserve">6379</text>
|
<text class="nj-jsonview-mono nj-jsonview-c-num" x="396" y="72" text-anchor="end" xml:space="preserve">6379</text>
|
||||||
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 C192 115 226 115 226 115" marker-end="url(#jvl-line)"/>
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 L226 115" marker-end="url(#jvl-line)"/>
|
||||||
<rect class="nj-jsonview-heap" x="228" y="98" width="180" height="34" rx="5"/>
|
<rect class="nj-jsonview-heap" x="228" y="98" width="180" height="34" rx="5"/>
|
||||||
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="120" xml:space="preserve">"price"</text>
|
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="120" xml:space="preserve">"price"</text>
|
||||||
<text class="nj-jsonview-mono nj-jsonview-c-num" x="396" y="120" text-anchor="end" xml:space="preserve">19.9</text>
|
<text class="nj-jsonview-mono nj-jsonview-c-num" x="396" y="120" text-anchor="end" xml:space="preserve">19.9</text>
|
||||||
<text class="nj-jsonview-note" x="418" y="120" xml:space="preserve">double: 19.90 is gone</text>
|
<text class="nj-jsonview-note" x="418" y="120" xml:space="preserve">double: 19.90 is gone</text>
|
||||||
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 C192 115 226 163 226 163" marker-end="url(#jvl-line)"/>
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 L226 163" marker-end="url(#jvl-line)"/>
|
||||||
<rect class="nj-jsonview-heap" x="228" y="146" width="180" height="34" rx="5"/>
|
<rect class="nj-jsonview-heap" x="228" y="146" width="180" height="34" rx="5"/>
|
||||||
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="168" xml:space="preserve">"tags"</text>
|
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="168" xml:space="preserve">"tags"</text>
|
||||||
<text class="nj-jsonview-mono nj-jsonview-c-line" x="396" y="168" text-anchor="end" xml:space="preserve">•</text>
|
<text class="nj-jsonview-mono nj-jsonview-c-line" x="396" y="168" text-anchor="end" xml:space="preserve">•</text>
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
<path class="nj-jsonview-sep" d="M624 146 V180"/>
|
<path class="nj-jsonview-sep" d="M624 146 V180"/>
|
||||||
<text class="nj-jsonview-mono" x="601" y="168" text-anchor="middle" xml:space="preserve">json</text>
|
<text class="nj-jsonview-mono" x="601" y="168" text-anchor="middle" xml:space="preserve">json</text>
|
||||||
<text class="nj-jsonview-mono" x="647" y="168" text-anchor="middle" xml:space="preserve">json</text>
|
<text class="nj-jsonview-mono" x="647" y="168" text-anchor="middle" xml:space="preserve">json</text>
|
||||||
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M601 146 C601 126 706 150 706 130" marker-end="url(#jvl-line)"/>
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M601 146 C601 130 641 130 706 130" marker-end="url(#jvl-line)"/>
|
||||||
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M647 180 C647 200 706 176 706 196" marker-end="url(#jvl-line)"/>
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M647 180 C647 196 687 196 706 196" marker-end="url(#jvl-line)"/>
|
||||||
<rect class="nj-jsonview-heap" x="708" y="113" width="126" height="34" rx="5"/>
|
<rect class="nj-jsonview-heap" x="708" y="113" width="126" height="34" rx="5"/>
|
||||||
<text class="nj-jsonview-mono nj-jsonview-sm nj-jsonview-c-str" x="771" y="135" text-anchor="middle" xml:space="preserve">std::string eu</text>
|
<text class="nj-jsonview-mono nj-jsonview-sm nj-jsonview-c-str" x="771" y="135" text-anchor="middle" xml:space="preserve">std::string eu</text>
|
||||||
<rect class="nj-jsonview-heap" x="708" y="179" width="126" height="34" rx="5"/>
|
<rect class="nj-jsonview-heap" x="708" y="179" width="126" height="34" rx="5"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,148 @@
|
|||||||
|
<svg class="nj-fig nj-jsonview " viewBox="0 0 840 456" role="img" aria-labelledby="fig-jvli-title fig-jvli-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="fig-jvli-title">The same J​SON text parsed by json_document::parse()</title>
|
||||||
|
<desc id="fig-jvli-title-d">The same text parsed by json_document::parse(): it keeps the 54 bytes of source text and builds a flat index of nine 16-byte nodes in document order: object, key port, unsigned integer 6379 (the converted value is in the node), key price, float, key tags, array, string eu, and an escaped string. Keys, the float, and the plain string point back into the source text; the escaped string points into the document's buffer of decoded strings. A json_view is two pointers: one to the document and one to a node, here the array.</desc>
|
||||||
|
<defs>
|
||||||
|
<marker id="jvli-line" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvli-str" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-str nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvli-key" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-key nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvli-num" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-num nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvli-dec" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-dec nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvli-own" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvli-struct" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvli-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(0 -236)">
|
||||||
|
<text class="nj-jsonview-h" x="16" y="262" xml:space="preserve">json_document::parse(): the source text stays where it is; one flat index of 16-byte nodes</text>
|
||||||
|
<text class="nj-jsonview-note" x="16" y="290" xml:space="preserve">source text, 54 bytes (borrowed, or owned by the document)</text>
|
||||||
|
<rect class="nj-jsonview-ribbon" x="42" y="300" width="756" height="26" rx="4"/>
|
||||||
|
<rect class="nj-jsonview-span nj-jsonview-c-key" x="71" y="302" width="54" height="22" rx="3"/>
|
||||||
|
<rect class="nj-jsonview-span nj-jsonview-c-num" x="169" y="302" width="54" height="22" rx="3"/>
|
||||||
|
<rect class="nj-jsonview-span nj-jsonview-c-key" x="267" y="302" width="68" height="22" rx="3"/>
|
||||||
|
<rect class="nj-jsonview-span nj-jsonview-c-num" x="379" y="302" width="68" height="22" rx="3"/>
|
||||||
|
<rect class="nj-jsonview-span nj-jsonview-c-key" x="491" y="302" width="54" height="22" rx="3"/>
|
||||||
|
<rect class="nj-jsonview-span nj-jsonview-c-str" x="617" y="302" width="26" height="22" rx="3"/>
|
||||||
|
<rect class="nj-jsonview-span nj-jsonview-c-dec" x="701" y="302" width="54" height="22" rx="3"/>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="49" y="317.5" text-anchor="middle" xml:space="preserve">{</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="63" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="77" y="317.5" text-anchor="middle" xml:space="preserve">p</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="91" y="317.5" text-anchor="middle" xml:space="preserve">o</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="105" y="317.5" text-anchor="middle" xml:space="preserve">r</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="119" y="317.5" text-anchor="middle" xml:space="preserve">t</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="133" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="147" y="317.5" text-anchor="middle" xml:space="preserve">:</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="175" y="317.5" text-anchor="middle" xml:space="preserve">6</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="189" y="317.5" text-anchor="middle" xml:space="preserve">3</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="203" y="317.5" text-anchor="middle" xml:space="preserve">7</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="217" y="317.5" text-anchor="middle" xml:space="preserve">9</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="231" y="317.5" text-anchor="middle" xml:space="preserve">,</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="259" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="273" y="317.5" text-anchor="middle" xml:space="preserve">p</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="287" y="317.5" text-anchor="middle" xml:space="preserve">r</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="301" y="317.5" text-anchor="middle" xml:space="preserve">i</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="315" y="317.5" text-anchor="middle" xml:space="preserve">c</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="329" y="317.5" text-anchor="middle" xml:space="preserve">e</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="343" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="357" y="317.5" text-anchor="middle" xml:space="preserve">:</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="385" y="317.5" text-anchor="middle" xml:space="preserve">1</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="399" y="317.5" text-anchor="middle" xml:space="preserve">9</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="413" y="317.5" text-anchor="middle" xml:space="preserve">.</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="427" y="317.5" text-anchor="middle" xml:space="preserve">9</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="441" y="317.5" text-anchor="middle" xml:space="preserve">0</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="455" y="317.5" text-anchor="middle" xml:space="preserve">,</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="483" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="497" y="317.5" text-anchor="middle" xml:space="preserve">t</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="511" y="317.5" text-anchor="middle" xml:space="preserve">a</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="525" y="317.5" text-anchor="middle" xml:space="preserve">g</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="539" y="317.5" text-anchor="middle" xml:space="preserve">s</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="553" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="567" y="317.5" text-anchor="middle" xml:space="preserve">:</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="595" y="317.5" text-anchor="middle" xml:space="preserve">[</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="609" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="623" y="317.5" text-anchor="middle" xml:space="preserve">e</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="637" y="317.5" text-anchor="middle" xml:space="preserve">u</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="651" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="665" y="317.5" text-anchor="middle" xml:space="preserve">,</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="693" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="707" y="317.5" text-anchor="middle" xml:space="preserve">a</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="721" y="317.5" text-anchor="middle" xml:space="preserve">\</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="735" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="749" y="317.5" text-anchor="middle" xml:space="preserve">b</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="763" y="317.5" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="777" y="317.5" text-anchor="middle" xml:space="preserve">]</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-byte" x="791" y="317.5" text-anchor="middle" xml:space="preserve">}</text>
|
||||||
|
<text class="nj-jsonview-tiny" x="49" y="340" text-anchor="middle" xml:space="preserve">0</text>
|
||||||
|
<text class="nj-jsonview-tiny" x="189" y="340" text-anchor="middle" xml:space="preserve">10</text>
|
||||||
|
<text class="nj-jsonview-tiny" x="329" y="340" text-anchor="middle" xml:space="preserve">20</text>
|
||||||
|
<text class="nj-jsonview-tiny" x="469" y="340" text-anchor="middle" xml:space="preserve">30</text>
|
||||||
|
<text class="nj-jsonview-tiny" x="609" y="340" text-anchor="middle" xml:space="preserve">40</text>
|
||||||
|
<text class="nj-jsonview-tiny" x="749" y="340" text-anchor="middle" xml:space="preserve">50</text>
|
||||||
|
<rect class="nj-jsonview-frame" x="16" y="366" width="808" height="254" rx="10"/>
|
||||||
|
<text class="nj-jsonview-note nj-jsonview-strong" x="30" y="602" xml:space="preserve">json_document: owns the node index and the buffer of decoded strings</text>
|
||||||
|
<text class="nj-jsonview-note" x="30" y="500" xml:space="preserve">node index: 9 nodes × 16 bytes = 144 bytes, in document order</text>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="36" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="42" y="420" xml:space="preserve">0</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-struct" x="78" y="434" text-anchor="middle" xml:space="preserve">object</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="78" y="451" text-anchor="middle" xml:space="preserve">len 3</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="78" y="465" text-anchor="middle" xml:space="preserve">next 9</text>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-key" x="124" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="130" y="420" xml:space="preserve">1</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-key" x="166" y="434" text-anchor="middle" xml:space="preserve">key</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="166" y="451" text-anchor="middle" xml:space="preserve">off 2</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="166" y="465" text-anchor="middle" xml:space="preserve">len 4</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-key" d="M166 406 C166 367 98 367 98 328" marker-end="url(#jvli-key)"/>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-num" x="212" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="218" y="420" xml:space="preserve">2</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-num" x="254" y="434" text-anchor="middle" xml:space="preserve">uint</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="254" y="451" text-anchor="middle" xml:space="preserve">6379</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="254" y="465" text-anchor="middle" xml:space="preserve">(converted)</text>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-key" x="300" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="306" y="420" xml:space="preserve">3</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-key" x="342" y="434" text-anchor="middle" xml:space="preserve">key</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="342" y="451" text-anchor="middle" xml:space="preserve">off 16</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="342" y="465" text-anchor="middle" xml:space="preserve">len 5</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-key" d="M342 406 C342 367 301 367 301 328" marker-end="url(#jvli-key)"/>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-num" x="388" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="394" y="420" xml:space="preserve">4</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-num" x="430" y="434" text-anchor="middle" xml:space="preserve">float</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="430" y="451" text-anchor="middle" xml:space="preserve">off 24</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="430" y="465" text-anchor="middle" xml:space="preserve">len 5</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-num" d="M430 406 C430 367 413 367 413 328" marker-end="url(#jvli-num)"/>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-key" x="476" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="482" y="420" xml:space="preserve">5</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-key" x="518" y="434" text-anchor="middle" xml:space="preserve">key</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="518" y="451" text-anchor="middle" xml:space="preserve">off 32</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="518" y="465" text-anchor="middle" xml:space="preserve">len 4</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-key" d="M518 406 C518 367 518 367 518 328" marker-end="url(#jvli-key)"/>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="564" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="570" y="420" xml:space="preserve">6</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-struct" x="606" y="434" text-anchor="middle" xml:space="preserve">array</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="606" y="451" text-anchor="middle" xml:space="preserve">len 2</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="606" y="465" text-anchor="middle" xml:space="preserve">next 3</text>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-str" x="652" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="658" y="420" xml:space="preserve">7</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-str" x="694" y="434" text-anchor="middle" xml:space="preserve">string</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="694" y="451" text-anchor="middle" xml:space="preserve">off 41</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="694" y="465" text-anchor="middle" xml:space="preserve">len 2</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-str" d="M694 406 C694 367 630 367 630 328" marker-end="url(#jvli-str)"/>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-dec" x="740" y="406" width="84" height="68" rx="5"/>
|
||||||
|
<text class="nj-jsonview-tiny" x="746" y="420" xml:space="preserve">8</text>
|
||||||
|
<text class="nj-jsonview-kind nj-jsonview-c-dec" x="782" y="434" text-anchor="middle" xml:space="preserve">string</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="782" y="451" text-anchor="middle" xml:space="preserve">escaped</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-xs" x="782" y="465" text-anchor="middle" xml:space="preserve">len 3</text>
|
||||||
|
<text class="nj-jsonview-note" x="773" y="568" text-anchor="middle" xml:space="preserve">decoded strings</text>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-dec" x="740" y="518" width="66" height="30" rx="4"/>
|
||||||
|
<text class="nj-jsonview-mono" x="751" y="538" text-anchor="middle" xml:space="preserve">a</text>
|
||||||
|
<text class="nj-jsonview-mono" x="773" y="538" text-anchor="middle" xml:space="preserve">"</text>
|
||||||
|
<text class="nj-jsonview-mono" x="795" y="538" text-anchor="middle" xml:space="preserve">b</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-dec" d="M782 474 C782 496 773 496 773 518" marker-end="url(#jvli-dec)"/>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-sm" x="470" y="670" text-anchor="end" xml:space="preserve">json_view v = doc.root()["tags"]</text>
|
||||||
|
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="480" y="648" width="140" height="34" rx="6"/>
|
||||||
|
<path class="nj-jsonview-sep" d="M550 648 V682"/>
|
||||||
|
<text class="nj-jsonview-mono" x="515" y="670" text-anchor="middle" xml:space="preserve">doc*</text>
|
||||||
|
<text class="nj-jsonview-mono" x="585" y="670" text-anchor="middle" xml:space="preserve">node*</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-struct" d="M515 648 L515 622" marker-end="url(#jvli-struct)"/>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-struct" d="M585 648 C585 561 606 561 606 474" marker-end="url(#jvli-struct)"/>
|
||||||
|
<text class="nj-jsonview-note" x="30" y="524" xml:space="preserve">integers are converted while parsing and kept in their node;</text>
|
||||||
|
<text class="nj-jsonview-note" x="30" y="542" xml:space="preserve">floats and strings point back into the text</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,49 @@
|
|||||||
|
<svg class="nj-fig nj-jsonview " viewBox="0 0 840 222" role="img" aria-labelledby="fig-jvlt-title fig-jvlt-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="fig-jvlt-title">A J​SON text parsed by basic_json::parse()</title>
|
||||||
|
<desc id="fig-jvlt-title-d">The text {"port": 6379, "price": 19.90, "tags": ["eu", "a\"b"]} parsed by basic_json::parse(): the root json value points to a std::map whose three nodes are separate heap allocations; the price is stored as the double 19.9; the tags array is a std::vector of two json values, each pointing to its own std::string.</desc>
|
||||||
|
<defs>
|
||||||
|
<marker id="jvlt-line" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvlt-str" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-str nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvlt-key" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-key nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvlt-num" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-num nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvlt-dec" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-dec nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvlt-own" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvlt-struct" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
<marker id="jvlt-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||||
|
</defs>
|
||||||
|
<text class="nj-jsonview-h" x="16" y="26" xml:space="preserve">basic_json::parse(): a tree, every array, object, and string in its own heap allocation</text>
|
||||||
|
<rect class="nj-jsonview-box" x="16" y="98" width="60" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono" x="46" y="120" text-anchor="middle" xml:space="preserve">json</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M76 115 L106 115" marker-end="url(#jvlt-line)"/>
|
||||||
|
<rect class="nj-jsonview-heap" x="108" y="98" width="84" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono" x="150" y="120" text-anchor="middle" xml:space="preserve">std::map</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 L226 67" marker-end="url(#jvlt-line)"/>
|
||||||
|
<rect class="nj-jsonview-heap" x="228" y="50" width="180" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="72" xml:space="preserve">"port"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-c-num" x="396" y="72" text-anchor="end" xml:space="preserve">6379</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 L226 115" marker-end="url(#jvlt-line)"/>
|
||||||
|
<rect class="nj-jsonview-heap" x="228" y="98" width="180" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="120" xml:space="preserve">"price"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-c-num" x="396" y="120" text-anchor="end" xml:space="preserve">19.9</text>
|
||||||
|
<text class="nj-jsonview-note" x="418" y="120" xml:space="preserve">double: 19.90 is gone</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M192 115 L226 163" marker-end="url(#jvlt-line)"/>
|
||||||
|
<rect class="nj-jsonview-heap" x="228" y="146" width="180" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-c-key" x="240" y="168" xml:space="preserve">"tags"</text>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-c-line" x="396" y="168" text-anchor="end" xml:space="preserve">•</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M408 163 L442 163" marker-end="url(#jvlt-line)"/>
|
||||||
|
<rect class="nj-jsonview-heap" x="444" y="146" width="104" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono" x="496" y="168" text-anchor="middle" xml:space="preserve">std::vector</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M548 163 L576 163" marker-end="url(#jvlt-line)"/>
|
||||||
|
<rect class="nj-jsonview-heap" x="578" y="146" width="92" height="34" rx="5"/>
|
||||||
|
<path class="nj-jsonview-sep" d="M624 146 V180"/>
|
||||||
|
<text class="nj-jsonview-mono" x="601" y="168" text-anchor="middle" xml:space="preserve">json</text>
|
||||||
|
<text class="nj-jsonview-mono" x="647" y="168" text-anchor="middle" xml:space="preserve">json</text>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M601 146 C601 130 641 130 706 130" marker-end="url(#jvlt-line)"/>
|
||||||
|
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M647 180 C647 196 687 196 706 196" marker-end="url(#jvlt-line)"/>
|
||||||
|
<rect class="nj-jsonview-heap" x="708" y="113" width="126" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-sm nj-jsonview-c-str" x="771" y="135" text-anchor="middle" xml:space="preserve">std::string eu</text>
|
||||||
|
<rect class="nj-jsonview-heap" x="708" y="179" width="126" height="34" rx="5"/>
|
||||||
|
<text class="nj-jsonview-mono nj-jsonview-sm nj-jsonview-c-str" x="771" y="201" text-anchor="middle" xml:space="preserve">std::string a"b</text>
|
||||||
|
<rect class="nj-jsonview-heap" x="16" y="186" width="18" height="14" rx="3"/>
|
||||||
|
<text class="nj-jsonview-note" x="42" y="198" xml:space="preserve">a separate heap allocation</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,69 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 600 380" role="img" aria-labelledby="nj-blog-cliffs nj-blog-cliffs-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-cliffs">Operations that grew quadratically or worse: time with 3.12.0 and 3.13.0, log scale</title>
|
||||||
|
<desc id="nj-blog-cliffs-d">Dot plot on a logarithmic time axis, less is better; an arrow leads from each 3.12.0 time to the 3.13.0 time. diff() 64k numbers vs []: 4237 ms to 10.8 ms; diff() 20k objects vs half: 122 ms to 4.13 ms; parse() callback, 32k objects: 1398 ms to 12.1 ms; operator< nested 24 deep: 64.4 ms to 0.002 ms; operator< citm, 10x: 284 ms to 3.94 ms; flatten() citm, ordered_json: 271 ms to 1.45 ms; to_bson() 200 deep, 100x: 20.3 ms to 0.352 ms; push_back() 100k, diagnostics: 1589 ms to 5.32 ms; at(i) 10k, diagnostics: 45.9 ms to 0.088 ms.</desc>
|
||||||
|
<line class="nj-charts-grid" x1="250" y1="30" x2="250" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="250" y="352">1 µs</text>
|
||||||
|
<line class="nj-charts-grid" x1="297.857" y1="30" x2="297.857" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="297.857" y="352">10 µs</text>
|
||||||
|
<line class="nj-charts-grid" x1="345.714" y1="30" x2="345.714" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="345.714" y="352">0.1 ms</text>
|
||||||
|
<line class="nj-charts-grid" x1="393.571" y1="30" x2="393.571" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="393.571" y="352">1 ms</text>
|
||||||
|
<line class="nj-charts-grid" x1="441.429" y1="30" x2="441.429" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="441.429" y="352">10 ms</text>
|
||||||
|
<line class="nj-charts-grid" x1="489.286" y1="30" x2="489.286" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="489.286" y="352">0.1 s</text>
|
||||||
|
<line class="nj-charts-grid" x1="537.143" y1="30" x2="537.143" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="537.143" y="352">1 s</text>
|
||||||
|
<line class="nj-charts-grid" x1="585" y1="30" x2="585" y2="336"/>
|
||||||
|
<text class="nj-charts-tick" x="585" y="352">10 s</text>
|
||||||
|
<text class="nj-charts-tick" x="417.5" y="370">runtime (log scale), less is better</text>
|
||||||
|
<defs><marker id="nj-blog-cliffs-head" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto"><path class="nj-charts-head" d="M0 0 L10 5 L0 10 z"/></marker></defs>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="250" cy="14" r="5"/><text class="nj-charts-leg" x="260" y="18.5">3.12.0</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="330" cy="14" r="5"/><text class="nj-charts-leg" x="340" y="18.5">3.13.0</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="52" text-anchor="end">diff() 64k numbers vs []</text>
|
||||||
|
<line class="nj-charts-axis" x1="561.152" y1="48" x2="450.028" y2="48" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="567.152" cy="48" r="5"><title>diff() 64k numbers vs [], 3.12.0: 4237 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="443.028" cy="48" r="5"><title>diff() 64k numbers vs [], 3.13.0: 10.8 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="505.09" y="41">392×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="86" text-anchor="end">diff() 20k objects vs half</text>
|
||||||
|
<line class="nj-charts-axis" x1="487.419" y1="82" x2="430.049" y2="82" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="493.419" cy="82" r="5"><title>diff() 20k objects vs half, 3.12.0: 122 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="423.049" cy="82" r="5"><title>diff() 20k objects vs half, 3.13.0: 4.13 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="458.234" y="75">30×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="120" text-anchor="end">parse() callback, 32k objects</text>
|
||||||
|
<line class="nj-charts-axis" x1="538.106" y1="116" x2="452.39" y2="116" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="544.106" cy="116" r="5"><title>parse() callback, 32k objects, 3.12.0: 1398 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="445.39" cy="116" r="5"><title>parse() callback, 32k objects, 3.13.0: 12.1 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="494.748" y="109">116×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="154" text-anchor="end">operator< nested 24 deep</text>
|
||||||
|
<line class="nj-charts-axis" x1="474.14" y1="150" x2="271.406" y2="150" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="480.14" cy="150" r="5"><title>operator< nested 24 deep, 3.12.0: 64.4 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="264.406" cy="150" r="5"><title>operator< nested 24 deep, 3.13.0: 0.002 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="372.273" y="143">32200×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="188" text-anchor="end">operator< citm, 10x</text>
|
||||||
|
<line class="nj-charts-axis" x1="504.98" y1="184" x2="429.07" y2="184" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="510.98" cy="184" r="5"><title>operator< citm, 10x, 3.12.0: 284 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="422.07" cy="184" r="5"><title>operator< citm, 10x, 3.13.0: 3.94 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="466.525" y="177">72×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="222" text-anchor="end">flatten() citm, ordered_json</text>
|
||||||
|
<line class="nj-charts-axis" x1="504.006" y1="218" x2="408.294" y2="218" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="510.006" cy="218" r="5"><title>flatten() citm, ordered_json, 3.12.0: 271 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="401.294" cy="218" r="5"><title>flatten() citm, ordered_json, 3.13.0: 1.45 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="455.65" y="211">187×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="256" text-anchor="end">to_bson() 200 deep, 100x</text>
|
||||||
|
<line class="nj-charts-axis" x1="450.144" y1="252" x2="378.87" y2="252" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="456.144" cy="252" r="5"><title>to_bson() 200 deep, 100x, 3.12.0: 20.3 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="371.87" cy="252" r="5"><title>to_bson() 200 deep, 100x, 3.13.0: 0.352 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="414.007" y="245">58×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="290" text-anchor="end">push_back() 100k, diagnostics</text>
|
||||||
|
<line class="nj-charts-axis" x1="540.768" y1="286" x2="435.311" y2="286" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="546.768" cy="286" r="5"><title>push_back() 100k, diagnostics, 3.12.0: 1589 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="428.311" cy="286" r="5"><title>push_back() 100k, diagnostics, 3.13.0: 5.32 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="487.54" y="279">299×</text>
|
||||||
|
<text class="nj-charts-rowlabel" x="240" y="324" text-anchor="end">at(i) 10k, diagnostics</text>
|
||||||
|
<line class="nj-charts-axis" x1="467.101" y1="320" x2="350.057" y2="320" marker-end="url(#nj-blog-cliffs-head)"/>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="473.101" cy="320" r="5"><title>at(i) 10k, diagnostics, 3.12.0: 45.9 ms</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="343.057" cy="320" r="5"><title>at(i) 10k, diagnostics, 3.13.0: 0.088 ms</title></circle>
|
||||||
|
<text class="nj-charts-factor" x="408.079" y="313">522×</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.2 KiB |
@@ -0,0 +1,66 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--swar" viewBox="0 0 600 430" role="img" aria-labelledby="nj-blog-digits nj-blog-digits-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-digits">Converting eight A​SCII digits to their value with three multiplications</title>
|
||||||
|
<desc id="nj-blog-digits-d">The text 12345678 as one word. Masking gives the digit values 1 to 8, one per byte. Multiplying by 10 times 2 to the 8 plus 1 and shifting gives 12, 34, 56 and 78 in 16-bit lanes; multiplying by 100 times 2 to the 16 plus 1 gives 1234 and 5678 in 32-bit lanes; multiplying by 10000 times 2 to the 32 plus 1 gives 12345678.</desc>
|
||||||
|
<text class="fg-h" x="40" y="20">eight A​SCII digits in one 64-bit word (byte 0 is the lowest)</text>
|
||||||
|
<rect class="fg-cell" x="40" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="69" y="53" text-anchor="middle">1</text>
|
||||||
|
<text class="nj-charts-tick" x="69" y="80">0x31</text>
|
||||||
|
<rect class="fg-cell" x="104" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="133" y="53" text-anchor="middle">2</text>
|
||||||
|
<text class="nj-charts-tick" x="133" y="80">0x32</text>
|
||||||
|
<rect class="fg-cell" x="168" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="197" y="53" text-anchor="middle">3</text>
|
||||||
|
<text class="nj-charts-tick" x="197" y="80">0x33</text>
|
||||||
|
<rect class="fg-cell" x="232" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="261" y="53" text-anchor="middle">4</text>
|
||||||
|
<text class="nj-charts-tick" x="261" y="80">0x34</text>
|
||||||
|
<rect class="fg-cell" x="296" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="325" y="53" text-anchor="middle">5</text>
|
||||||
|
<text class="nj-charts-tick" x="325" y="80">0x35</text>
|
||||||
|
<rect class="fg-cell" x="360" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="389" y="53" text-anchor="middle">6</text>
|
||||||
|
<text class="nj-charts-tick" x="389" y="80">0x36</text>
|
||||||
|
<rect class="fg-cell" x="424" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="453" y="53" text-anchor="middle">7</text>
|
||||||
|
<text class="nj-charts-tick" x="453" y="80">0x37</text>
|
||||||
|
<rect class="fg-cell" x="488" y="30" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="517" y="53" text-anchor="middle">8</text>
|
||||||
|
<text class="nj-charts-tick" x="517" y="80">0x38</text>
|
||||||
|
<text class="fg-h" x="40" y="112">& 0x0F0F0F0F0F0F0F0F: one digit value per byte</text>
|
||||||
|
<rect class="fg-cell" x="40" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="69" y="145" text-anchor="middle">1</text>
|
||||||
|
<rect class="fg-cell" x="104" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="133" y="145" text-anchor="middle">2</text>
|
||||||
|
<rect class="fg-cell" x="168" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="197" y="145" text-anchor="middle">3</text>
|
||||||
|
<rect class="fg-cell" x="232" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="261" y="145" text-anchor="middle">4</text>
|
||||||
|
<rect class="fg-cell" x="296" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="325" y="145" text-anchor="middle">5</text>
|
||||||
|
<rect class="fg-cell" x="360" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="389" y="145" text-anchor="middle">6</text>
|
||||||
|
<rect class="fg-cell" x="424" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="453" y="145" text-anchor="middle">7</text>
|
||||||
|
<rect class="fg-cell" x="488" y="122" width="58" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="517" y="145" text-anchor="middle">8</text>
|
||||||
|
<text class="fg-h" x="40" y="186">× (10 · 2⁸ + 1) >> 8, every second byte kept: two digits per 16 bits</text>
|
||||||
|
<rect class="fg-cell" x="40" y="196" width="122" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="101" y="219" text-anchor="middle">12</text>
|
||||||
|
<rect class="fg-cell" x="168" y="196" width="122" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="229" y="219" text-anchor="middle">34</text>
|
||||||
|
<rect class="fg-cell" x="296" y="196" width="122" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="357" y="219" text-anchor="middle">56</text>
|
||||||
|
<rect class="fg-cell" x="424" y="196" width="122" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="485" y="219" text-anchor="middle">78</text>
|
||||||
|
<text class="fg-h" x="40" y="260">× (100 · 2¹⁶ + 1) >> 16, every second lane kept: four digits per 32 bits</text>
|
||||||
|
<rect class="fg-cell" x="40" y="270" width="250" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="165" y="293" text-anchor="middle">1234</text>
|
||||||
|
<rect class="fg-cell" x="296" y="270" width="250" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="421" y="293" text-anchor="middle">5678</text>
|
||||||
|
<text class="fg-h" x="40" y="334">× (10000 · 2³² + 1) >> 32: the value</text>
|
||||||
|
<rect class="fg-cell" x="40" y="344" width="506" height="34" rx="4"/>
|
||||||
|
<rect class="nj-charts-c1" fill-opacity=".25" x="40" y="344" width="506" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="293" y="367" text-anchor="middle">12345678</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="400">Each step combines neighboring lanes, in all lanes of the word at once:</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="418">three multiplications instead of eight, and no branch.</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,36 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 560 250" role="img" aria-labelledby="nj-blog-dist nj-blog-dist-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-dist">Speedup of 3.13.0 over 3.12.0 for all 182 benchmarks of the suite</title>
|
||||||
|
<desc id="nj-blog-dist-d">Bars counting the benchmarks per speedup range: below 1x: 1; 1 to 1.5x: 22; 1.5 to 2x: 12; 2 to 3x: 60; 3 to 5x: 58; 5 to 10x: 25; 10x or more: 4. Median 2.9x, geometric mean 3.0x.</desc>
|
||||||
|
<line class="nj-charts-grid" x1="52" y1="196" x2="548" y2="196"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="46" y="200">0</text>
|
||||||
|
<line class="nj-charts-grid" x1="52" y1="144.571" x2="548" y2="144.571"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="46" y="148.571">20</text>
|
||||||
|
<line class="nj-charts-grid" x1="52" y1="93.1429" x2="548" y2="93.1429"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="46" y="97.1429">40</text>
|
||||||
|
<line class="nj-charts-grid" x1="52" y1="41.7143" x2="548" y2="41.7143"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="46" y="45.7143">60</text>
|
||||||
|
<rect class="nj-charts-c2" x="60.0" y="193.4" width="54.9" height="2.6" rx="3"><title>below 1×: 1</title></rect>
|
||||||
|
<text class="nj-charts-val" x="87.4" y="188.4" text-anchor="middle">1</text>
|
||||||
|
<text class="nj-charts-tick" x="87.4" y="212.0">below 1×</text>
|
||||||
|
<rect class="nj-charts-c1" x="130.9" y="139.4" width="54.9" height="56.6" rx="3"><title>1 to 1.5×: 22</title></rect>
|
||||||
|
<text class="nj-charts-val" x="158.3" y="134.4" text-anchor="middle">22</text>
|
||||||
|
<text class="nj-charts-tick" x="158.3" y="212.0">1 to 1.5×</text>
|
||||||
|
<rect class="nj-charts-c1" x="201.7" y="165.1" width="54.9" height="30.9" rx="3"><title>1.5 to 2×: 12</title></rect>
|
||||||
|
<text class="nj-charts-val" x="229.1" y="160.1" text-anchor="middle">12</text>
|
||||||
|
<text class="nj-charts-tick" x="229.1" y="212.0">1.5 to 2×</text>
|
||||||
|
<rect class="nj-charts-c1" x="272.6" y="41.7" width="54.9" height="154.3" rx="3"><title>2 to 3×: 60</title></rect>
|
||||||
|
<text class="nj-charts-val" x="300.0" y="36.7" text-anchor="middle">60</text>
|
||||||
|
<text class="nj-charts-tick" x="300.0" y="212.0">2 to 3×</text>
|
||||||
|
<rect class="nj-charts-c1" x="343.4" y="46.9" width="54.9" height="149.1" rx="3"><title>3 to 5×: 58</title></rect>
|
||||||
|
<text class="nj-charts-val" x="370.9" y="41.9" text-anchor="middle">58</text>
|
||||||
|
<text class="nj-charts-tick" x="370.9" y="212.0">3 to 5×</text>
|
||||||
|
<rect class="nj-charts-c1" x="414.3" y="131.7" width="54.9" height="64.3" rx="3"><title>5 to 10×: 25</title></rect>
|
||||||
|
<text class="nj-charts-val" x="441.7" y="126.7" text-anchor="middle">25</text>
|
||||||
|
<text class="nj-charts-tick" x="441.7" y="212.0">5 to 10×</text>
|
||||||
|
<rect class="nj-charts-c1" x="485.1" y="185.7" width="54.9" height="10.3" rx="3"><title>10× or more: 4</title></rect>
|
||||||
|
<text class="nj-charts-val" x="512.6" y="180.7" text-anchor="middle">4</text>
|
||||||
|
<text class="nj-charts-tick" x="512.6" y="212.0">10× or more</text>
|
||||||
|
<line class="nj-charts-axis" x1="52" y1="196" x2="548" y2="196"/>
|
||||||
|
<text class="nj-charts-tick" x="300" y="240">speedup over 3.12.0, more is better</text>
|
||||||
|
<text class="nj-charts-tick" transform="rotate(-90)" x="-106" y="14">number of benchmarks</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,102 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 620 482" role="img" aria-labelledby="nj-blog-heldout nj-blog-heldout-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-heldout">Validating 19 files 3.13.0 was not tuned on: json_document's time relative to the fastest other library per file</title>
|
||||||
|
<desc id="nj-blog-heldout-d">Dot plot per file, less is better; below 1.0 json_document is the fastest fully validating library. mesh: Mac 0.68, x86-64 0.44 (serde_json (Rust)); marine_ik: Mac 0.59, x86-64 0.64 (Boost.J​SON); semanticscholar-corpus: Mac 0.79, x86-64 0.65 (sonic-rs (Rust)); gsoc-2018: Mac 0.60, x86-64 0.66 (Boost.J​SON); twitter_api_compact_response: Mac 0.91, x86-64 0.70 (simdjson); twitterescaped: Mac 0.93, x86-64 0.76 (simdjson); numbers: Mac 0.67, x86-64 0.77 (Boost.J​SON); github_events: Mac 0.79, x86-64 0.77 (simdjson); update-center: Mac 0.88, x86-64 0.79 (simdjson); twitter_timeline: Mac 0.97, x86-64 0.80 (yyjson); twitter_api_response: Mac 0.93, x86-64 0.81 (simdjson); google_maps_api_compact_response: Mac 0.86, x86-64 0.81 (yyjson); repeat: Mac 0.65, x86-64 0.82 (simdjson); random: Mac 0.81, x86-64 0.88 (simdjson); apache_builds: Mac 1.08, x86-64 0.90 (simdjson); instruments: Mac 0.90, x86-64 0.92 (simdjson); mesh.pretty: Mac 0.86, x86-64 0.92 (Boost.J​SON); tree-pretty: Mac 0.93, x86-64 0.96 (simdjson); google_maps_api_response: Mac 0.93, x86-64 0.99 (simdjson).</desc>
|
||||||
|
<line class="nj-charts-grid" x1="262" y1="50" x2="262" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="262" y="454">0.4×</text>
|
||||||
|
<line class="nj-charts-grid" x1="291.867" y1="50" x2="291.867" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="291.867" y="454">0.5×</text>
|
||||||
|
<line class="nj-charts-grid" x1="321.733" y1="50" x2="321.733" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="321.733" y="454">0.6×</text>
|
||||||
|
<line class="nj-charts-grid" x1="351.6" y1="50" x2="351.6" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="351.6" y="454">0.7×</text>
|
||||||
|
<line class="nj-charts-grid" x1="381.467" y1="50" x2="381.467" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="381.467" y="454">0.8×</text>
|
||||||
|
<line class="nj-charts-grid" x1="411.333" y1="50" x2="411.333" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="411.333" y="454">0.9×</text>
|
||||||
|
<line class="nj-charts-grid" x1="441.2" y1="50" x2="441.2" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="441.2" y="454">1.0×</text>
|
||||||
|
<line class="nj-charts-grid" x1="471.067" y1="50" x2="471.067" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="471.067" y="454">1.1×</text>
|
||||||
|
<text class="nj-charts-tick" x="374" y="472">json_document’s time / the fastest other library’s, less is better</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="13" cy="14" r="5"/><text class="nj-charts-leg" x="23" y="18.5">Apple M1 Max</text>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="134.2" cy="14" r="5"/><text class="nj-charts-leg" x="144.2" y="18.5">x86-64, G​CC 16.2</text>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="48">next on x86-64</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="68" text-anchor="end">mesh</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="344.8" cy="60.5" r="4.5"><title>mesh, Apple M1 Max: 0.68× the time of Glaze</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="273.6" cy="67.5" r="4.5"><title>mesh, x86-64, G​CC 16.2: 0.44× the time of serde_json (Rust)</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="68">serde_json (Rust)</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="88" text-anchor="end">marine_ik</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="319.5" cy="80.5" r="4.5"><title>marine_ik, Apple M1 Max: 0.59× the time of Glaze</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="334.2" cy="87.5" r="4.5"><title>marine_ik, x86-64, G​CC 16.2: 0.64× the time of Boost.J​SON</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="88">Boost.J​SON</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="108" text-anchor="end">semanticscholar-corpus</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="377.9" cy="100.5" r="4.5"><title>semanticscholar-corpus, Apple M1 Max: 0.79× the time of sonic-rs (Rust)</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="336.0" cy="107.5" r="4.5"><title>semanticscholar-corpus, x86-64, G​CC 16.2: 0.65× the time of sonic-rs (Rust)</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="108">sonic-rs (Rust)</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="128" text-anchor="end">gsoc-2018</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="322.9" cy="120.5" r="4.5"><title>gsoc-2018, Apple M1 Max: 0.60× the time of sonic-rs (Rust)</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="338.8" cy="127.5" r="4.5"><title>gsoc-2018, x86-64, G​CC 16.2: 0.66× the time of Boost.J​SON</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="128">Boost.J​SON</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="148" text-anchor="end">twitter_api_compact_response</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="414.0" cy="140.5" r="4.5"><title>twitter_api_compact_response, Apple M1 Max: 0.91× the time of yyjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="352.1" cy="147.5" r="4.5"><title>twitter_api_compact_response, x86-64, G​CC 16.2: 0.70× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="148">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="168" text-anchor="end">twitterescaped</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="419.5" cy="160.5" r="4.5"><title>twitterescaped, Apple M1 Max: 0.93× the time of yyjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="370.8" cy="167.5" r="4.5"><title>twitterescaped, x86-64, G​CC 16.2: 0.76× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="168">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="188" text-anchor="end">numbers</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="343.3" cy="180.5" r="4.5"><title>numbers, Apple M1 Max: 0.67× the time of Boost.J​SON</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="373.1" cy="187.5" r="4.5"><title>numbers, x86-64, G​CC 16.2: 0.77× the time of Boost.J​SON</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="188">Boost.J​SON</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="208" text-anchor="end">github_events</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="379.5" cy="200.5" r="4.5"><title>github_events, Apple M1 Max: 0.79× the time of simdjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="373.4" cy="207.5" r="4.5"><title>github_events, x86-64, G​CC 16.2: 0.77× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="208">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="228" text-anchor="end">update-center</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="405.1" cy="220.5" r="4.5"><title>update-center, Apple M1 Max: 0.88× the time of sonic-rs (Rust)</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="377.7" cy="227.5" r="4.5"><title>update-center, x86-64, G​CC 16.2: 0.79× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="228">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="248" text-anchor="end">twitter_timeline</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="431.6" cy="240.5" r="4.5"><title>twitter_timeline, Apple M1 Max: 0.97× the time of yyjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="381.3" cy="247.5" r="4.5"><title>twitter_timeline, x86-64, G​CC 16.2: 0.80× the time of yyjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="248">yyjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="268" text-anchor="end">twitter_api_response</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="420.4" cy="260.5" r="4.5"><title>twitter_api_response, Apple M1 Max: 0.93× the time of simdjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="384.1" cy="267.5" r="4.5"><title>twitter_api_response, x86-64, G​CC 16.2: 0.81× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="268">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="288" text-anchor="end">google_maps_api_compact_response</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="399.2" cy="280.5" r="4.5"><title>google_maps_api_compact_response, Apple M1 Max: 0.86× the time of yyjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="384.2" cy="287.5" r="4.5"><title>google_maps_api_compact_response, x86-64, G​CC 16.2: 0.81× the time of yyjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="288">yyjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="308" text-anchor="end">repeat</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="336.7" cy="300.5" r="4.5"><title>repeat, Apple M1 Max: 0.65× the time of simdjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="388.2" cy="307.5" r="4.5"><title>repeat, x86-64, G​CC 16.2: 0.82× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="308">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="328" text-anchor="end">random</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="383.7" cy="320.5" r="4.5"><title>random, Apple M1 Max: 0.81× the time of simdjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="404.9" cy="327.5" r="4.5"><title>random, x86-64, G​CC 16.2: 0.88× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="328">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="348" text-anchor="end">apache_builds</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="463.6" cy="340.5" r="4.5"><title>apache_builds, Apple M1 Max: 1.08× the time of simdjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="412.4" cy="347.5" r="4.5"><title>apache_builds, x86-64, G​CC 16.2: 0.90× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="348">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="368" text-anchor="end">instruments</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="412.5" cy="360.5" r="4.5"><title>instruments, Apple M1 Max: 0.90× the time of yyjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="416.2" cy="367.5" r="4.5"><title>instruments, x86-64, G​CC 16.2: 0.92× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="368">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="388" text-anchor="end">mesh.pretty</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="398.9" cy="380.5" r="4.5"><title>mesh.pretty, Apple M1 Max: 0.86× the time of Glaze</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="418.6" cy="387.5" r="4.5"><title>mesh.pretty, x86-64, G​CC 16.2: 0.92× the time of Boost.J​SON</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="388">Boost.J​SON</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="408" text-anchor="end">tree-pretty</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="420.1" cy="400.5" r="4.5"><title>tree-pretty, Apple M1 Max: 0.93× the time of simdjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="429.3" cy="407.5" r="4.5"><title>tree-pretty, x86-64, G​CC 16.2: 0.96× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="408">simdjson</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="252" y="428" text-anchor="end">google_maps_api_response</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="419.0" cy="420.5" r="4.5"><title>google_maps_api_response, Apple M1 Max: 0.93× the time of simdjson</title></circle>
|
||||||
|
<circle class="nj-charts-ojson-dot" cx="439.0" cy="427.5" r="4.5"><title>google_maps_api_response, x86-64, G​CC 16.2: 0.99× the time of simdjson</title></circle>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--l" x="500" y="428">simdjson</text>
|
||||||
|
<line class="nj-charts-ref" x1="441.2" y1="50" x2="441.2" y2="438"/>
|
||||||
|
<text class="nj-charts-tick" x="441.2" y="44">as fast as the next</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,72 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--swar" viewBox="0 0 640 294" role="img" aria-labelledby="nj-blog-overlap nj-blog-overlap-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-overlap">Writing an 18-byte string with two overlapping 16-byte loads and stores</title>
|
||||||
|
<desc id="nj-blog-overlap-d">The 18 bytes of profile_link_color. Load a covers bytes 0 to 15, load b covers bytes 2 to 17, so bytes 2 to 15 are in both. Neither contains a byte that needs escaping, so a is stored at the destination and b 2 bytes later; the second store writes bytes 2 to 15 again with the same values. This works for any length from 16 to 32 bytes without a loop.</desc>
|
||||||
|
<text class="fg-h" x="40" y="20">a string of n = 18 bytes: too long for one 16-byte vector, too short for a loop</text>
|
||||||
|
<rect class="fg-cell" x="40" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="53.5" y="58" text-anchor="middle">p</text>
|
||||||
|
<rect class="fg-cell" x="71" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="84.5" y="58" text-anchor="middle">r</text>
|
||||||
|
<rect class="fg-cell" x="102" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="102" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="115.5" y="58" text-anchor="middle">o</text>
|
||||||
|
<rect class="fg-cell" x="133" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="133" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="146.5" y="58" text-anchor="middle">f</text>
|
||||||
|
<rect class="fg-cell" x="164" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="164" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="177.5" y="58" text-anchor="middle">i</text>
|
||||||
|
<rect class="fg-cell" x="195" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="195" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="208.5" y="58" text-anchor="middle">l</text>
|
||||||
|
<rect class="fg-cell" x="226" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="226" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="239.5" y="58" text-anchor="middle">e</text>
|
||||||
|
<rect class="fg-cell" x="257" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="257" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="270.5" y="58" text-anchor="middle">_</text>
|
||||||
|
<rect class="fg-cell" x="288" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="288" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="301.5" y="58" text-anchor="middle">l</text>
|
||||||
|
<rect class="fg-cell" x="319" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="319" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="332.5" y="58" text-anchor="middle">i</text>
|
||||||
|
<rect class="fg-cell" x="350" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="350" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="363.5" y="58" text-anchor="middle">n</text>
|
||||||
|
<rect class="fg-cell" x="381" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="381" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="394.5" y="58" text-anchor="middle">k</text>
|
||||||
|
<rect class="fg-cell" x="412" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="412" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="425.5" y="58" text-anchor="middle">_</text>
|
||||||
|
<rect class="fg-cell" x="443" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="443" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="456.5" y="58" text-anchor="middle">c</text>
|
||||||
|
<rect class="fg-cell" x="474" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="474" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="487.5" y="58" text-anchor="middle">o</text>
|
||||||
|
<rect class="fg-cell" x="505" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<rect class="nj-charts-c3" fill-opacity=".3" x="505" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="518.5" y="58" text-anchor="middle">l</text>
|
||||||
|
<rect class="fg-cell" x="536" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="549.5" y="58" text-anchor="middle">o</text>
|
||||||
|
<rect class="fg-cell" x="567" y="34" width="27" height="36" rx="4"/>
|
||||||
|
<text class="fg-code" x="580.5" y="58" text-anchor="middle">r</text>
|
||||||
|
<text class="nj-charts-tick" x="53.5" y="86">0</text>
|
||||||
|
<text class="nj-charts-tick" x="115.5" y="86">2</text>
|
||||||
|
<text class="nj-charts-tick" x="518.5" y="86">15</text>
|
||||||
|
<text class="nj-charts-tick" x="580.5" y="86">17</text>
|
||||||
|
<rect class="nj-charts-c1" fill-opacity=".22" x="40" y="100" width="492" height="22" rx="4"/>
|
||||||
|
<rect class="nj-charts-c1" x="40" y="100" width="4" height="22" rx="2"/>
|
||||||
|
<rect class="nj-charts-c1" x="528" y="100" width="4" height="22" rx="2"/>
|
||||||
|
<text class="fg-code" x="286" y="116" text-anchor="middle" style="font-size:14px">a = load 16 bytes at p</text>
|
||||||
|
<rect class="nj-charts-c2" fill-opacity=".22" x="102" y="130" width="492" height="22" rx="4"/>
|
||||||
|
<rect class="nj-charts-c2" x="102" y="130" width="4" height="22" rx="2"/>
|
||||||
|
<rect class="nj-charts-c2" x="590" y="130" width="4" height="22" rx="2"/>
|
||||||
|
<text class="fg-code" x="348" y="146" text-anchor="middle" style="font-size:14px">b = load 16 bytes at p + n − 16</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="182">Both loads lie inside the string, and together they cover all of it; bytes 2 to 15 are in both.</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="204">Test a and b for a quote, a backslash, a control character or 0x7F: none, so write</text>
|
||||||
|
<text class="fg-code" x="40" y="234">store a at d, store b at d + n − 16</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="262">The second store writes bytes 2 to 15 again, with the same values. For any n from 16 to 32:</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="280">two loads, one test, two stores, no loop and no branch on the length.</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,87 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 560 604" role="img" aria-labelledby="nj-blog-overview nj-blog-overview-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-overview">Speedup of 3.13.0 over 3.12.0 per workload, Apple M1 Max, Apple clang</title>
|
||||||
|
<desc id="nj-blog-overview-d">Horizontal bars of the speedup, more is better. twitter.json 3.1x; citm_catalog.json 4.2x; canada.json 3.4x; canada.json, indented 6.5x; citm, ordered_json 6.8x; twitter, std::ifstream 2.5x; citm, FILE* 4.7x; citm, accept() 7.1x; dump() twitter 6.7x; dump() canada 3.0x; dump(4) citm 4.7x; to_cbor() twitter 4.6x; twitter, memory 3.4x; twitter, FILE* 6.6x; diff() canada 10x; iterate canada 2.7x; copy + destroy citm 1.13x; == citm 1.06x.</desc>
|
||||||
|
<text class="nj-charts-panel" x="8" y="20">Parsing</text>
|
||||||
|
<text class="nj-charts-panel" x="8" y="244">Writing</text>
|
||||||
|
<text class="nj-charts-panel" x="8" y="372">Reading C​BOR</text>
|
||||||
|
<text class="nj-charts-panel" x="8" y="452">Values</text>
|
||||||
|
<line class="nj-charts-grid" x1="220" y1="8" x2="220" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="220" y="576">1×</text>
|
||||||
|
<line class="nj-charts-grid" x1="250" y1="8" x2="250" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="250" y="576">2×</text>
|
||||||
|
<line class="nj-charts-grid" x1="280" y1="8" x2="280" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="280" y="576">3×</text>
|
||||||
|
<line class="nj-charts-grid" x1="310" y1="8" x2="310" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="310" y="576">4×</text>
|
||||||
|
<line class="nj-charts-grid" x1="340" y1="8" x2="340" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="340" y="576">5×</text>
|
||||||
|
<line class="nj-charts-grid" x1="370" y1="8" x2="370" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="370" y="576">6×</text>
|
||||||
|
<line class="nj-charts-grid" x1="400" y1="8" x2="400" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="400" y="576">7×</text>
|
||||||
|
<line class="nj-charts-grid" x1="430" y1="8" x2="430" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="430" y="576">8×</text>
|
||||||
|
<line class="nj-charts-grid" x1="460" y1="8" x2="460" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="460" y="576">9×</text>
|
||||||
|
<line class="nj-charts-grid" x1="490" y1="8" x2="490" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="490" y="576">10×</text>
|
||||||
|
<line class="nj-charts-grid" x1="520" y1="8" x2="520" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="520" y="576">11×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="42" text-anchor="end">twitter.json</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 30 H280.2 Q284.2 30 284.2 34 V41 Q284.2 45 280.2 45 H190 Z"><title>twitter.json: 3.1×</title></path>
|
||||||
|
<text class="nj-charts-val" x="289.2" y="42">3.1×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="66" text-anchor="end">citm_catalog.json</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 54 H313.5 Q317.5 54 317.5 58 V65 Q317.5 69 313.5 69 H190 Z"><title>citm_catalog.json: 4.2×</title></path>
|
||||||
|
<text class="nj-charts-val" x="322.5" y="66">4.2×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="90" text-anchor="end">canada.json</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 78 H286.5 Q290.5 78 290.5 82 V89 Q290.5 93 286.5 93 H190 Z"><title>canada.json: 3.4×</title></path>
|
||||||
|
<text class="nj-charts-val" x="295.5" y="90">3.4×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="114" text-anchor="end">canada.json, indented</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 102 H381.3 Q385.3 102 385.3 106 V113 Q385.3 117 381.3 117 H190 Z"><title>canada.json, indented: 6.5×</title></path>
|
||||||
|
<text class="nj-charts-val" x="390.3" y="114">6.5×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="138" text-anchor="end">citm, ordered_json</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 126 H391.5 Q395.5 126 395.5 130 V137 Q395.5 141 391.5 141 H190 Z"><title>citm, ordered_json: 6.8×</title></path>
|
||||||
|
<text class="nj-charts-val" x="400.5" y="138">6.8×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="162" text-anchor="end">twitter, std::ifstream</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 150 H261 Q265 150 265 154 V161 Q265 165 261 165 H190 Z"><title>twitter, std::ifstream: 2.5×</title></path>
|
||||||
|
<text class="nj-charts-val" x="270" y="162">2.5×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="186" text-anchor="end">citm, FILE*</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 174 H328.2 Q332.2 174 332.2 178 V185 Q332.2 189 328.2 189 H190 Z"><title>citm, FILE*: 4.7×</title></path>
|
||||||
|
<text class="nj-charts-val" x="337.2" y="186">4.7×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="210" text-anchor="end">citm, accept()</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 198 H398.1 Q402.1 198 402.1 202 V209 Q402.1 213 398.1 213 H190 Z"><title>citm, accept(): 7.1×</title></path>
|
||||||
|
<text class="nj-charts-val" x="407.1" y="210">7.1×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="266" text-anchor="end">dump() twitter</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 254 H387 Q391 254 391 258 V265 Q391 269 387 269 H190 Z"><title>dump() twitter: 6.7×</title></path>
|
||||||
|
<text class="nj-charts-val" x="396" y="266">6.7×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="290" text-anchor="end">dump() canada</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 278 H275.4 Q279.4 278 279.4 282 V289 Q279.4 293 275.4 293 H190 Z"><title>dump() canada: 3.0×</title></path>
|
||||||
|
<text class="nj-charts-val" x="284.4" y="290">3.0×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="314" text-anchor="end">dump(4) citm</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 302 H327 Q331 302 331 306 V313 Q331 317 327 317 H190 Z"><title>dump(4) citm: 4.7×</title></path>
|
||||||
|
<text class="nj-charts-val" x="336" y="314">4.7×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="338" text-anchor="end">to_cbor() twitter</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 326 H323.7 Q327.7 326 327.7 330 V337 Q327.7 341 323.7 341 H190 Z"><title>to_cbor() twitter: 4.6×</title></path>
|
||||||
|
<text class="nj-charts-val" x="332.7" y="338">4.6×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="394" text-anchor="end">twitter, memory</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 382 H287.7 Q291.7 382 291.7 386 V393 Q291.7 397 287.7 397 H190 Z"><title>twitter, memory: 3.4×</title></path>
|
||||||
|
<text class="nj-charts-val" x="296.7" y="394">3.4×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="418" text-anchor="end">twitter, FILE*</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 406 H383.7 Q387.7 406 387.7 410 V417 Q387.7 421 383.7 421 H190 Z"><title>twitter, FILE*: 6.6×</title></path>
|
||||||
|
<text class="nj-charts-val" x="392.7" y="418">6.6×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="474" text-anchor="end">diff() canada</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 462 H499.8 Q503.8 462 503.8 466 V473 Q503.8 477 499.8 477 H190 Z"><title>diff() canada: 10×</title></path>
|
||||||
|
<text class="nj-charts-val" x="508.8" y="474">10×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="498" text-anchor="end">iterate canada</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 486 H267.9 Q271.9 486 271.9 490 V497 Q271.9 501 267.9 501 H190 Z"><title>iterate canada: 2.7×</title></path>
|
||||||
|
<text class="nj-charts-val" x="276.9" y="498">2.7×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="522" text-anchor="end">copy + destroy citm</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 510 H219.9 Q223.9 510 223.9 514 V521 Q223.9 525 219.9 525 H190 Z"><title>copy + destroy citm: 1.13×</title></path>
|
||||||
|
<text class="nj-charts-val" x="228.9" y="522">1.13×</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="546" text-anchor="end">== citm</text>
|
||||||
|
<path class="nj-charts-c1" d="M190 534 H217.8 Q221.8 534 221.8 538 V545 Q221.8 549 217.8 549 H190 Z"><title>== citm: 1.06×</title></path>
|
||||||
|
<text class="nj-charts-val" x="226.8" y="546">1.06×</text>
|
||||||
|
<line class="nj-charts-ref" x1="220" y1="8" x2="220" y2="560"/>
|
||||||
|
<line class="nj-charts-zero" x1="190" y1="8" x2="190" y2="560"/>
|
||||||
|
<text class="nj-charts-tick" x="355" y="594">speedup over 3.12.0 (3.12.0 = 1×), more is better</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -0,0 +1,99 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 600 618" role="img" aria-labelledby="nj-blog-price nj-blog-price-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-price">Compile time of two programs with 3.13.0, alone, with a precompiled header and as a module, compared with 3.12.0</title>
|
||||||
|
<desc id="nj-blog-price-d">Horizontal bars of the change in compile time against 3.12.0, less is better. Apple clang 21, -O0 (debug), count_numbers, 3.13.0: +38%; Apple clang 21, -O0 (debug), count_numbers, 3.13.0, precompiled header: -28%; Apple clang 21, -O0 (debug), count_numbers, 3.13.0, module: -4%; Apple clang 21, -O0 (debug), server_config, 3.13.0: +34%; Apple clang 21, -O0 (debug), server_config, 3.13.0, precompiled header: -23%; Apple clang 21, -O0 (debug), server_config, 3.13.0, module: +2%; Apple clang 21, -O2, count_numbers, 3.13.0: +49%; Apple clang 21, -O2, count_numbers, 3.13.0, precompiled header: +5%; Apple clang 21, -O2, count_numbers, 3.13.0, module: +21%; Apple clang 21, -O2, server_config, 3.13.0: +45%; Apple clang 21, -O2, server_config, 3.13.0, precompiled header: +10%; Apple clang 21, -O2, server_config, 3.13.0, module: +24%; G​CC 16, -O0 (debug), count_numbers, 3.13.0: +62%; G​CC 16, -O0 (debug), count_numbers, 3.13.0, precompiled header: +10%; G​CC 16, -O0 (debug), count_numbers, 3.13.0, module: +8%; G​CC 16, -O0 (debug), server_config, 3.13.0: +47%; G​CC 16, -O0 (debug), server_config, 3.13.0, precompiled header: +14%; G​CC 16, -O0 (debug), server_config, 3.13.0, module: +15%; G​CC 16, -O2, count_numbers, 3.13.0: +58%; G​CC 16, -O2, count_numbers, 3.13.0, precompiled header: +28%; G​CC 16, -O2, count_numbers, 3.13.0, module: +25%; G​CC 16, -O2, server_config, 3.13.0: +51%; G​CC 16, -O2, server_config, 3.13.0, precompiled header: +28%; G​CC 16, -O2, server_config, 3.13.0, module: +30%.</desc>
|
||||||
|
<rect class="nj-charts-c2" x="8" y="6" width="12" height="12" rx="3"/>
|
||||||
|
<text class="nj-charts-leg" x="26" y="16.5">3.13.0</text>
|
||||||
|
<rect class="nj-charts-c1" x="97.2" y="6" width="12" height="12" rx="3"/>
|
||||||
|
<text class="nj-charts-leg" x="115.2" y="16.5">3.13.0, precompiled header</text>
|
||||||
|
<rect class="nj-charts-c3" x="350.4" y="6" width="12" height="12" rx="3"/>
|
||||||
|
<text class="nj-charts-leg" x="368.4" y="16.5">3.13.0, module</text>
|
||||||
|
<text class="nj-charts-tick" x="320.833" y="46">as fast as 3.12.0</text>
|
||||||
|
<text class="nj-charts-panel" x="8" y="68">Apple clang 21, -O0 (debug)</text>
|
||||||
|
<line class="nj-charts-grid" x1="184.167" y1="74" x2="184.167" y2="170"/>
|
||||||
|
<line class="nj-charts-grid" x1="252.5" y1="74" x2="252.5" y2="170"/>
|
||||||
|
<line class="nj-charts-grid" x1="389.167" y1="74" x2="389.167" y2="170"/>
|
||||||
|
<line class="nj-charts-grid" x1="457.5" y1="74" x2="457.5" y2="170"/>
|
||||||
|
<line class="nj-charts-grid" x1="525.833" y1="74" x2="525.833" y2="170"/>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="101" text-anchor="end">count_numbers</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 78 H446.667 Q450.667 78 450.667 82 V85 Q450.667 89 446.667 89 H320.833 Z"><title>count_numbers, Apple clang 21, -O0 (debug), 3.13.0: 1,320 ms against 955 ms with 3.12.0 (+38%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="455.667" y="87.5">+38%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 92 H229.167 Q225.167 92 225.167 96 V99 Q225.167 103 229.167 103 H320.833 Z"><title>count_numbers, Apple clang 21, -O0 (debug), 3.13.0, precompiled header: 688 ms against 955 ms with 3.12.0 (−28%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="220.167" y="101.5" text-anchor="end">−28%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 106 H311.167 Q307.167 106 307.167 110 V113 Q307.167 117 311.167 117 H320.833 Z"><title>count_numbers, Apple clang 21, -O0 (debug), 3.13.0, module: 918 ms against 955 ms with 3.12.0 (−4%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="302.167" y="115.5" text-anchor="end">−4%</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="150" text-anchor="end">server_config</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 127 H433 Q437 127 437 131 V134 Q437 138 433 138 H320.833 Z"><title>server_config, Apple clang 21, -O0 (debug), 3.13.0: 1,496 ms against 1,113 ms with 3.12.0 (+34%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="442" y="136.5">+34%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 141 H246.25 Q242.25 141 242.25 145 V148 Q242.25 152 246.25 152 H320.833 Z"><title>server_config, Apple clang 21, -O0 (debug), 3.13.0, precompiled header: 863 ms against 1,113 ms with 3.12.0 (−23%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="237.25" y="150.5" text-anchor="end">−23%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 155 H323.667 Q327.667 155 327.667 159 V162 Q327.667 166 323.667 166 H320.833 Z"><title>server_config, Apple clang 21, -O0 (debug), 3.13.0, module: 1,136 ms against 1,113 ms with 3.12.0 (+2%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="332.667" y="164.5">+2%</text>
|
||||||
|
<text class="nj-charts-panel" x="8" y="198">Apple clang 21, -O2</text>
|
||||||
|
<line class="nj-charts-grid" x1="184.167" y1="204" x2="184.167" y2="300"/>
|
||||||
|
<line class="nj-charts-grid" x1="252.5" y1="204" x2="252.5" y2="300"/>
|
||||||
|
<line class="nj-charts-grid" x1="389.167" y1="204" x2="389.167" y2="300"/>
|
||||||
|
<line class="nj-charts-grid" x1="457.5" y1="204" x2="457.5" y2="300"/>
|
||||||
|
<line class="nj-charts-grid" x1="525.833" y1="204" x2="525.833" y2="300"/>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="231" text-anchor="end">count_numbers</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 208 H484.25 Q488.25 208 488.25 212 V215 Q488.25 219 484.25 219 H320.833 Z"><title>count_numbers, Apple clang 21, -O2, 3.13.0: 2,081 ms against 1,393 ms with 3.12.0 (+49%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="493.25" y="217.5">+49%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 222 H333.917 Q337.917 222 337.917 226 V229 Q337.917 233 333.917 233 H320.833 Z"><title>count_numbers, Apple clang 21, -O2, 3.13.0, precompiled header: 1,458 ms against 1,393 ms with 3.12.0 (+5%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="342.917" y="231.5">+5%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 236 H388.583 Q392.583 236 392.583 240 V243 Q392.583 247 388.583 247 H320.833 Z"><title>count_numbers, Apple clang 21, -O2, 3.13.0, module: 1,685 ms against 1,393 ms with 3.12.0 (+21%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="397.583" y="245.5">+21%</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="280" text-anchor="end">server_config</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 257 H470.583 Q474.583 257 474.583 261 V264 Q474.583 268 470.583 268 H320.833 Z"><title>server_config, Apple clang 21, -O2, 3.13.0: 2,546 ms against 1,754 ms with 3.12.0 (+45%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="479.583" y="266.5">+45%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 271 H351 Q355 271 355 275 V278 Q355 282 351 282 H320.833 Z"><title>server_config, Apple clang 21, -O2, 3.13.0, precompiled header: 1,925 ms against 1,754 ms with 3.12.0 (+10%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="360" y="280.5">+10%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 285 H398.833 Q402.833 285 402.833 289 V292 Q402.833 296 398.833 296 H320.833 Z"><title>server_config, Apple clang 21, -O2, 3.13.0, module: 2,169 ms against 1,754 ms with 3.12.0 (+24%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="407.833" y="294.5">+24%</text>
|
||||||
|
<text class="nj-charts-panel" x="8" y="328">G​CC 16, -O0 (debug)</text>
|
||||||
|
<line class="nj-charts-grid" x1="184.167" y1="334" x2="184.167" y2="430"/>
|
||||||
|
<line class="nj-charts-grid" x1="252.5" y1="334" x2="252.5" y2="430"/>
|
||||||
|
<line class="nj-charts-grid" x1="389.167" y1="334" x2="389.167" y2="430"/>
|
||||||
|
<line class="nj-charts-grid" x1="457.5" y1="334" x2="457.5" y2="430"/>
|
||||||
|
<line class="nj-charts-grid" x1="525.833" y1="334" x2="525.833" y2="430"/>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="361" text-anchor="end">count_numbers</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 338 H528.667 Q532.667 338 532.667 342 V345 Q532.667 349 528.667 349 H320.833 Z"><title>count_numbers, G​CC 16, -O0 (debug), 3.13.0: 2,548 ms against 1,569 ms with 3.12.0 (+62%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="537.667" y="347.5">+62%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 352 H351 Q355 352 355 356 V359 Q355 363 351 363 H320.833 Z"><title>count_numbers, G​CC 16, -O0 (debug), 3.13.0, precompiled header: 1,725 ms against 1,569 ms with 3.12.0 (+10%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="360" y="361.5">+10%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 366 H344.167 Q348.167 366 348.167 370 V373 Q348.167 377 344.167 377 H320.833 Z"><title>count_numbers, G​CC 16, -O0 (debug), 3.13.0, module: 1,700 ms against 1,569 ms with 3.12.0 (+8%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="353.167" y="375.5">+8%</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="410" text-anchor="end">server_config</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 387 H477.417 Q481.417 387 481.417 391 V394 Q481.417 398 477.417 398 H320.833 Z"><title>server_config, G​CC 16, -O0 (debug), 3.13.0: 2,985 ms against 2,025 ms with 3.12.0 (+47%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="486.417" y="396.5">+47%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 401 H364.667 Q368.667 401 368.667 405 V408 Q368.667 412 364.667 412 H320.833 Z"><title>server_config, G​CC 16, -O0 (debug), 3.13.0, precompiled header: 2,302 ms against 2,025 ms with 3.12.0 (+14%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="373.667" y="410.5">+14%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 415 H368.083 Q372.083 415 372.083 419 V422 Q372.083 426 368.083 426 H320.833 Z"><title>server_config, G​CC 16, -O0 (debug), 3.13.0, module: 2,326 ms against 2,025 ms with 3.12.0 (+15%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="377.083" y="424.5">+15%</text>
|
||||||
|
<text class="nj-charts-panel" x="8" y="458">G​CC 16, -O2</text>
|
||||||
|
<line class="nj-charts-grid" x1="184.167" y1="464" x2="184.167" y2="560"/>
|
||||||
|
<line class="nj-charts-grid" x1="252.5" y1="464" x2="252.5" y2="560"/>
|
||||||
|
<line class="nj-charts-grid" x1="389.167" y1="464" x2="389.167" y2="560"/>
|
||||||
|
<line class="nj-charts-grid" x1="457.5" y1="464" x2="457.5" y2="560"/>
|
||||||
|
<line class="nj-charts-grid" x1="525.833" y1="464" x2="525.833" y2="560"/>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="491" text-anchor="end">count_numbers</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 468 H515 Q519 468 519 472 V475 Q519 479 515 479 H320.833 Z"><title>count_numbers, G​CC 16, -O2, 3.13.0: 3,702 ms against 2,336 ms with 3.12.0 (+58%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="524" y="477.5">+58%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 482 H412.5 Q416.5 482 416.5 486 V489 Q416.5 493 412.5 493 H320.833 Z"><title>count_numbers, G​CC 16, -O2, 3.13.0, precompiled header: 2,987 ms against 2,336 ms with 3.12.0 (+28%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="421.5" y="491.5">+28%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 496 H402.25 Q406.25 496 406.25 500 V503 Q406.25 507 402.25 507 H320.833 Z"><title>count_numbers, G​CC 16, -O2, 3.13.0, module: 2,913 ms against 2,336 ms with 3.12.0 (+25%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="411.25" y="505.5">+25%</text>
|
||||||
|
<text class="nj-charts-rowlabel nj-charts-code" x="142" y="540" text-anchor="end">server_config</text>
|
||||||
|
<path class="nj-charts-c2" d="M320.833 517 H491.083 Q495.083 517 495.083 521 V524 Q495.083 528 491.083 528 H320.833 Z"><title>server_config, G​CC 16, -O2, 3.13.0: 4,714 ms against 3,122 ms with 3.12.0 (+51%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="500.083" y="526.5">+51%</text>
|
||||||
|
<path class="nj-charts-c1" d="M320.833 531 H412.5 Q416.5 531 416.5 535 V538 Q416.5 542 412.5 542 H320.833 Z"><title>server_config, G​CC 16, -O2, 3.13.0, precompiled header: 4,006 ms against 3,122 ms with 3.12.0 (+28%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="421.5" y="540.5">+28%</text>
|
||||||
|
<path class="nj-charts-c3" d="M320.833 545 H419.333 Q423.333 545 423.333 549 V552 Q423.333 556 419.333 556 H320.833 Z"><title>server_config, G​CC 16, -O2, 3.13.0, module: 4,055 ms against 3,122 ms with 3.12.0 (+30%)</title></path>
|
||||||
|
<text class="nj-charts-val" x="428.333" y="554.5">+30%</text>
|
||||||
|
<line class="nj-charts-ref" x1="320.833" y1="52" x2="320.833" y2="574"/>
|
||||||
|
<text class="nj-charts-tick" x="184.167" y="590">−40%</text>
|
||||||
|
<text class="nj-charts-tick" x="252.5" y="590">−20%</text>
|
||||||
|
<text class="nj-charts-tick" x="320.833" y="590">0</text>
|
||||||
|
<text class="nj-charts-tick" x="389.167" y="590">+20%</text>
|
||||||
|
<text class="nj-charts-tick" x="457.5" y="590">+40%</text>
|
||||||
|
<text class="nj-charts-tick" x="525.833" y="590">+60%</text>
|
||||||
|
<text class="nj-charts-tick" x="355" y="608">compile time compared with 3.12.0, less is better</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,60 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--swar" viewBox="0 0 640 312" role="img" aria-labelledby="nj-blog-swar nj-blog-swar-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-swar">Finding the end of a run of ordinary string characters eight bytes at a time</title>
|
||||||
|
<desc id="nj-blog-swar-d">The eight bytes w, i, t, t, e, r, quote and comma are loaded as one word. The test sets the high bit only in byte 6, the quote. Counting trailing zero bits gives 55, divided by eight gives 6: bytes 0 to 5 are copied at once, then the quote ends the string.</desc>
|
||||||
|
<text class="fg-h" x="40" y="20">eight input bytes, loaded as one 64-bit word (byte 0 is the lowest)</text>
|
||||||
|
<rect class="fg-cell" x="40" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="72" y="60" text-anchor="middle">w</text>
|
||||||
|
<text class="nj-charts-tick" x="72" y="92">0x77</text>
|
||||||
|
<text class="nj-charts-tick" x="72" y="108">byte 0</text>
|
||||||
|
<rect class="fg-cell" x="110" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="142" y="60" text-anchor="middle">i</text>
|
||||||
|
<text class="nj-charts-tick" x="142" y="92">0x69</text>
|
||||||
|
<text class="nj-charts-tick" x="142" y="108">byte 1</text>
|
||||||
|
<rect class="fg-cell" x="180" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="212" y="60" text-anchor="middle">t</text>
|
||||||
|
<text class="nj-charts-tick" x="212" y="92">0x74</text>
|
||||||
|
<text class="nj-charts-tick" x="212" y="108">byte 2</text>
|
||||||
|
<rect class="fg-cell" x="250" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="282" y="60" text-anchor="middle">t</text>
|
||||||
|
<text class="nj-charts-tick" x="282" y="92">0x74</text>
|
||||||
|
<text class="nj-charts-tick" x="282" y="108">byte 3</text>
|
||||||
|
<rect class="fg-cell" x="320" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="352" y="60" text-anchor="middle">e</text>
|
||||||
|
<text class="nj-charts-tick" x="352" y="92">0x65</text>
|
||||||
|
<text class="nj-charts-tick" x="352" y="108">byte 4</text>
|
||||||
|
<rect class="fg-cell" x="390" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="422" y="60" text-anchor="middle">r</text>
|
||||||
|
<text class="nj-charts-tick" x="422" y="92">0x72</text>
|
||||||
|
<text class="nj-charts-tick" x="422" y="108">byte 5</text>
|
||||||
|
<rect class="fg-cell" x="460" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<rect class="nj-charts-c2" fill-opacity=".25" x="460" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="492" y="60" text-anchor="middle">"</text>
|
||||||
|
<text class="nj-charts-tick" x="492" y="92">0x22</text>
|
||||||
|
<text class="nj-charts-tick" x="492" y="108">byte 6</text>
|
||||||
|
<rect class="fg-cell" x="530" y="34" width="64" height="40" rx="4"/>
|
||||||
|
<text class="fg-code" x="562" y="60" text-anchor="middle">,</text>
|
||||||
|
<text class="nj-charts-tick" x="562" y="92">0x2C</text>
|
||||||
|
<text class="nj-charts-tick" x="562" y="108">byte 7</text>
|
||||||
|
<text class="fg-h" x="40" y="132">swar_string_special(): the high bit of each byte that needs handling</text>
|
||||||
|
<rect class="fg-cell" x="40" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="72" y="169" text-anchor="middle">0x00</text>
|
||||||
|
<rect class="fg-cell" x="110" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="142" y="169" text-anchor="middle">0x00</text>
|
||||||
|
<rect class="fg-cell" x="180" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="212" y="169" text-anchor="middle">0x00</text>
|
||||||
|
<rect class="fg-cell" x="250" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="282" y="169" text-anchor="middle">0x00</text>
|
||||||
|
<rect class="fg-cell" x="320" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="352" y="169" text-anchor="middle">0x00</text>
|
||||||
|
<rect class="fg-cell" x="390" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="422" y="169" text-anchor="middle">0x00</text>
|
||||||
|
<rect class="fg-cell" x="460" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<rect class="nj-charts-c2" fill-opacity=".25" x="460" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="492" y="169" text-anchor="middle">0x80</text>
|
||||||
|
<rect class="fg-cell" x="530" y="146" width="64" height="34" rx="4"/>
|
||||||
|
<text class="fg-code" x="562" y="169" text-anchor="middle">0x00</text>
|
||||||
|
<text class="fg-code" x="40" y="216">count_trailing_zeros(0x0080000000000000) = 55</text>
|
||||||
|
<text class="fg-code" x="40" y="242">55 / 8 = 6: copy bytes 0 to 5 in one go, then handle the quote</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="272">No branch per byte: a few subtractions, ANDs and ORs per word,</text>
|
||||||
|
<text class="fg-note fg-note--l" x="40" y="290">then one count of trailing zeros.</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,45 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 270" role="img" aria-labelledby="nj-blog-tl-accept_twitter nj-blog-tl-accept_twitter-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-tl-accept_twitter">accept() twitter.json: speedup over 3.12.0 by commit</title>
|
||||||
|
<desc id="nj-blog-tl-accept_twitter-d">accept() twitter.json: speedup over 3.12.0 at each of 353 commits, ending at 5.1x. Labeled steps, by theme: 4, 1, 4, 4, 2, 2.</desc>
|
||||||
|
<text class="nj-charts-panel" x="8" y="18">accept() twitter.json</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1×</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="160.667" x2="330" y2="160.667"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="164.667">2×</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="113.333" x2="330" y2="113.333"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="117.333">4×</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="66" x2="330" y2="66"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="70">8×</text>
|
||||||
|
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
|
||||||
|
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
|
||||||
|
<line class="nj-charts-axis" x1="117.938" y1="208" x2="117.938" y2="212"/>
|
||||||
|
<text class="nj-charts-tick" x="117.938" y="225">Sep 2026</text>
|
||||||
|
<line class="nj-charts-axis" x1="198.375" y1="208" x2="198.375" y2="212"/>
|
||||||
|
<text class="nj-charts-tick" x="198.375" y="225">Oct</text>
|
||||||
|
<polyline class="nj-charts-json" points="44.0,208.0 44.8,208.0 44.8,208.1 45.6,208.1 45.6,207.8 46.4,207.8 46.4,208.0 47.2,208.0 47.2,207.7 48.1,207.7 48.1,207.9 48.9,207.9 48.9,208.0 49.7,208.0 49.7,208.1 50.5,208.1 50.5,208.4 51.3,208.4 51.3,207.1 52.1,207.1 52.1,207.9 52.9,207.9 52.9,208.2 53.8,208.2 53.8,207.9 54.6,207.9 54.6,207.9 55.4,207.9 55.4,207.7 56.2,207.7 56.2,207.9 57.0,207.9 57.0,207.8 57.8,207.8 57.8,207.7 58.6,207.7 58.6,208.2 59.4,208.2 59.4,207.3 60.2,207.3 60.2,208.0 61.1,208.0 61.1,208.0 61.9,208.0 61.9,207.8 62.7,207.8 62.7,207.9 63.5,207.9 63.5,207.9 64.3,207.9 64.3,207.2 65.1,207.2 65.1,207.8 65.9,207.8 65.9,207.6 66.8,207.6 66.8,207.8 67.6,207.8 67.6,207.9 68.4,207.9 68.4,207.9 69.2,207.9 69.2,207.4 70.0,207.4 70.0,207.7 70.8,207.7 70.8,207.6 71.6,207.6 71.6,207.7 72.4,207.7 72.4,208.1 73.2,208.1 73.2,207.8 74.1,207.8 74.1,207.1 74.9,207.1 74.9,207.9 75.7,207.9 75.7,207.1 76.5,207.1 76.5,207.8 77.3,207.8 77.3,207.3 78.1,207.3 78.1,208.1 78.9,208.1 78.9,207.6 79.8,207.6 79.8,207.6 80.6,207.6 80.6,208.1 81.4,208.1 81.4,208.3 82.2,208.3 82.2,207.8 83.0,207.8 83.0,207.7 83.8,207.7 83.8,207.9 84.6,207.9 84.6,207.8 85.4,207.8 85.4,207.5 86.2,207.5 86.2,207.9 87.1,207.9 87.1,207.8 87.9,207.8 87.9,208.0 88.7,208.0 88.7,194.6 89.5,194.6 89.5,195.3 90.3,195.3 90.3,195.6 91.1,195.6 91.1,195.1 91.9,195.1 91.9,195.6 92.8,195.6 92.8,194.6 93.6,194.6 93.6,194.7 94.4,194.7 94.4,195.3 95.2,195.3 95.2,195.1 96.0,195.1 96.0,195.1 96.8,195.1 96.8,195.6 97.6,195.6 97.6,194.6 98.4,194.6 98.4,195.2 99.2,195.2 99.2,195.3 100.1,195.3 100.1,195.6 100.9,195.6 100.9,195.3 101.7,195.3 101.7,195.4 102.5,195.4 102.5,195.2 103.3,195.2 103.3,195.8 104.1,195.8 104.1,195.1 104.9,195.1 104.9,195.3 105.8,195.3 105.8,196.2 106.6,196.2 106.6,195.7 107.4,195.7 107.4,194.5 108.2,194.5 108.2,194.7 109.0,194.7 109.0,195.1 109.8,195.1 109.8,195.6 110.6,195.6 110.6,195.6 111.4,195.6 111.4,195.5 112.2,195.5 112.2,194.6 113.1,194.6 113.1,194.4 113.9,194.4 113.9,195.1 114.7,195.1 114.7,195.4 115.5,195.4 115.5,195.8 116.3,195.8 116.3,194.6 117.1,194.6 117.1,195.0 117.9,195.0 117.9,195.4 118.8,195.4 118.8,195.1 119.6,195.1 119.6,195.5 120.4,195.5 120.4,193.5 121.2,193.5 121.2,194.9 122.0,194.9 122.0,195.1 122.8,195.1 122.8,194.8 123.6,194.8 123.6,194.6 124.4,194.6 124.4,194.7 125.3,194.7 125.3,195.1 126.1,195.1 126.1,195.0 126.9,195.0 126.9,194.7 127.7,194.7 127.7,195.3 128.5,195.3 128.5,194.6 129.3,194.6 129.3,194.7 130.1,194.7 130.1,143.3 130.9,143.3 130.9,143.0 131.8,143.0 131.8,142.2 132.6,142.2 132.6,141.9 133.4,141.9 133.4,143.0 134.2,143.0 134.2,142.6 135.0,142.6 135.0,142.4 135.8,142.4 135.8,142.9 136.6,142.9 136.6,143.1 137.4,143.1 137.4,143.2 138.2,143.2 138.2,142.1 139.1,142.1 139.1,143.1 139.9,143.1 139.9,142.4 140.7,142.4 140.7,143.0 141.5,143.0 141.5,141.8 142.3,141.8 142.3,143.1 143.1,143.1 143.1,141.9 143.9,141.9 143.9,143.1 144.8,143.1 144.8,142.9 145.6,142.9 145.6,142.0 146.4,142.0 146.4,142.5 147.2,142.5 147.2,142.4 148.0,142.4 148.0,143.5 148.8,143.5 148.8,142.9 149.6,142.9 149.6,142.3 150.4,142.3 150.4,143.1 151.2,143.1 151.2,141.9 152.1,141.9 152.1,142.4 152.9,142.4 152.9,141.9 153.7,141.9 153.7,141.8 154.5,141.8 154.5,141.3 155.3,141.3 155.3,142.9 156.1,142.9 156.1,141.2 156.9,141.2 156.9,142.8 157.8,142.8 157.8,142.6 158.6,142.6 158.6,142.3 159.4,142.3 159.4,142.7 160.2,142.7 160.2,142.0 161.0,142.0 161.0,141.5 161.8,141.5 161.8,141.2 162.6,141.2 162.6,141.4 163.4,141.4 163.4,141.9 164.2,141.9 164.2,141.6 165.1,141.6 165.1,141.8 165.9,141.8 165.9,142.0 166.7,142.0 166.7,142.0 167.5,142.0 167.5,141.5 168.3,141.5 168.3,142.4 169.1,142.4 169.1,141.7 169.9,141.7 169.9,142.4 170.8,142.4 170.8,141.9 171.6,141.9 171.6,141.7 172.4,141.7 172.4,141.1 173.2,141.1 173.2,141.5 174.0,141.5 174.0,141.6 174.8,141.6 174.8,141.7 175.6,141.7 175.6,140.3 176.4,140.3 176.4,140.7 177.2,140.7 177.2,140.6 178.1,140.6 178.1,141.0 178.9,141.0 178.9,141.8 179.7,141.8 179.7,141.6 180.5,141.6 180.5,141.5 181.3,141.5 181.3,141.0 182.1,141.0 182.1,141.1 182.9,141.1 182.9,140.7 183.8,140.7 183.8,140.4 184.6,140.4 184.6,142.1 185.4,142.1 185.4,141.7 186.2,141.7 186.2,141.2 187.0,141.2 187.0,141.5 187.8,141.5 187.8,140.4 188.6,140.4 188.6,141.0 189.4,141.0 189.4,141.5 190.2,141.5 190.2,141.2 191.1,141.2 191.1,142.1 191.9,142.1 191.9,140.4 192.7,140.4 192.7,141.1 193.5,141.1 193.5,141.5 194.3,141.5 194.3,140.9 195.1,140.9 195.1,141.7 195.9,141.7 195.9,140.4 196.8,140.4 196.8,141.9 197.6,141.9 197.6,141.1 198.4,141.1 198.4,141.4 199.2,141.4 199.2,140.6 200.0,140.6 200.0,141.4 200.8,141.4 200.8,140.1 201.6,140.1 201.6,141.2 202.4,141.2 202.4,140.8 203.2,140.8 203.2,141.4 204.1,141.4 204.1,141.5 204.9,141.5 204.9,141.2 205.7,141.2 205.7,142.0 206.5,142.0 206.5,142.6 207.3,142.6 207.3,142.3 208.1,142.3 208.1,140.6 208.9,140.6 208.9,140.8 209.8,140.8 209.8,140.9 210.6,140.9 210.6,141.1 211.4,141.1 211.4,141.6 212.2,141.6 212.2,141.0 213.0,141.0 213.0,141.5 213.8,141.5 213.8,141.4 214.6,141.4 214.6,142.0 215.4,142.0 215.4,140.6 216.2,140.6 216.2,1Line truncated
|
||||||
|
<circle class="nj-charts-json-dot" cx="88.7" cy="194.6" r="3.5"><title>#5234: 1.22×</title></circle>
|
||||||
|
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="88.7" y1="54.0" x2="88.7" y2="189.6"/>
|
||||||
|
<circle class="nj-theme-dot" cx="88.7" cy="46.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="88.7" y="50.0" text-anchor="middle">4</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="130.1" cy="143.3" r="3.5"><title>#5283: 2.6×</title></circle>
|
||||||
|
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="130.1" y1="54.0" x2="130.1" y2="138.3"/>
|
||||||
|
<circle class="nj-theme-dot" cx="130.1" cy="46.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="130.1" y="50.0" text-anchor="middle">1</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="222.8" cy="135.2" r="3.5"><title>#5592: 2.9×</title></circle>
|
||||||
|
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="222.8" y1="54.0" x2="222.8" y2="130.2"/>
|
||||||
|
<circle class="nj-theme-dot" cx="222.8" cy="46.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="222.8" y="50.0" text-anchor="middle">4</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="243.1" cy="118.7" r="3.5"><title>#5809: 3.7×</title></circle>
|
||||||
|
<circle class="nj-charts-json-dot" cx="243.9" cy="112.7" r="3.5"><title>#5810: 4.0×</title></circle>
|
||||||
|
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="243.9" y1="36.0" x2="243.9" y2="107.7"/>
|
||||||
|
<circle class="nj-theme-dot" cx="234.9" cy="28.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="234.9" y="32.0" text-anchor="middle">4</text>
|
||||||
|
<circle class="nj-theme-dot" cx="252.9" cy="28.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="252.9" y="32.0" text-anchor="middle">2</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="273.1" cy="99.2" r="3.5"><title>#5853: 4.9×</title></circle>
|
||||||
|
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="273.1" y1="54.0" x2="273.1" y2="94.2"/>
|
||||||
|
<circle class="nj-theme-dot" cx="273.1" cy="46.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="273.1" y="50.0" text-anchor="middle">2</text>
|
||||||
|
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
|
||||||
|
<text class="nj-charts-note" x="8" y="262">speedup over 3.12.0 (log scale), more is better</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,30 @@
|
|||||||
|
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 270" role="img" aria-labelledby="nj-blog-tl-dump_canada nj-blog-tl-dump_canada-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title id="nj-blog-tl-dump_canada">dump() canada.json: speedup over 3.12.0 by commit</title>
|
||||||
|
<desc id="nj-blog-tl-dump_canada-d">dump() canada.json: speedup over 3.12.0 at each of 353 commits, ending at 3.3x. Labeled steps, by theme: 6, 3.</desc>
|
||||||
|
<text class="nj-charts-panel" x="8" y="18">dump() canada.json</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1×</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="160.667" x2="330" y2="160.667"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="164.667">2×</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="113.333" x2="330" y2="113.333"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="117.333">4×</text>
|
||||||
|
<line class="nj-charts-grid" x1="44" y1="66" x2="330" y2="66"/>
|
||||||
|
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="70">8×</text>
|
||||||
|
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
|
||||||
|
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
|
||||||
|
<line class="nj-charts-axis" x1="117.938" y1="208" x2="117.938" y2="212"/>
|
||||||
|
<text class="nj-charts-tick" x="117.938" y="225">Sep 2026</text>
|
||||||
|
<line class="nj-charts-axis" x1="198.375" y1="208" x2="198.375" y2="212"/>
|
||||||
|
<text class="nj-charts-tick" x="198.375" y="225">Oct</text>
|
||||||
|
<polyline class="nj-charts-json" points="44.0,208.0 44.8,208.0 44.8,208.0 45.6,208.0 45.6,207.2 46.4,207.2 46.4,207.9 47.2,207.9 47.2,206.7 48.1,206.7 48.1,207.5 48.9,207.5 48.9,207.7 49.7,207.7 49.7,207.3 50.5,207.3 50.5,207.9 51.3,207.9 51.3,206.6 52.1,206.6 52.1,207.5 52.9,207.5 52.9,208.0 53.8,208.0 53.8,207.6 54.6,207.6 54.6,207.2 55.4,207.2 55.4,207.5 56.2,207.5 56.2,206.6 57.0,206.6 57.0,207.1 57.8,207.1 57.8,207.1 58.6,207.1 58.6,207.2 59.4,207.2 59.4,206.6 60.2,206.6 60.2,207.5 61.1,207.5 61.1,207.6 61.9,207.6 61.9,207.7 62.7,207.7 62.7,207.2 63.5,207.2 63.5,206.5 64.3,206.5 64.3,206.8 65.1,206.8 65.1,207.1 65.9,207.1 65.9,207.0 66.8,207.0 66.8,207.1 67.6,207.1 67.6,206.7 68.4,206.7 68.4,208.2 69.2,208.2 69.2,207.5 70.0,207.5 70.0,206.6 70.8,206.6 70.8,206.7 71.6,206.7 71.6,206.3 72.4,206.3 72.4,208.4 73.2,208.4 73.2,207.3 74.1,207.3 74.1,206.8 74.9,206.8 74.9,208.0 75.7,208.0 75.7,207.8 76.5,207.8 76.5,207.1 77.3,207.1 77.3,207.5 78.1,207.5 78.1,207.9 78.9,207.9 78.9,206.3 79.8,206.3 79.8,206.8 80.6,206.8 80.6,207.5 81.4,207.5 81.4,208.1 82.2,208.1 82.2,206.6 83.0,206.6 83.0,207.2 83.8,207.2 83.8,207.2 84.6,207.2 84.6,207.4 85.4,207.4 85.4,207.0 86.2,207.0 86.2,206.7 87.1,206.7 87.1,206.5 87.9,206.5 87.9,207.6 88.7,207.6 88.7,207.4 89.5,207.4 89.5,207.2 90.3,207.2 90.3,207.5 91.1,207.5 91.1,207.6 91.9,207.6 91.9,208.0 92.8,208.0 92.8,208.2 93.6,208.2 93.6,206.7 94.4,206.7 94.4,207.4 95.2,207.4 95.2,206.7 96.0,206.7 96.0,206.6 96.8,206.6 96.8,208.0 97.6,208.0 97.6,208.0 98.4,208.0 98.4,206.3 99.2,206.3 99.2,208.3 100.1,208.3 100.1,207.8 100.9,207.8 100.9,207.4 101.7,207.4 101.7,206.3 102.5,206.3 102.5,208.1 103.3,208.1 103.3,207.5 104.1,207.5 104.1,207.4 104.9,207.4 104.9,207.5 105.8,207.5 105.8,207.7 106.6,207.7 106.6,208.0 107.4,208.0 107.4,207.0 108.2,207.0 108.2,207.5 109.0,207.5 109.0,207.2 109.8,207.2 109.8,207.7 110.6,207.7 110.6,207.3 111.4,207.3 111.4,208.0 112.2,208.0 112.2,206.7 113.1,206.7 113.1,206.6 113.9,206.6 113.9,207.2 114.7,207.2 114.7,208.1 115.5,208.1 115.5,208.1 116.3,208.1 116.3,206.7 117.1,206.7 117.1,207.8 117.9,207.8 117.9,207.6 118.8,207.6 118.8,207.4 119.6,207.4 119.6,207.1 120.4,207.1 120.4,207.5 121.2,207.5 121.2,208.8 122.0,208.8 122.0,209.2 122.8,209.2 122.8,208.4 123.6,208.4 123.6,208.3 124.4,208.3 124.4,208.0 125.3,208.0 125.3,209.0 126.1,209.0 126.1,208.5 126.9,208.5 126.9,209.1 127.7,209.1 127.7,209.2 128.5,209.2 128.5,208.6 129.3,208.6 129.3,207.7 130.1,207.7 130.1,207.7 130.9,207.7 130.9,207.5 131.8,207.5 131.8,202.2 132.6,202.2 132.6,202.3 133.4,202.3 133.4,201.7 134.2,201.7 134.2,202.3 135.0,202.3 135.0,201.9 135.8,201.9 135.8,201.2 136.6,201.2 136.6,202.1 137.4,202.1 137.4,202.6 138.2,202.6 138.2,201.5 139.1,201.5 139.1,202.3 139.9,202.3 139.9,202.3 140.7,202.3 140.7,201.7 141.5,201.7 141.5,201.3 142.3,201.3 142.3,202.5 143.1,202.5 143.1,201.1 143.9,201.1 143.9,202.9 144.8,202.9 144.8,202.2 145.6,202.2 145.6,201.7 146.4,201.7 146.4,201.7 147.2,201.7 147.2,201.6 148.0,201.6 148.0,203.0 148.8,203.0 148.8,202.7 149.6,202.7 149.6,202.4 150.4,202.4 150.4,202.8 151.2,202.8 151.2,202.1 152.1,202.1 152.1,203.1 152.9,203.1 152.9,202.3 153.7,202.3 153.7,202.3 154.5,202.3 154.5,201.8 155.3,201.8 155.3,203.1 156.1,203.1 156.1,201.8 156.9,201.8 156.9,202.6 157.8,202.6 157.8,202.6 158.6,202.6 158.6,202.9 159.4,202.9 159.4,202.3 160.2,202.3 160.2,202.3 161.0,202.3 161.0,201.6 161.8,201.6 161.8,201.5 162.6,201.5 162.6,202.4 163.4,202.4 163.4,201.7 164.2,201.7 164.2,201.2 165.1,201.2 165.1,202.2 165.9,202.2 165.9,201.6 166.7,201.6 166.7,201.6 167.5,201.6 167.5,201.8 168.3,201.8 168.3,202.1 169.1,202.1 169.1,201.7 169.9,201.7 169.9,201.4 170.8,201.4 170.8,202.2 171.6,202.2 171.6,202.7 172.4,202.7 172.4,202.4 173.2,202.4 173.2,201.7 174.0,201.7 174.0,201.7 174.8,201.7 174.8,202.3 175.6,202.3 175.6,201.6 176.4,201.6 176.4,201.7 177.2,201.7 177.2,202.2 178.1,202.2 178.1,202.2 178.9,202.2 178.9,202.4 179.7,202.4 179.7,201.7 180.5,201.7 180.5,202.1 181.3,202.1 181.3,201.6 182.1,201.6 182.1,202.2 182.9,202.2 182.9,201.9 183.8,201.9 183.8,201.9 184.6,201.9 184.6,202.5 185.4,202.5 185.4,201.6 186.2,201.6 186.2,202.1 187.0,202.1 187.0,202.3 187.8,202.3 187.8,201.8 188.6,201.8 188.6,201.5 189.4,201.5 189.4,202.3 190.2,202.3 190.2,202.2 191.1,202.2 191.1,202.9 191.9,202.9 191.9,201.3 192.7,201.3 192.7,202.2 193.5,202.2 193.5,203.6 194.3,203.6 194.3,201.9 195.1,201.9 195.1,202.4 195.9,202.4 195.9,201.7 196.8,201.7 196.8,202.7 197.6,202.7 197.6,202.3 198.4,202.3 198.4,202.5 199.2,202.5 199.2,202.1 200.0,202.1 200.0,203.4 200.8,203.4 200.8,202.1 201.6,202.1 201.6,202.3 202.4,202.3 202.4,201.9 203.2,201.9 203.2,202.3 204.1,202.3 204.1,201.7 204.9,201.7 204.9,202.4 205.7,202.4 205.7,202.3 206.5,202.3 206.5,202.9 207.3,202.9 207.3,202.9 208.1,202.9 208.1,202.2 208.9,202.2 208.9,202.4 209.8,202.4 209.8,202.2 210.6,202.2 210.6,202.7 211.4,202.7 211.4,202.5 212.2,202.5 212.2,202.5 213.0,202.5 213.0,201.5 213.8,201.5 213.8,203.1 214.6,203.1 214.6,203.2 215.4,203.2 215.4,201.5 216.2,201.5 216.2,2Line truncated
|
||||||
|
<circle class="nj-charts-json-dot" cx="131.8" cy="202.2" r="3.5"><title>#5285: 1.09×</title></circle>
|
||||||
|
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="131.8" y1="54.0" x2="131.8" y2="197.2"/>
|
||||||
|
<circle class="nj-theme-dot" cx="131.8" cy="46.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="131.8" y="50.0" text-anchor="middle">6</text>
|
||||||
|
<circle class="nj-charts-json-dot" cx="246.3" cy="130.7" r="3.5"><title>#5634: 3.1×</title></circle>
|
||||||
|
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="246.3" y1="54.0" x2="246.3" y2="125.7"/>
|
||||||
|
<circle class="nj-theme-dot" cx="246.3" cy="46.0" r="7.5"/>
|
||||||
|
<text class="nj-theme-num" x="246.3" y="50.0" text-anchor="middle">3</text>
|
||||||
|
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
|
||||||
|
<text class="nj-charts-note" x="8" y="262">speedup over 3.12.0 (log scale), more is better</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 10 KiB |