Compare commits

...
Author SHA1 Message Date
Niels Lohmann 20e022655d json_view: node_kind in the node writers, spell out read_only, comment fixes
The builder and the edit functions take and store a node_kind instead of
casting a value_t; the numbering assertions and the comments now speak of
node_kind, and the link kind notes that it follows value_t::discarded.
The read-only aliases name view_mode::read_only like the editable ones.
view_mode.md says the mode is a compile-time property of the type.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-26 07:12:05 +01:00
Niels Lohmann 266432ea65 json_view: view_mode enum instead of bool Editable, node_kind for node::kind
basic_json_document and basic_json_view take a view_mode (read_only, the
default, or editable) instead of a bool: basic_json_document<json, true>
did not say what true means. The shortcut aliases are unchanged.

The kind of a node is a node_kind (the value_t values plus link, 10 as
before), which removes the static_cast<std::uint8_t>(value_t::...) of
every comparison. The node and image layouts are unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 20:41:47 +01:00
37 changed files with 563 additions and 289 deletions

No files matched your search

+1
View File
@@ -90,6 +90,7 @@ cc_library(
"include/nlohmann/detail/view/simd.hpp", "include/nlohmann/detail/view/simd.hpp",
"include/nlohmann/detail/view/string_ref.hpp", "include/nlohmann/detail/view/string_ref.hpp",
"include/nlohmann/detail/view/value.hpp", "include/nlohmann/detail/view/value.hpp",
"include/nlohmann/detail/view/view_mode.hpp",
"include/nlohmann/json.hpp", "include/nlohmann/json.hpp",
"include/nlohmann/json_fwd.hpp", "include/nlohmann/json_fwd.hpp",
"include/nlohmann/json_literals.hpp", "include/nlohmann/json_literals.hpp",
@@ -12,7 +12,7 @@ void erase(view_type array, I idx);
std::size_t erase(const json_pointer& ptr); std::size_t erase(const json_pointer& ptr);
``` ```
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `erase`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `erase`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many 1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
template<typename BasicJsonType, bool Editable = false> template<typename BasicJsonType, view_mode Mode = view_mode::read_only>
class basic_json_document; class basic_json_document;
``` ```
@@ -20,9 +20,9 @@ is move-only: a copy would duplicate the whole index, the decoded-string buffer,
enough in every use we know of. The index, the decoded strings, and the owned text live in one heap block that the enough in every use we know of. The index, the decoded strings, and the owned text live in one heap block that the
document holds through a `#!cpp std::unique_ptr`; this is also why views survive a move of the document. document holds through a `#!cpp std::unique_ptr`; this is also why views survive a move of the document.
With `#!cpp Editable == true`, the document also offers [`set`](set.md), [`push_back`](push_back.md), With `#!cpp Mode == view_mode::editable`, the document also offers [`set`](set.md), [`push_back`](push_back.md),
[`insert`](insert.md), and [`erase`](erase.md) to change values in place, see [Edits](#edits) below. The source text [`insert`](insert.md), and [`erase`](erase.md) to change values in place, see [Edits](#edits) below. The source text
itself is never written; a read-only document (`#!cpp Editable == false`, the default) does not carry any of the itself is never written; a read-only document (`#!cpp view_mode::read_only`, the default) does not carry any of the
bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp static_assert`). bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp static_assert`).
## Template parameters ## Template parameters
@@ -32,9 +32,10 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
[`ordered_json`](../ordered_json.md). Only 64-bit `number_integer_t`/`number_unsigned_t` types are supported; this [`ordered_json`](../ordered_json.md). Only 64-bit `number_integer_t`/`number_unsigned_t` types are supported; this
is checked with a `static_assert`. is checked with a `static_assert`.
`Editable` `Mode`
: whether the document supports [`set`](set.md), [`push_back`](push_back.md), [`insert`](insert.md), and : a [`view_mode`](../view_mode.md): `#!cpp view_mode::editable` if the document supports [`set`](set.md),
[`erase`](erase.md) (optional, `#!cpp false` by default). See [Edits](#edits) below. [`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md) (optional, `#!cpp view_mode::read_only`
by default). See [Edits](#edits) below.
## Specializations ## Specializations
@@ -47,7 +48,7 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
## Member types ## Member types
- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType, Editable>`) - **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType, Mode>`)
- **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md) - **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md)
- **image_check** - how [`load()`](load.md) validates an image (`full`, `bounds`, `none`), see [`image_check`](load.md#image_check) - **image_check** - how [`load()`](load.md) validates an image (`full`, `bounds`, `none`), see [`image_check`](load.md#image_check)
@@ -80,15 +81,15 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
### Edits ### Edits
- [**set**](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers - [**set**](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers
to (`#!cpp Editable` documents only) to (editable documents only)
- [**push_back**](push_back.md) - append to an array (`#!cpp Editable` documents only) - [**push_back**](push_back.md) - append to an array (editable documents only)
- [**insert**](insert.md) - insert an element into an array before a given position (`#!cpp Editable` documents only) - [**insert**](insert.md) - insert an element into an array before a given position (editable documents only)
- [**erase**](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to - [**erase**](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
(`#!cpp Editable` documents only) (editable documents only)
## Edits ## Edits
An editable document (`#!cpp Editable == true`) can be changed after parsing, with [`set`](set.md), An editable document (`#!cpp view_mode::editable`) can be changed after parsing, with [`set`](set.md),
[`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md); [`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md);
[`json_editable_document`](../json_editable_document.md) and [`json_editable_document`](../json_editable_document.md) and
[`ordered_json_editable_document`](../ordered_json_editable_document.md) are the corresponding specializations. A few [`ordered_json_editable_document`](../ordered_json_editable_document.md) are the corresponding specializations. A few
@@ -5,7 +5,7 @@ template<typename I, typename V>
view_type insert(view_type array, I idx, V&& value); view_type insert(view_type array, I idx, V&& value);
``` ```
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `insert`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `insert`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
Inserts `value` into `array` as a new element before position `idx`, which must not be past the end Inserts `value` into `array` as a new element before position `idx`, which must not be past the end
@@ -13,7 +13,7 @@ editable, and it does not have to be `array`'s own document -- which is copied s
source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers, source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers,
strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...). strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `push_back`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `push_back`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
## Template parameters ## Template parameters
@@ -12,7 +12,7 @@ An edited document is written in its *current* state, with its values in documen
parser would have produced them for that JSON text: a member [`set`](set.md) added goes at the end, an parser would have produced them for that JSON text: a member [`set`](set.md) added goes at the end, an
[`erase`](erase.md)d member leaves no trace, and a float that is not finite (NaN or positive/negative infinity) [`erase`](erase.md)d member leaves no trace, and a float that is not finite (NaN or positive/negative infinity)
becomes null, the same substitution [`dump()`](../basic_json_view/dump.md) makes. The same document always saves to becomes null, the same substitution [`dump()`](../basic_json_view/dump.md) makes. The same document always saves to
the same bytes -- also across `BasicJsonType` and `#!cpp Editable`, since the image reflects document order and the same bytes -- also across `BasicJsonType` and the [`view_mode`](../view_mode.md), since the image reflects document order and
values only, not which specialization produced them. values only, not which specialization produced them.
## Return value ## Return value
@@ -18,7 +18,7 @@ template<typename V>
view_type set(const json_pointer& ptr, V&& value); view_type set(const json_pointer& ptr, V&& value);
``` ```
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `set`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `set`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
1. Replaces the value `target` refers to with `value`. 1. Replaces the value `target` refers to with `value`.
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
template<typename BasicJsonType, bool Editable = false> template<typename BasicJsonType, view_mode Mode = view_mode::read_only>
class basic_json_view; class basic_json_view;
``` ```
@@ -37,7 +37,7 @@ Moving the document itself does not invalidate its views: the index is heap-allo
`#!cpp operator ValueType()`), and no `#!cpp get_ptr()` or `#!cpp get_ref()`; see [`get`](get.md). `#!cpp operator ValueType()`), and no `#!cpp get_ptr()` or `#!cpp get_ref()`; see [`get`](get.md).
`basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an `basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an
**editable** document (`#!cpp Editable == true`) sees every edit made through **editable** document (`#!cpp view_mode::editable`) sees every edit made through
[`basic_json_document::set`](../basic_json_document/set.md) and [`basic_json_document::set`](../basic_json_document/set.md) and
[`basic_json_document::push_back`](../basic_json_document/push_back.md): once a value is changed, every view that [`basic_json_document::push_back`](../basic_json_document/push_back.md): once a value is changed, every view that
still refers to it -- including ones taken before the change -- reads the new value. See still refers to it -- including ones taken before the change -- reads the new value. See
@@ -49,9 +49,10 @@ still refers to it -- including ones taken before the change -- reads the new va
: a specialization of [`basic_json`](../basic_json/index.md), matching the : a specialization of [`basic_json`](../basic_json/index.md), matching the
[`basic_json_document`](../basic_json_document/index.md) the view was taken from. [`basic_json_document`](../basic_json_document/index.md) the view was taken from.
`Editable` `Mode`
: whether the view is of an editable document, matching the [`basic_json_document`](../basic_json_document/index.md) : a [`view_mode`](../view_mode.md): whether the view is of an editable document, matching the
it was taken from (optional, `#!cpp false` by default). See [Edits](../basic_json_document/index.md#edits). [`basic_json_document`](../basic_json_document/index.md) it was taken from (optional, `#!cpp view_mode::read_only` by
default). See [Edits](../basic_json_document/index.md#edits).
## Specializations ## Specializations
+1 -1
View File
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_document = basic_json_document<json>; using json_document = basic_json_document<json, view_mode::read_only>;
``` ```
This type is a [`basic_json_document`](basic_json_document/index.md) of the default [`json`](json.md) This type is a [`basic_json_document`](basic_json_document/index.md) of the default [`json`](json.md)
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_editable_document = basic_json_document<json, true>; using json_editable_document = basic_json_document<json, view_mode::editable>;
``` ```
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the default This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the default
+1 -1
View File
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_editable_view = basic_json_view<json, true>; using json_editable_view = basic_json_view<json, view_mode::editable>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a
+1 -1
View File
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_view = basic_json_view<json>; using json_view = basic_json_view<json, view_mode::read_only>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`json_document`](json_document.md). This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`json_document`](json_document.md).
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_document = basic_json_document<ordered_json>; using ordered_json_document = basic_json_document<ordered_json, view_mode::read_only>;
``` ```
This type is a [`basic_json_document`](basic_json_document/index.md) of the [`ordered_json`](ordered_json.md) This type is a [`basic_json_document`](basic_json_document/index.md) of the [`ordered_json`](ordered_json.md)
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_editable_document = basic_json_document<ordered_json, true>; using ordered_json_editable_document = basic_json_document<ordered_json, view_mode::editable>;
``` ```
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_editable_view = basic_json_view<ordered_json, true>; using ordered_json_editable_view = basic_json_view<ordered_json, view_mode::editable>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
+1 -1
View File
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_view = basic_json_view<ordered_json>; using ordered_json_view = basic_json_view<ordered_json, view_mode::read_only>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
+80
View File
@@ -0,0 +1,80 @@
# <small>nlohmann::</small>view_mode
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp
enum class view_mode : std::uint8_t
{
read_only,
editable
};
```
The second template parameter of [`basic_json_document`](basic_json_document/index.md) and
[`basic_json_view`](basic_json_view/index.md): whether a document can be changed after parsing, and whether a view is
of such a document.
`read_only`
: the default: the document is parsed once and then only read. It carries none of the bookkeeping edits need, and
calling [`set`](basic_json_document/set.md), [`push_back`](basic_json_document/push_back.md),
[`insert`](basic_json_document/insert.md), or [`erase`](basic_json_document/erase.md) on it fails to compile.
`editable`
: the document also offers [`set`](basic_json_document/set.md), [`push_back`](basic_json_document/push_back.md),
[`insert`](basic_json_document/insert.md), and [`erase`](basic_json_document/erase.md); its views see every edit,
see [Edits](basic_json_document/index.md#edits).
The shortcuts name both modes: [`json_document`](json_document.md) and [`json_view`](json_view.md) are read-only,
[`json_editable_document`](json_editable_document.md) and [`json_editable_view`](json_editable_view.md) are editable
(and likewise for `ordered_json`).
## Notes
The mode is a compile-time property of the document's and the view's type, not a setting of a call, and there is no conversion between the two modes: a read-only view reads a
container in place, while a view of an editable document follows the links that edits leave behind. A function that
accepts views therefore names the modes it accepts in its signature:
```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);
// either
template<typename BasicJsonType, view_mode Mode>
void f(basic_json_view<BasicJsonType, Mode> v);
```
A function that takes a [`json_view`](json_view.md) accepts neither an [`ordered_json_view`](ordered_json_view.md)
nor a [`json_editable_view`](json_editable_view.md). Views of the two modes still compare with
[`operator==`](basic_json_view/operator_eq.md).
## Examples
??? example
The example below shows a function template that accepts the views of both modes, and one that accepts only
views of editable documents.
```cpp
--8<-- "examples/view_mode.cpp"
```
Output:
```
--8<-- "examples/view_mode.output"
```
## See also
- [basic_json_document](basic_json_document/index.md) - a parsed JSON text
- [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`
## Version history
Added in version 3.13.0.
@@ -5,7 +5,7 @@ using ordered_json_editable_document = nlohmann::ordered_json_editable_document;
int main() int main()
{ {
// ordered_json_editable_document is basic_json_document<nlohmann::ordered_json, true> // ordered_json_editable_document is basic_json_document<nlohmann::ordered_json, nlohmann::view_mode::editable>
ordered_json_editable_document doc = ordered_json_editable_document::parse(R"({"z": 1, "a": 2, "m": 3})"); ordered_json_editable_document doc = ordered_json_editable_document::parse(R"({"z": 1, "a": 2, "m": 3})");
doc.set(doc.root(), "b", 4); // set() always appends a new member at the end doc.set(doc.root(), "b", 4); // set() always appends a new member at the end
+29
View File
@@ -0,0 +1,29 @@
#include <iostream>
#include <nlohmann/json_view.hpp>
using nlohmann::basic_json_view;
using nlohmann::view_mode;
// accepts the views of read-only and of editable documents, of json and of ordered_json
template<typename BasicJsonType, view_mode Mode>
std::size_t count_members(basic_json_view<BasicJsonType, Mode> v)
{
return v.is_object() ? v.size() : 0;
}
// accepts only the views of editable documents
template<typename BasicJsonType>
bool is_edited(basic_json_view<BasicJsonType, view_mode::editable> v)
{
return v.is_string() && v.get_string() == "edited";
}
int main()
{
const nlohmann::json_document doc = nlohmann::json_document::parse(R"({"a": 1, "b": 2})");
nlohmann::json_editable_document edoc = nlohmann::json_editable_document::parse(R"({"a": 1})");
edoc.set(edoc.root()["a"], "edited");
std::cout << count_members(doc.root()) << ' ' << count_members(edoc.root()) << '\n';
std::cout << std::boolalpha << is_edited(edoc.root()["a"]) << '\n';
}
@@ -0,0 +1,2 @@
2 1
true
+2 -2
View File
@@ -252,14 +252,14 @@ the stream's `width`/`fill` for indentation.
## Editing a document ## Editing a document
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but 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, true>`](../api/basic_json_document/index.md) -- more conveniently 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 spelled [`json_editable_document`](../api/json_editable_document.md) or
[`ordered_json_editable_document`](../api/ordered_json_editable_document.md) -- also lets you [`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 [`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) [`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. an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
`#!cpp Editable` defaults to `#!cpp false`, so `json_document`/`ordered_json_document` are unaffected -- they carry 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 none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a
runtime one. runtime one.
+1
View File
@@ -407,6 +407,7 @@ nav:
- 'ordered_json_editable_view': api/ordered_json_editable_view.md - 'ordered_json_editable_view': api/ordered_json_editable_view.md
- 'ordered_json_view': api/ordered_json_view.md - 'ordered_json_view': api/ordered_json_view.md
- 'ordered_map': api/ordered_map.md - 'ordered_map': api/ordered_map.md
- 'view_mode': api/view_mode.md
- macros: - macros:
- 'Overview': api/macros/index.md - 'Overview': api/macros/index.md
- 'JSON_ASSERT': api/macros/json_assert.md - 'JSON_ASSERT': api/macros/json_assert.md
+19 -19
View File
@@ -639,11 +639,11 @@ run_found:
switch (cur()) switch (cur())
{ {
case '{': case '{':
open(value_t::object); open(node_kind::object);
++p; ++p;
goto obj_first; goto obj_first;
case '[': case '[':
open(value_t::array); open(node_kind::array);
++p; ++p;
goto arr_first; goto arr_first;
default: default:
@@ -665,11 +665,11 @@ run_found:
if (NLOHMANN_VIEW_UNLIKELY(!string<true>())) { return false; } \ if (NLOHMANN_VIEW_UNLIKELY(!string<true>())) { return false; } \
break; \ break; \
case '{': \ case '{': \
open(value_t::object); \ open(node_kind::object); \
++p; \ ++p; \
goto obj_first; \ goto obj_first; \
case '[': \ case '[': \
open(value_t::array); \ open(node_kind::array); \
++p; \ ++p; \
goto arr_first; \ goto arr_first; \
case '-': \ case '-': \
@@ -886,17 +886,17 @@ root_done:
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_true() NLOHMANN_VIEW_ALWAYS_INLINE bool literal_true()
{ {
return literal("true", value_t::boolean, node_flags::is_true); return literal("true", node_kind::boolean, node_flags::is_true);
} }
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_false() NLOHMANN_VIEW_ALWAYS_INLINE bool literal_false()
{ {
return literal("false", value_t::boolean, 0); return literal("false", node_kind::boolean, 0);
} }
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_null() NLOHMANN_VIEW_ALWAYS_INLINE bool literal_null()
{ {
return literal("null", value_t::null, 0); return literal("null", node_kind::null, 0);
} }
/// skip whitespace (and comments); false on a malformed comment /// skip whitespace (and comments); false on a malformed comment
@@ -962,7 +962,7 @@ indent_done:
/// append a node: (kind, flags, extra, off) and the second word (len, or /// append a node: (kind, flags, extra, off) and the second word (len, or
/// an integer's value); two stores on little-endian targets /// an integer's value); two stores on little-endian targets
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(value_t k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second) NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
{ {
if (NLOHMANN_VIEW_UNLIKELY(out == cap)) if (NLOHMANN_VIEW_UNLIKELY(out == cap))
{ {
@@ -978,7 +978,7 @@ indent_done:
std::memcpy(reinterpret_cast<unsigned char*>(n), &first, 8); std::memcpy(reinterpret_cast<unsigned char*>(n), &first, 8);
std::memcpy(reinterpret_cast<unsigned char*>(n) + 8, &second, 8); std::memcpy(reinterpret_cast<unsigned char*>(n) + 8, &second, 8);
#else #else
n->kind = static_cast<std::uint8_t>(k); n->kind = k;
n->flags = flags; n->flags = flags;
n->extra = extra; n->extra = extra;
n->off = static_cast<std::uint32_t>(off); n->off = static_cast<std::uint32_t>(off);
@@ -990,7 +990,7 @@ indent_done:
return n; return n;
} }
NLOHMANN_VIEW_ALWAYS_INLINE void open(value_t k) NLOHMANN_VIEW_ALWAYS_INLINE void open(node_kind k)
{ {
// (base is read after push_node(), which moves the node array when it // (base is read after push_node(), which moves the node array when it
// grows; in one expression the order of the two is unspecified) // grows; in one expression the order of the two is unspecified)
@@ -1017,7 +1017,7 @@ indent_done:
max_depth = (std::max)(max_depth, depth); max_depth = (std::max)(max_depth, depth);
cur_idx = idx; cur_idx = idx;
cur_count = 0; cur_count = 0;
cur_is_object = k == value_t::object; cur_is_object = k == node_kind::object;
} }
NLOHMANN_VIEW_ALWAYS_INLINE void close() NLOHMANN_VIEW_ALWAYS_INLINE void close()
@@ -1047,7 +1047,7 @@ indent_done:
/// the literal `text` at p (N counts the terminating NUL) /// the literal `text` at p (N counts the terminating NUL)
template<std::size_t N> template<std::size_t N>
NLOHMANN_VIEW_ALWAYS_INLINE bool literal(const char (&text)[N], value_t k, std::uint8_t flags) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): a string literal NLOHMANN_VIEW_ALWAYS_INLINE bool literal(const char (&text)[N], node_kind k, std::uint8_t flags) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): a string literal
{ {
const std::size_t n = N - 1; const std::size_t n = N - 1;
if (NLOHMANN_VIEW_UNLIKELY(e - p < static_cast<std::ptrdiff_t>(n) || std::memcmp(p, text, n) != 0)) if (NLOHMANN_VIEW_UNLIKELY(e - p < static_cast<std::ptrdiff_t>(n) || std::memcmp(p, text, n) != 0))
@@ -1126,23 +1126,23 @@ indent_done:
is_float = true; is_float = true;
} }
value_t kind = value_t::number_float; node_kind kind = node_kind::number_float;
if (!is_float) if (!is_float)
{ {
// integers that do not fit become floats, as in parse() // integers that do not fit become floats, as in parse()
if (negative) if (negative)
{ {
kind = digits_exceed(int_start, int_digits, "9223372036854775808") ? value_t::number_float : value_t::number_integer; kind = digits_exceed(int_start, int_digits, "9223372036854775808") ? node_kind::number_float : node_kind::number_integer;
} }
else else
{ {
kind = digits_exceed(int_start, int_digits, "18446744073709551615") ? value_t::number_float : value_t::number_unsigned; kind = digits_exceed(int_start, int_digits, "18446744073709551615") ? node_kind::number_float : node_kind::number_unsigned;
} }
} }
// parse() rejects floats that overflow; only numbers whose magnitude // parse() rejects floats that overflow; only numbers whose magnitude
// could reach the largest FloatType (1e308 for double, 1e38 for // could reach the largest FloatType (1e308 for double, 1e38 for
// float) need the conversion // float) need the conversion
if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::int64_t>(int_digits) + exponent > std::numeric_limits<FloatType>::max_exponent10 - 8 && kind == value_t::number_float)) if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::int64_t>(int_digits) + exponent > std::numeric_limits<FloatType>::max_exponent10 - 8 && kind == node_kind::number_float))
{ {
if (builder::float_overflows(s, p)) if (builder::float_overflows(s, p))
{ {
@@ -1152,7 +1152,7 @@ indent_done:
} }
const auto layout = static_cast<std::uint16_t>((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8)); const auto layout = static_cast<std::uint16_t>((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8));
auto second = static_cast<std::uint64_t>(p - s); auto second = static_cast<std::uint64_t>(p - s);
if (kind != value_t::number_float) if (kind != node_kind::number_float)
{ {
// integers are converted now, while their digits are in cache // integers are converted now, while their digits are in cache
const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast<unsigned>(int_digits), e) const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast<unsigned>(int_digits), e)
@@ -1175,7 +1175,7 @@ indent_done:
p = scan_string_run<Value>(p, e); p = scan_string_run<Value>(p, e);
if (NLOHMANN_VIEW_LIKELY(cur() == '"')) if (NLOHMANN_VIEW_LIKELY(cur() == '"'))
{ {
push_node(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s)); push_node(node_kind::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
++p; ++p;
return true; return true;
} }
@@ -1185,7 +1185,7 @@ indent_done:
return false; return false;
} }
p = r.p; p = r.p;
push_node(value_t::string, node_flags::escaped, 0, r.start, r.len); push_node(node_kind::string, node_flags::escaped, 0, r.start, r.len);
return true; return true;
} }
}; };
@@ -232,7 +232,7 @@ struct document_data
/// following loads. /// following loads.
static NLOHMANN_VIEW_ALWAYS_INLINE const node* deref(const node* n) noexcept static NLOHMANN_VIEW_ALWAYS_INLINE const node* deref(const node* n) noexcept
{ {
return NLOHMANN_VIEW_LIKELY(n->kind != kind_link) ? n : follow_link(n); return NLOHMANN_VIEW_LIKELY(n->kind != node_kind::link) ? n : follow_link(n);
} }
static NLOHMANN_VIEW_NOINLINE const node* follow_link(const node* n) noexcept static NLOHMANN_VIEW_NOINLINE const node* follow_link(const node* n) noexcept
+42 -44
View File
@@ -26,12 +26,10 @@
#include <nlohmann/detail/view/lookup.hpp> #include <nlohmann/detail/view/lookup.hpp>
#include <nlohmann/detail/view/macro_scope.hpp> #include <nlohmann/detail/view/macro_scope.hpp>
#include <nlohmann/detail/view/node.hpp> #include <nlohmann/detail/view/node.hpp>
#include <nlohmann/detail/view/view_mode.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN NLOHMANN_JSON_NAMESPACE_BEGIN
template<typename BasicJsonType, bool Editable>
class basic_json_view;
namespace detail namespace detail
{ {
namespace view namespace view
@@ -120,15 +118,15 @@ class editor
View set(const View& object, string_view_t key, V&& value) View set(const View& object, string_view_t key, V&& value)
{ {
node* const o = own(object); node* const o = own(object);
if (o->kind != static_cast<std::uint8_t>(value_t::object) && o->kind != static_cast<std::uint8_t>(value_t::null)) if (o->kind != node_kind::object && o->kind != node_kind::null)
{ {
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name()); throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name());
} }
check_utf8(key.data(), key.size()); check_utf8(key.data(), key.size());
const encoded e = encode(std::forward<V>(value)); const encoded e = encode(std::forward<V>(value));
if (o->kind == static_cast<std::uint8_t>(value_t::null)) if (o->kind == node_kind::null)
{ {
become_empty(o, value_t::object); become_empty(o, node_kind::object);
} }
// an existing member: assign it (and drop later duplicates, so that // an existing member: assign it (and drop later duplicates, so that
// lookups, iteration, and materialize() agree) // lookups, iteration, and materialize() agree)
@@ -171,7 +169,7 @@ class editor
View set(const View& array, std::size_t idx, V&& value) View set(const View& array, std::size_t idx, V&& value)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array)) if (a->kind != node_kind::array)
{ {
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name()); throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name());
} }
@@ -188,14 +186,14 @@ class editor
View push_back(const View& array, V&& value) View push_back(const View& array, V&& value)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array) && a->kind != static_cast<std::uint8_t>(value_t::null)) if (a->kind != node_kind::array && a->kind != node_kind::null)
{ {
throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name()); throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name());
} }
const encoded e = encode(std::forward<V>(value)); const encoded e = encode(std::forward<V>(value));
if (a->kind == static_cast<std::uint8_t>(value_t::null)) if (a->kind == node_kind::null)
{ {
become_empty(a, value_t::array); become_empty(a, node_kind::array);
} }
node* const slot = new_slot(e); node* const slot = new_slot(e);
node* const h = block_of(m_doc, a, 1); node* const h = block_of(m_doc, a, 1);
@@ -212,7 +210,7 @@ class editor
View insert(const View& array, std::size_t idx, V&& value) View insert(const View& array, std::size_t idx, V&& value)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array)) if (a->kind != node_kind::array)
{ {
throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name()); throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name());
} }
@@ -232,7 +230,7 @@ class editor
std::size_t erase(const View& object, string_view_t key) std::size_t erase(const View& object, string_view_t key)
{ {
node* const o = own(object); node* const o = own(object);
if (o->kind != static_cast<std::uint8_t>(value_t::object)) if (o->kind != node_kind::object)
{ {
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name()); throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name());
} }
@@ -250,7 +248,7 @@ class editor
void erase(const View& array, std::size_t idx) void erase(const View& array, std::size_t idx)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array)) if (a->kind != node_kind::array)
{ {
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name()); throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name());
} }
@@ -324,11 +322,11 @@ class editor
} }
/// turn a null into an empty array/object in place /// turn a null into an empty array/object in place
static void become_empty(node* n, value_t k) noexcept static void become_empty(node* n, node_kind k) noexcept
{ {
const std::uint8_t linked = n->flags & node_flags::linked; const std::uint8_t linked = n->flags & node_flags::linked;
*n = node{}; *n = node{};
n->kind = static_cast<std::uint8_t>(k); n->kind = k;
n->flags = static_cast<std::uint8_t>(node_flags::is_new | linked); n->flags = static_cast<std::uint8_t>(node_flags::is_new | linked);
n->next = 1; n->next = 1;
} }
@@ -405,8 +403,8 @@ class editor
template<typename T> template<typename T>
struct is_view : std::false_type {}; struct is_view : std::false_type {};
template<typename J, bool E> template<typename J, view_mode M>
struct is_view<basic_json_view<J, E>> : std::true_type {}; struct is_view<basic_json_view<J, M>> : std::true_type {};
template<typename V> template<typename V>
encoded encode(V&& v) encoded encode(V&& v)
@@ -421,8 +419,8 @@ class editor
} }
/// a view of any document (copied; nothing is shared with it) /// a view of any document (copied; nothing is shared with it)
template<typename J, bool E> template<typename J, view_mode M>
encoded encode_impl(const basic_json_view<J, E>& v, encode_tag<0> /*view*/) encoded encode_impl(const basic_json_view<J, M>& v, encode_tag<0> /*view*/)
{ {
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr)) if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
{ {
@@ -434,8 +432,8 @@ class editor
r.scalar = copy_scalar(*v.m_doc, *v.m_node); r.scalar = copy_scalar(*v.m_doc, *v.m_node);
return r; return r;
} }
r.region = alloc_nodes(m_doc, count_nodes<E>(*v.m_doc, v.m_node)); r.region = alloc_nodes(m_doc, count_nodes<M == view_mode::editable>(*v.m_doc, v.m_node));
fill_nodes<E>(*v.m_doc, v.m_node, r.region); fill_nodes<M == view_mode::editable>(*v.m_doc, v.m_node, r.region);
edit_state_of(m_doc).regions.emplace(r.region, nullptr); edit_state_of(m_doc).regions.emplace(r.region, nullptr);
return r; return r;
} }
@@ -457,14 +455,14 @@ class editor
encoded encode_impl(std::nullptr_t /*unused*/, encode_tag<2> /*null*/) encoded encode_impl(std::nullptr_t /*unused*/, encode_tag<2> /*null*/)
{ {
encoded r; encoded r;
r.scalar = plain_node(value_t::null); r.scalar = plain_node(node_kind::null);
return r; return r;
} }
encoded encode_impl(bool b, encode_tag<3> /*boolean*/) encoded encode_impl(bool b, encode_tag<3> /*boolean*/)
{ {
encoded r; encoded r;
r.scalar = plain_node(value_t::boolean); r.scalar = plain_node(node_kind::boolean);
r.scalar.flags = static_cast<std::uint8_t>(r.scalar.flags | (b ? node_flags::is_true : 0)); r.scalar.flags = static_cast<std::uint8_t>(r.scalar.flags | (b ? node_flags::is_true : 0));
return r; return r;
} }
@@ -493,10 +491,10 @@ class editor
return encode_impl(BasicJsonType(std::forward<T>(x)), encode_tag<1> {}); return encode_impl(BasicJsonType(std::forward<T>(x)), encode_tag<1> {});
} }
static node plain_node(value_t k) noexcept static node plain_node(node_kind k) noexcept
{ {
node n{}; node n{};
n.kind = static_cast<std::uint8_t>(k); n.kind = k;
n.flags = node_flags::is_new; n.flags = node_flags::is_new;
return n; return n;
} }
@@ -510,19 +508,19 @@ class editor
template<typename T> template<typename T>
node number_node(T x, std::integral_constant<int, 1> /*signed*/) node number_node(T x, std::integral_constant<int, 1> /*signed*/)
{ {
return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(x)), value_t::number_integer); return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(x)), node_kind::number_integer);
} }
template<typename T> template<typename T>
node number_node(T x, std::integral_constant<int, 2> /*unsigned*/) node number_node(T x, std::integral_constant<int, 2> /*unsigned*/)
{ {
return integer_node(static_cast<std::uint64_t>(x), value_t::number_unsigned); return integer_node(static_cast<std::uint64_t>(x), node_kind::number_unsigned);
} }
/// an integer with its canonical token in the edit arena /// an integer with its canonical token in the edit arena
node integer_node(std::uint64_t bits, value_t k) node integer_node(std::uint64_t bits, node_kind k)
{ {
const bool negative = k == value_t::number_integer && static_cast<std::int64_t>(bits) < 0; const bool negative = k == node_kind::number_integer && static_cast<std::int64_t>(bits) < 0;
std::uint64_t magnitude = negative ? 0 - bits : bits; std::uint64_t magnitude = negative ? 0 - bits : bits;
std::array<char, 24> buf{}; std::array<char, 24> buf{};
char* p = buf.data() + buf.size(); // NOLINT(misc-const-correctness): digits are written through p char* p = buf.data() + buf.size(); // NOLINT(misc-const-correctness): digits are written through p
@@ -541,7 +539,7 @@ class editor
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited); n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
n.off = append_text(m_doc, p, len); n.off = append_text(m_doc, p, len);
// number_length() adds one for the sign of number_integer nodes // number_length() adds one for the sign of number_integer nodes
n.extra = static_cast<std::uint16_t>(k == value_t::number_integer ? len - 1 : len); n.extra = static_cast<std::uint16_t>(k == node_kind::number_integer ? len - 1 : len);
set_integer_bits(n, bits); set_integer_bits(n, bits);
return n; return n;
} }
@@ -563,7 +561,7 @@ class editor
{ {
text = BasicJsonType(x).dump(); text = BasicJsonType(x).dump();
} }
node n = plain_node(value_t::number_float); node n = plain_node(node_kind::number_float);
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited); n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
n.extra = 0xFFFFu; // (the digit layout is not recorded) n.extra = 0xFFFFu; // (the digit layout is not recorded)
n.off = append_text(m_doc, text.data(), text.size()); n.off = append_text(m_doc, text.data(), text.size());
@@ -578,7 +576,7 @@ class editor
{ {
throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
} }
node n = plain_node(value_t::string); node n = plain_node(node_kind::string);
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited); n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
n.off = append_text(m_doc, s, len); n.off = append_text(m_doc, s, len);
n.len = static_cast<std::uint32_t>(len); n.len = static_cast<std::uint32_t>(len);
@@ -600,10 +598,10 @@ class editor
return string_node(from.str(n), n.len); return string_node(from.str(n), n.len);
case value_t::number_integer: case value_t::number_integer:
case value_t::number_unsigned: case value_t::number_unsigned:
return integer_node(integer_bits(n), static_cast<value_t>(n.kind)); return integer_node(integer_bits(n), n.kind);
case value_t::number_float: case value_t::number_float:
{ {
node r = plain_node(value_t::number_float); node r = plain_node(node_kind::number_float);
r.flags = static_cast<std::uint8_t>(r.flags | node_flags::edited); r.flags = static_cast<std::uint8_t>(r.flags | node_flags::edited);
r.off = append_text(m_doc, from.str(n), n.len); r.off = append_text(m_doc, from.str(n), n.len);
r.len = n.len; r.len = n.len;
@@ -612,7 +610,7 @@ class editor
} }
case value_t::boolean: case value_t::boolean:
{ {
node r = plain_node(value_t::boolean); node r = plain_node(node_kind::boolean);
r.flags = static_cast<std::uint8_t>(r.flags | (n.flags & node_flags::is_true)); r.flags = static_cast<std::uint8_t>(r.flags | (n.flags & node_flags::is_true));
return r; return r;
} }
@@ -622,7 +620,7 @@ class editor
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
return plain_node(value_t::null); return plain_node(node_kind::null);
} }
} }
@@ -631,17 +629,17 @@ class editor
switch (j.type()) switch (j.type())
{ {
case value_t::null: case value_t::null:
return plain_node(value_t::null); return plain_node(node_kind::null);
case value_t::boolean: case value_t::boolean:
{ {
node r = plain_node(value_t::boolean); node r = plain_node(node_kind::boolean);
r.flags = static_cast<std::uint8_t>(r.flags | (j.template get<bool>() ? node_flags::is_true : 0)); r.flags = static_cast<std::uint8_t>(r.flags | (j.template get<bool>() ? node_flags::is_true : 0));
return r; return r;
} }
case value_t::number_integer: case value_t::number_integer:
return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(j.template get<number_integer_t>())), value_t::number_integer); return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(j.template get<number_integer_t>())), node_kind::number_integer);
case value_t::number_unsigned: case value_t::number_unsigned:
return integer_node(static_cast<std::uint64_t>(j.template get<number_unsigned_t>()), value_t::number_unsigned); return integer_node(static_cast<std::uint64_t>(j.template get<number_unsigned_t>()), node_kind::number_unsigned);
case value_t::number_float: case value_t::number_float:
return float_node(j.template get<number_float_t>()); return float_node(j.template get<number_float_t>());
case value_t::string: case value_t::string:
@@ -681,7 +679,7 @@ class editor
++r; ++r;
if (is_container(*n)) if (is_container(*n))
{ {
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::object)}); open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object});
} }
// the next value: close finished containers, then step over the key // the next value: close finished containers, then step over the key
for (;;) for (;;)
@@ -728,9 +726,9 @@ class editor
if (is_container(*n)) if (is_container(*n))
{ {
node* const self = out++; node* const self = out++;
*self = plain_node(static_cast<value_t>(n->kind)); *self = plain_node(n->kind);
self->len = n->len; self->len = n->len;
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::object), self}); open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object, self});
} }
else else
{ {
@@ -819,7 +817,7 @@ class editor
if (n->is_structured()) if (n->is_structured())
{ {
node* const self = out++; node* const self = out++;
*self = plain_node(n->type()); *self = plain_node(static_cast<node_kind>(n->type()));
self->len = static_cast<std::uint32_t>(n->size()); self->len = static_cast<std::uint32_t>(n->size());
open.push_back(frame{n->cbegin(), n->cend(), n->is_object(), self}); open.push_back(frame{n->cbegin(), n->cend(), n->is_object(), self});
} }
@@ -27,7 +27,7 @@
// live in chunks that never move; strings and number tokens written by edits // live in chunks that never move; strings and number tokens written by edits
// live in the edit arena. An array/object whose elements change gets // live in the edit arena. An array/object whose elements change gets
// node_flags::moved: its elements then live in a separate sequence (a header // node_flags::moved: its elements then live in a separate sequence (a header
// node, then the entries), whose entries link to the values (kind_link). // node, then the entries), whose entries link to the values (node_kind::link).
NLOHMANN_JSON_NAMESPACE_BEGIN NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail namespace detail
@@ -185,7 +185,7 @@ inline node* block_of(document_data& d, node* n, std::size_t extra)
return nh; return nh;
} }
reserve_moved(d); // (so that set_moved() below cannot throw: the links are marked before) reserve_moved(d); // (so that set_moved() below cannot throw: the links are marked before)
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object); const bool object = n->kind == node_kind::object;
const std::size_t used = 1 + (static_cast<std::size_t>(n->len) * (object ? 2 : 1)); const std::size_t used = 1 + (static_cast<std::size_t>(n->len) * (object ? 2 : 1));
const std::size_t cap = used + extra; const std::size_t cap = used + extra;
node* const h = alloc_nodes(d, cap); node* const h = alloc_nodes(d, cap);
@@ -251,7 +251,7 @@ inline node* find_parent(const document_data& d, const node* target)
{ {
return nullptr; // LCOV_EXCL_LINE (the value is inside c) return nullptr; // LCOV_EXCL_LINE (the value is inside c)
} }
const bool object = c->kind == static_cast<std::uint8_t>(value_t::object); const bool object = c->kind == node_kind::object;
const node* down = nullptr; const node* down = nullptr;
for (const node* p = d.first_child_edited(c), *e = d.child_end_edited(c); p != e;) for (const node* p = d.first_child_edited(c), *e = d.child_end_edited(c); p != e;)
{ {
+10 -10
View File
@@ -117,7 +117,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
r.off = (v->flags & (node_flags::moved | node_flags::is_new)) != 0 ? 0 : v->off; r.off = (v->flags & (node_flags::moved | node_flags::is_new)) != 0 ? 0 : v->off;
r.len = 0; // counted below r.len = 0; // counted below
r.next = 0; // set when the container is complete r.next = 0; // set when the container is complete
stack.push_back(frame{d.first_child_edited(v), d.child_end_edited(v), out.size(), 0, v->kind == static_cast<std::uint8_t>(value_t::object)}); stack.push_back(frame{d.first_child_edited(v), d.child_end_edited(v), out.size(), 0, v->kind == node_kind::object});
break; break;
case value_t::string: case value_t::string:
r = string_node(*v); r = string_node(*v);
@@ -138,7 +138,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
if (t[0] == 'n' || t[0] == 'i' || (v->len > 1 && t[1] == 'i')) if (t[0] == 'n' || t[0] == 'i' || (v->len > 1 && t[1] == 'i'))
{ {
r = node{}; // nan and infinity: null, as dump() writes them r = node{}; // nan and infinity: null, as dump() writes them
r.kind = static_cast<std::uint8_t>(value_t::null); r.kind = node_kind::null;
break; break;
} }
r.off = static_cast<std::uint32_t>(d.size + text_tail.size()); r.off = static_cast<std::uint32_t>(d.size + text_tail.size());
@@ -221,7 +221,7 @@ inline std::vector<std::uint8_t> save_image(const document_data& d)
// extra of an object, and the same document saves to the same bytes // extra of an object, and the same document saves to the same bytes
for (std::size_t i = 0; i < count; ++i) for (std::size_t i = 0; i < count; ++i)
{ {
if (nodes[i].kind == static_cast<std::uint8_t>(value_t::object) && nodes[i].extra != 0) if (nodes[i].kind == node_kind::object && nodes[i].extra != 0)
{ {
std::memset(o + (i * sizeof(node)) + offsetof(node, extra), 0, sizeof(nodes[i].extra)); std::memset(o + (i * sizeof(node)) + offsetof(node, extra), 0, sizeof(nodes[i].extra));
} }
@@ -337,7 +337,7 @@ inline bool float_token_finite(const unsigned char* s, std::size_t len, const nu
if (static_cast<std::int64_t>(t.int_digits) + t.exponent > 300) if (static_cast<std::int64_t>(t.int_digits) + t.exponent > 300)
{ {
node n{}; node n{};
n.kind = static_cast<std::uint8_t>(value_t::number_float); n.kind = node_kind::number_float;
n.len = static_cast<std::uint32_t>(len); n.len = static_cast<std::uint32_t>(len);
const auto v = float_value<double>(reinterpret_cast<const char*>(s), n); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) const auto v = float_value<double>(reinterpret_cast<const char*>(s), n); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
return v <= (std::numeric_limits<double>::max)() && v >= -(std::numeric_limits<double>::max)(); return v <= (std::numeric_limits<double>::max)() && v >= -(std::numeric_limits<double>::max)();
@@ -358,7 +358,7 @@ inline bool check_integer(const node& n, const unsigned char* text)
} }
// the token's value is the stored one; number_integer nodes of edits can // the token's value is the stored one; number_integer nodes of edits can
// be non-negative (as basic_json keeps the type of a value) // be non-negative (as basic_json keeps the type of a value)
const bool integer = n.kind == static_cast<std::uint8_t>(value_t::number_integer); const bool integer = n.kind == node_kind::number_integer;
if (t.is_float || t.int_digits > 20 || (t.negative && !integer)) if (t.is_float || t.int_digits > 20 || (t.negative && !integer))
{ {
return false; return false;
@@ -540,7 +540,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{ {
return false; return false;
} }
if (n.kind != static_cast<std::uint8_t>(value_t::number_float)) if (n.kind != node_kind::number_float)
{ {
return (n.extra >> 8u) == 0; return (n.extra >> 8u) == 0;
} }
@@ -581,7 +581,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
const node& n = nodes[i]; const node& n = nodes[i];
if (!stack.empty() && stack.back().object && stack.back().expect_key) if (!stack.empty() && stack.back().object && stack.back().expect_key)
{ {
if (n.kind != static_cast<std::uint8_t>(value_t::string) || !check_string(n)) if (n.kind != node_kind::string || !check_string(n))
{ {
return false; return false;
} }
@@ -622,7 +622,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{ {
// (the contents of float tokens are checked later; the token of an // (the contents of float tokens are checked later; the token of an
// integer has at most 256 characters) // integer has at most 256 characters)
if (n.kind == static_cast<std::uint8_t>(value_t::number_float)) if (n.kind == node_kind::number_float)
{ {
floats.push_back(float_range{n.off, n.len, n.extra}); floats.push_back(float_range{n.off, n.len, n.extra});
} }
@@ -640,7 +640,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{ {
return false; return false;
} }
stack.push_back(frame{i + n.next, n.len, 0, n.kind == static_cast<std::uint8_t>(value_t::object), true}); stack.push_back(frame{i + n.next, n.len, 0, n.kind == node_kind::object, true});
complete = false; complete = false;
break; break;
} }
@@ -729,7 +729,7 @@ inline void load_image(document_data& d, const std::uint8_t* image, std::size_t
for (std::size_t i = 0; i < count; ++i) for (std::size_t i = 0; i < count; ++i)
{ {
const node& n = d.nodes[i]; const node& n = d.nodes[i];
if (n.kind == static_cast<std::uint8_t>(value_t::object) && n.len >= document_data::index_min_members) if (n.kind == node_kind::object && n.len >= document_data::index_min_members)
{ {
d.large_objects.push_back(static_cast<std::uint32_t>(i)); d.large_objects.push_back(static_cast<std::uint32_t>(i));
} }
+1 -1
View File
@@ -182,7 +182,7 @@ const node* element_at(const document_data& d, const node* array, std::size_t id
template<bool Editable> template<bool Editable>
const node* last_child(const document_data& d, const node* container) noexcept const node* last_child(const document_data& d, const node* container) noexcept
{ {
const std::size_t value_offset = container->kind == static_cast<std::uint8_t>(value_t::object) ? 1 : 0; const std::size_t value_offset = container->kind == node_kind::object ? 1 : 0;
const node* const end = navigation<Editable>::end(d, container); const node* const end = navigation<Editable>::end(d, container);
const node* last = navigation<Editable>::first(d, container); const node* last = navigation<Editable>::first(d, container);
if (value_offset == 0 && container->next == container->len + 1u && (!navigation<Editable>::editable() || (container->flags & node_flags::moved) == 0)) if (value_offset == 0 && container->next == container->len + 1u && (!navigation<Editable>::editable() || (container->flags & node_flags::moved) == 0))
+1 -1
View File
@@ -101,7 +101,7 @@ BasicJsonType materialize(const document_data& d, const node* n)
case value_t::object: case value_t::object:
case value_t::array: case value_t::array:
{ {
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object); const bool object = n->kind == node_kind::object;
if (object) if (object)
{ {
sax.start_object(n->len); sax.start_object(n->len);
+27 -14
View File
@@ -41,9 +41,22 @@ struct node_flags
static constexpr std::uint8_t linked = 32; ///< an entry of a moved sequence links to this value (editable documents): its extent in the parsed layout no longer matters static constexpr std::uint8_t linked = 32; ///< an entry of a moved sequence links to this value (editable documents): its extent in the parsed layout no longer matters
}; };
/// kind of an entry of an edited sequence that stands for a value stored /// the kind of a node: the value_t of its value, or link
/// elsewhere (the address of the value's node is kept in the len/next bytes) enum class node_kind : std::uint8_t
constexpr std::uint8_t kind_link = 10; {
null = static_cast<std::uint8_t>(value_t::null),
object = static_cast<std::uint8_t>(value_t::object),
array = static_cast<std::uint8_t>(value_t::array),
string = static_cast<std::uint8_t>(value_t::string), // NOLINT(build/include_what_you_use): an enumerator, not std::string
boolean = static_cast<std::uint8_t>(value_t::boolean),
number_integer = static_cast<std::uint8_t>(value_t::number_integer),
number_unsigned = static_cast<std::uint8_t>(value_t::number_unsigned),
number_float = static_cast<std::uint8_t>(value_t::number_float),
/// an entry of an edited sequence that stands for a value stored elsewhere
/// (the address of the value's node is kept in the len/next bytes); placed
/// after value_t::discarded, which no node ever has
link = static_cast<std::uint8_t>(value_t::discarded) + 1,
};
/// One entry of the flat index, in document order. An object's members are /// One entry of the flat index, in document order. An object's members are
/// stored as key node followed by the value's subtree. Integers keep their /// stored as key node followed by the value's subtree. Integers keep their
@@ -51,7 +64,7 @@ constexpr std::uint8_t kind_link = 10;
/// always the next one, and the token length follows from `extra`). /// always the next one, and the token length follows from `extra`).
struct node struct node
{ {
std::uint8_t kind; ///< value_t, or kind_link node_kind kind; ///< the kind of the value, or link
std::uint8_t flags; ///< node_flags std::uint8_t flags; ///< node_flags
std::uint16_t extra; ///< numbers: integer digits (low byte) and fraction digits (high byte), 255 = "many"; objects: number of the object's key hash index (1-based, 0 = none); otherwise 0 std::uint16_t extra; ///< numbers: integer digits (low byte) and fraction digits (high byte), 255 = "many"; objects: number of the object's key hash index (1-based, 0 = none); otherwise 0
std::uint32_t off; ///< source offset (string content, number token, literal, bracket); arena offset if escaped/edited; number of the element sequence if moved std::uint32_t off; ///< source offset (string content, number token, literal, bracket); arena offset if escaped/edited; number of the element sequence if moved
@@ -60,13 +73,13 @@ struct node
}; };
static_assert(sizeof(node) == 16, "node must stay 16 bytes"); static_assert(sizeof(node) == 16, "node must stay 16 bytes");
// the node kinds are value_t values; the test below (object and array are 1 // images store the node kinds as bytes, and the test below (object and array
// and 2) and those of the number kinds depend on this numbering // are 1 and 2) and those of the number kinds depend on this numbering
static_assert(static_cast<std::uint8_t>(value_t::null) == 0 && static_cast<std::uint8_t>(value_t::object) == 1 static_assert(static_cast<std::uint8_t>(node_kind::null) == 0 && static_cast<std::uint8_t>(node_kind::object) == 1
&& static_cast<std::uint8_t>(value_t::array) == 2 && static_cast<std::uint8_t>(value_t::string) == 3 && static_cast<std::uint8_t>(node_kind::array) == 2 && static_cast<std::uint8_t>(node_kind::string) == 3
&& static_cast<std::uint8_t>(value_t::boolean) == 4 && static_cast<std::uint8_t>(value_t::number_integer) == 5 && static_cast<std::uint8_t>(node_kind::boolean) == 4 && static_cast<std::uint8_t>(node_kind::number_integer) == 5
&& static_cast<std::uint8_t>(value_t::number_unsigned) == 6 && static_cast<std::uint8_t>(value_t::number_float) == 7, && static_cast<std::uint8_t>(node_kind::number_unsigned) == 6 && static_cast<std::uint8_t>(node_kind::number_float) == 7,
"the node format depends on the numbering of value_t"); "the node format depends on the numbering of node_kind");
NLOHMANN_VIEW_ALWAYS_INLINE bool is_container(const node& n) noexcept NLOHMANN_VIEW_ALWAYS_INLINE bool is_container(const node& n) noexcept
{ {
@@ -86,7 +99,7 @@ inline void make_link(node& n, node* target) noexcept
{ {
target->flags = static_cast<std::uint8_t>(target->flags | node_flags::linked); target->flags = static_cast<std::uint8_t>(target->flags | node_flags::linked);
n = node{}; n = node{};
n.kind = kind_link; n.kind = node_kind::link;
std::memcpy(reinterpret_cast<unsigned char*>(&n) + 8, static_cast<const void*>(&target), sizeof(const node*)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) std::memcpy(reinterpret_cast<unsigned char*>(&n) + 8, static_cast<const void*>(&target), sizeof(const node*)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
} }
@@ -116,8 +129,8 @@ NLOHMANN_VIEW_ALWAYS_INLINE void set_integer_bits(node& n, std::uint64_t v) noex
/// token length of a number node /// token length of a number node
NLOHMANN_VIEW_ALWAYS_INLINE std::uint32_t number_length(const node& n) noexcept NLOHMANN_VIEW_ALWAYS_INLINE std::uint32_t number_length(const node& n) noexcept
{ {
return n.kind == static_cast<std::uint8_t>(value_t::number_float) ? n.len return n.kind == node_kind::number_float ? n.len
: (n.extra & 0xFFu) + (n.kind == static_cast<std::uint8_t>(value_t::number_integer) ? 1u : 0u); : (n.extra & 0xFFu) + (n.kind == node_kind::number_integer ? 1u : 0u);
} }
/// estimated number of nodes for an input of `size` bytes (one node per ~12 /// estimated number of nodes for an input of `size` bytes (one node per ~12
+1 -1
View File
@@ -874,7 +874,7 @@ class view_serializer
case value_t::object: case value_t::object:
case value_t::array: case value_t::array:
{ {
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object); const bool object = n->kind == node_kind::object;
room(8); room(8);
if (n->len == 0) if (n->len == 0)
{ {
@@ -0,0 +1,27 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstdint> // uint8_t
#include <nlohmann/json.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN
/// whether a basic_json_document and its views can be edited
enum class view_mode : std::uint8_t
{
read_only, ///< parsed once, then only read (the default)
editable, ///< also has set(), push_back(), insert(), and erase()
};
template<typename BasicJsonType, view_mode Mode>
class basic_json_view;
NLOHMANN_JSON_NAMESPACE_END
+36 -29
View File
@@ -84,6 +84,7 @@
#include <nlohmann/detail/view/serializer.hpp> #include <nlohmann/detail/view/serializer.hpp>
#include <nlohmann/detail/view/string_ref.hpp> #include <nlohmann/detail/view/string_ref.hpp>
#include <nlohmann/detail/view/value.hpp> #include <nlohmann/detail/view/value.hpp>
#include <nlohmann/detail/view/view_mode.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN NLOHMANN_JSON_NAMESPACE_BEGIN
@@ -93,13 +94,16 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
Trivially copyable (two pointers). Valid as long as the document is alive and Trivially copyable (two pointers). Valid as long as the document is alive and
has not been re-parsed, and as long as a borrowed source text is alive. has not been re-parsed, and as long as a borrowed source text is alive.
*/ */
template<typename BasicJsonType, bool Editable = false> template<typename BasicJsonType, view_mode Mode = view_mode::read_only>
class basic_json_view class basic_json_view
{ {
/// whether this is a view of an editable document
static constexpr bool is_editable = Mode == view_mode::editable;
using node = detail::view::node; using node = detail::view::node;
using document_data = detail::view::document_data; using document_data = detail::view::document_data;
/// how the index is walked (with edits only for editable documents) /// how the index is walked (with edits only for editable documents)
using navigation = detail::view::navigation<Editable>; using navigation = detail::view::navigation<is_editable>;
public: public:
using value_t = detail::value_t; using value_t = detail::value_t;
@@ -446,7 +450,7 @@ class basic_json_view
if (is_structured() && m_node->len != 0) if (is_structured() && m_node->len != 0)
{ {
return unedited() ? basic_json_view(m_doc, detail::view::last_child<false>(*m_doc, m_node) + (is_object() ? 1 : 0)) return unedited() ? basic_json_view(m_doc, detail::view::last_child<false>(*m_doc, m_node) + (is_object() ? 1 : 0))
: basic_json_view(m_doc, navigation::value(detail::view::last_child<Editable>(*m_doc, m_node) + (is_object() ? 1 : 0))); : basic_json_view(m_doc, navigation::value(detail::view::last_child<is_editable>(*m_doc, m_node) + (is_object() ? 1 : 0)));
} }
return front(); return front();
} }
@@ -504,7 +508,7 @@ class basic_json_view
/// more than once); 0 if this is not an object /// more than once); 0 if this is not an object
size_type count(string_view_t key) const size_type count(string_view_t key) const
{ {
return is_object() ? detail::view::count_members<Editable>(*m_doc, m_node, key.data(), key.size()) : 0; return is_object() ? detail::view::count_members<is_editable>(*m_doc, m_node, key.data(), key.size()) : 0;
} }
size_type count(const char* key) const size_type count(const char* key) const
@@ -646,7 +650,7 @@ class basic_json_view
} }
else else
{ {
detail::view::view_serializer<BasicJsonType, Editable>(*m_doc, out, estimate, style).dump(m_node); detail::view::view_serializer<BasicJsonType, is_editable>(*m_doc, out, estimate, style).dump(m_node);
} }
return out; return out;
} }
@@ -682,14 +686,14 @@ class basic_json_view
} }
/// a view of an editable document compares with one of a read-only document /// a view of an editable document compares with one of a read-only document
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 > template < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b) friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
{ {
return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, E>>(b)); return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, M>>(b));
} }
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 > template < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b) friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
{ {
return !(a == b); return !(a == b);
} }
@@ -727,7 +731,7 @@ class basic_json_view
{ {
return BasicJsonType(value_t::discarded); return BasicJsonType(value_t::discarded);
} }
return unedited() ? detail::view::materialize<BasicJsonType, false>(*m_doc, m_node) : detail::view::materialize<BasicJsonType, Editable>(*m_doc, m_node); return unedited() ? detail::view::materialize<BasicJsonType, false>(*m_doc, m_node) : detail::view::materialize<BasicJsonType, is_editable>(*m_doc, m_node);
} }
/// byte offset of this value in the source text (for strings: of the /// byte offset of this value in the source text (for strings: of the
@@ -740,8 +744,8 @@ class basic_json_view
} }
private: private:
template<typename, bool> friend class basic_json_document; template<typename, view_mode> friend class basic_json_document;
template<typename, bool> friend class basic_json_view; template<typename, view_mode> friend class basic_json_view;
template<typename, typename> friend class detail::view::editor; template<typename, typename> friend class detail::view::editor;
friend iterator; friend iterator;
@@ -757,11 +761,11 @@ class basic_json_view
} }
/// the template argument as a runtime condition: testing it directly in /// the template argument as a runtime condition: testing it directly in
/// `Editable && ...` makes the condition constant, which MSVC reports as /// `is_editable && ...` makes the condition constant, which MSVC reports as
/// C4127 /// C4127
static bool editable() noexcept static bool editable() noexcept
{ {
return Editable; return is_editable;
} }
/// the number of source bytes of this value (estimated for values with /// the number of source bytes of this value (estimated for values with
@@ -810,7 +814,7 @@ class basic_json_view
const node* const k = detail::view::find_member<false>(*m_doc, m_node, key.data(), key.size()); const node* const k = detail::view::find_member<false>(*m_doc, m_node, key.data(), key.size());
return k != nullptr ? basic_json_view(m_doc, k + 1) : basic_json_view(); return k != nullptr ? basic_json_view(m_doc, k + 1) : basic_json_view();
} }
const node* const k = detail::view::find_member<Editable>(*m_doc, m_node, key.data(), key.size()); const node* const k = detail::view::find_member<is_editable>(*m_doc, m_node, key.data(), key.size());
return k != nullptr ? basic_json_view(m_doc, navigation::value(k + 1)) : basic_json_view(); return k != nullptr ? basic_json_view(m_doc, navigation::value(k + 1)) : basic_json_view();
} }
@@ -824,7 +828,7 @@ class basic_json_view
/// the member of this object with @a key (the last one if the key occurs more than once), or nullptr /// the member of this object with @a key (the last one if the key occurs more than once), or nullptr
NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key) const NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key) const
{ {
return find_key(key, std::integral_constant<bool, Editable> {}); return find_key(key, std::integral_constant<bool, is_editable> {});
} }
NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key, std::true_type /*editable*/) const NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key, std::true_type /*editable*/) const
@@ -846,7 +850,7 @@ class basic_json_view
{ {
return basic_json_view(m_doc, detail::view::element_at<false>(*m_doc, m_node, idx)); return basic_json_view(m_doc, detail::view::element_at<false>(*m_doc, m_node, idx));
} }
return basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx))); return basic_json_view(m_doc, navigation::value(detail::view::element_at<is_editable>(*m_doc, m_node, idx)));
} }
// --- get() dispatch --- // --- get() dispatch ---
@@ -952,16 +956,19 @@ Borrowed parses keep a pointer to the caller's text, which must outlive the
document. Owned parses (parse_copy, rvalue std::string, streams, and inputs document. Owned parses (parse_copy, rvalue std::string, streams, and inputs
that are not contiguous byte ranges) keep their own copy. that are not contiguous byte ranges) keep their own copy.
*/ */
template<typename BasicJsonType, bool Editable = false> template<typename BasicJsonType, view_mode Mode = view_mode::read_only>
class basic_json_document class basic_json_document
{ {
/// whether the document can be edited
static constexpr bool is_editable = Mode == view_mode::editable;
using document_data = detail::view::document_data; using document_data = detail::view::document_data;
static_assert(sizeof(typename BasicJsonType::number_integer_t) == 8 && sizeof(typename BasicJsonType::number_unsigned_t) == 8, static_assert(sizeof(typename BasicJsonType::number_integer_t) == 8 && sizeof(typename BasicJsonType::number_unsigned_t) == 8,
"json_view supports 64-bit integer types only"); "json_view supports 64-bit integer types only");
public: public:
using view_type = basic_json_view<BasicJsonType, Editable>; using view_type = basic_json_view<BasicJsonType, Mode>;
using value_t = detail::value_t; using value_t = detail::value_t;
/// an empty (discarded) document /// an empty (discarded) document
@@ -1357,7 +1364,7 @@ class basic_json_document
detail::view::editor<BasicJsonType, view_type> editor() detail::view::editor<BasicJsonType, view_type> editor()
{ {
static_assert(Editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, true> (json_editable_document)"); static_assert(is_editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, view_mode::editable> (json_editable_document)");
if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded)) if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded))
{ {
detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document"); detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document");
@@ -1634,21 +1641,21 @@ class basic_json_document
}; };
/// a parsed JSON text for json /// a parsed JSON text for json
using json_document = basic_json_document<json>; using json_document = basic_json_document<json, view_mode::read_only>;
/// a value of a json_document /// a value of a json_document
using json_view = basic_json_view<json>; using json_view = basic_json_view<json, view_mode::read_only>;
/// a parsed JSON text for ordered_json /// a parsed JSON text for ordered_json
using ordered_json_document = basic_json_document<ordered_json>; using ordered_json_document = basic_json_document<ordered_json, view_mode::read_only>;
/// a value of an ordered_json_document /// a value of an ordered_json_document
using ordered_json_view = basic_json_view<ordered_json>; using ordered_json_view = basic_json_view<ordered_json, view_mode::read_only>;
/// an editable parsed JSON text for json /// an editable parsed JSON text for json
using json_editable_document = basic_json_document<json, true>; using json_editable_document = basic_json_document<json, view_mode::editable>;
/// a value of a json_editable_document /// a value of a json_editable_document
using json_editable_view = basic_json_view<json, true>; using json_editable_view = basic_json_view<json, view_mode::editable>;
/// an editable parsed JSON text for ordered_json /// an editable parsed JSON text for ordered_json
using ordered_json_editable_document = basic_json_document<ordered_json, true>; using ordered_json_editable_document = basic_json_document<ordered_json, view_mode::editable>;
/// a value of an ordered_json_editable_document /// a value of an ordered_json_editable_document
using ordered_json_editable_view = basic_json_view<ordered_json, true>; using ordered_json_editable_view = basic_json_view<ordered_json, view_mode::editable>;
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
+168 -121
View File
@@ -239,9 +239,22 @@ struct node_flags
static constexpr std::uint8_t linked = 32; ///< an entry of a moved sequence links to this value (editable documents): its extent in the parsed layout no longer matters static constexpr std::uint8_t linked = 32; ///< an entry of a moved sequence links to this value (editable documents): its extent in the parsed layout no longer matters
}; };
/// kind of an entry of an edited sequence that stands for a value stored /// the kind of a node: the value_t of its value, or link
/// elsewhere (the address of the value's node is kept in the len/next bytes) enum class node_kind : std::uint8_t
constexpr std::uint8_t kind_link = 10; {
null = static_cast<std::uint8_t>(value_t::null),
object = static_cast<std::uint8_t>(value_t::object),
array = static_cast<std::uint8_t>(value_t::array),
string = static_cast<std::uint8_t>(value_t::string), // NOLINT(build/include_what_you_use): an enumerator, not std::string
boolean = static_cast<std::uint8_t>(value_t::boolean),
number_integer = static_cast<std::uint8_t>(value_t::number_integer),
number_unsigned = static_cast<std::uint8_t>(value_t::number_unsigned),
number_float = static_cast<std::uint8_t>(value_t::number_float),
/// an entry of an edited sequence that stands for a value stored elsewhere
/// (the address of the value's node is kept in the len/next bytes); placed
/// after value_t::discarded, which no node ever has
link = static_cast<std::uint8_t>(value_t::discarded) + 1,
};
/// One entry of the flat index, in document order. An object's members are /// One entry of the flat index, in document order. An object's members are
/// stored as key node followed by the value's subtree. Integers keep their /// stored as key node followed by the value's subtree. Integers keep their
@@ -249,7 +262,7 @@ constexpr std::uint8_t kind_link = 10;
/// always the next one, and the token length follows from `extra`). /// always the next one, and the token length follows from `extra`).
struct node struct node
{ {
std::uint8_t kind; ///< value_t, or kind_link node_kind kind; ///< the kind of the value, or link
std::uint8_t flags; ///< node_flags std::uint8_t flags; ///< node_flags
std::uint16_t extra; ///< numbers: integer digits (low byte) and fraction digits (high byte), 255 = "many"; objects: number of the object's key hash index (1-based, 0 = none); otherwise 0 std::uint16_t extra; ///< numbers: integer digits (low byte) and fraction digits (high byte), 255 = "many"; objects: number of the object's key hash index (1-based, 0 = none); otherwise 0
std::uint32_t off; ///< source offset (string content, number token, literal, bracket); arena offset if escaped/edited; number of the element sequence if moved std::uint32_t off; ///< source offset (string content, number token, literal, bracket); arena offset if escaped/edited; number of the element sequence if moved
@@ -258,13 +271,13 @@ struct node
}; };
static_assert(sizeof(node) == 16, "node must stay 16 bytes"); static_assert(sizeof(node) == 16, "node must stay 16 bytes");
// the node kinds are value_t values; the test below (object and array are 1 // images store the node kinds as bytes, and the test below (object and array
// and 2) and those of the number kinds depend on this numbering // are 1 and 2) and those of the number kinds depend on this numbering
static_assert(static_cast<std::uint8_t>(value_t::null) == 0 && static_cast<std::uint8_t>(value_t::object) == 1 static_assert(static_cast<std::uint8_t>(node_kind::null) == 0 && static_cast<std::uint8_t>(node_kind::object) == 1
&& static_cast<std::uint8_t>(value_t::array) == 2 && static_cast<std::uint8_t>(value_t::string) == 3 && static_cast<std::uint8_t>(node_kind::array) == 2 && static_cast<std::uint8_t>(node_kind::string) == 3
&& static_cast<std::uint8_t>(value_t::boolean) == 4 && static_cast<std::uint8_t>(value_t::number_integer) == 5 && static_cast<std::uint8_t>(node_kind::boolean) == 4 && static_cast<std::uint8_t>(node_kind::number_integer) == 5
&& static_cast<std::uint8_t>(value_t::number_unsigned) == 6 && static_cast<std::uint8_t>(value_t::number_float) == 7, && static_cast<std::uint8_t>(node_kind::number_unsigned) == 6 && static_cast<std::uint8_t>(node_kind::number_float) == 7,
"the node format depends on the numbering of value_t"); "the node format depends on the numbering of node_kind");
NLOHMANN_VIEW_ALWAYS_INLINE bool is_container(const node& n) noexcept NLOHMANN_VIEW_ALWAYS_INLINE bool is_container(const node& n) noexcept
{ {
@@ -284,7 +297,7 @@ inline void make_link(node& n, node* target) noexcept
{ {
target->flags = static_cast<std::uint8_t>(target->flags | node_flags::linked); target->flags = static_cast<std::uint8_t>(target->flags | node_flags::linked);
n = node{}; n = node{};
n.kind = kind_link; n.kind = node_kind::link;
std::memcpy(reinterpret_cast<unsigned char*>(&n) + 8, static_cast<const void*>(&target), sizeof(const node*)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) std::memcpy(reinterpret_cast<unsigned char*>(&n) + 8, static_cast<const void*>(&target), sizeof(const node*)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
} }
@@ -314,8 +327,8 @@ NLOHMANN_VIEW_ALWAYS_INLINE void set_integer_bits(node& n, std::uint64_t v) noex
/// token length of a number node /// token length of a number node
NLOHMANN_VIEW_ALWAYS_INLINE std::uint32_t number_length(const node& n) noexcept NLOHMANN_VIEW_ALWAYS_INLINE std::uint32_t number_length(const node& n) noexcept
{ {
return n.kind == static_cast<std::uint8_t>(value_t::number_float) ? n.len return n.kind == node_kind::number_float ? n.len
: (n.extra & 0xFFu) + (n.kind == static_cast<std::uint8_t>(value_t::number_integer) ? 1u : 0u); : (n.extra & 0xFFu) + (n.kind == node_kind::number_integer ? 1u : 0u);
} }
/// estimated number of nodes for an input of `size` bytes (one node per ~12 /// estimated number of nodes for an input of `size` bytes (one node per ~12
@@ -547,7 +560,7 @@ struct document_data
/// following loads. /// following loads.
static NLOHMANN_VIEW_ALWAYS_INLINE const node* deref(const node* n) noexcept static NLOHMANN_VIEW_ALWAYS_INLINE const node* deref(const node* n) noexcept
{ {
return NLOHMANN_VIEW_LIKELY(n->kind != kind_link) ? n : follow_link(n); return NLOHMANN_VIEW_LIKELY(n->kind != node_kind::link) ? n : follow_link(n);
} }
static NLOHMANN_VIEW_NOINLINE const node* follow_link(const node* n) noexcept static NLOHMANN_VIEW_NOINLINE const node* follow_link(const node* n) noexcept
@@ -1830,11 +1843,11 @@ run_found:
switch (cur()) switch (cur())
{ {
case '{': case '{':
open(value_t::object); open(node_kind::object);
++p; ++p;
goto obj_first; goto obj_first;
case '[': case '[':
open(value_t::array); open(node_kind::array);
++p; ++p;
goto arr_first; goto arr_first;
default: default:
@@ -1856,11 +1869,11 @@ run_found:
if (NLOHMANN_VIEW_UNLIKELY(!string<true>())) { return false; } \ if (NLOHMANN_VIEW_UNLIKELY(!string<true>())) { return false; } \
break; \ break; \
case '{': \ case '{': \
open(value_t::object); \ open(node_kind::object); \
++p; \ ++p; \
goto obj_first; \ goto obj_first; \
case '[': \ case '[': \
open(value_t::array); \ open(node_kind::array); \
++p; \ ++p; \
goto arr_first; \ goto arr_first; \
case '-': \ case '-': \
@@ -2077,17 +2090,17 @@ root_done:
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_true() NLOHMANN_VIEW_ALWAYS_INLINE bool literal_true()
{ {
return literal("true", value_t::boolean, node_flags::is_true); return literal("true", node_kind::boolean, node_flags::is_true);
} }
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_false() NLOHMANN_VIEW_ALWAYS_INLINE bool literal_false()
{ {
return literal("false", value_t::boolean, 0); return literal("false", node_kind::boolean, 0);
} }
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_null() NLOHMANN_VIEW_ALWAYS_INLINE bool literal_null()
{ {
return literal("null", value_t::null, 0); return literal("null", node_kind::null, 0);
} }
/// skip whitespace (and comments); false on a malformed comment /// skip whitespace (and comments); false on a malformed comment
@@ -2153,7 +2166,7 @@ indent_done:
/// append a node: (kind, flags, extra, off) and the second word (len, or /// append a node: (kind, flags, extra, off) and the second word (len, or
/// an integer's value); two stores on little-endian targets /// an integer's value); two stores on little-endian targets
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(value_t k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second) NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
{ {
if (NLOHMANN_VIEW_UNLIKELY(out == cap)) if (NLOHMANN_VIEW_UNLIKELY(out == cap))
{ {
@@ -2169,7 +2182,7 @@ indent_done:
std::memcpy(reinterpret_cast<unsigned char*>(n), &first, 8); std::memcpy(reinterpret_cast<unsigned char*>(n), &first, 8);
std::memcpy(reinterpret_cast<unsigned char*>(n) + 8, &second, 8); std::memcpy(reinterpret_cast<unsigned char*>(n) + 8, &second, 8);
#else #else
n->kind = static_cast<std::uint8_t>(k); n->kind = k;
n->flags = flags; n->flags = flags;
n->extra = extra; n->extra = extra;
n->off = static_cast<std::uint32_t>(off); n->off = static_cast<std::uint32_t>(off);
@@ -2181,7 +2194,7 @@ indent_done:
return n; return n;
} }
NLOHMANN_VIEW_ALWAYS_INLINE void open(value_t k) NLOHMANN_VIEW_ALWAYS_INLINE void open(node_kind k)
{ {
// (base is read after push_node(), which moves the node array when it // (base is read after push_node(), which moves the node array when it
// grows; in one expression the order of the two is unspecified) // grows; in one expression the order of the two is unspecified)
@@ -2208,7 +2221,7 @@ indent_done:
max_depth = (std::max)(max_depth, depth); max_depth = (std::max)(max_depth, depth);
cur_idx = idx; cur_idx = idx;
cur_count = 0; cur_count = 0;
cur_is_object = k == value_t::object; cur_is_object = k == node_kind::object;
} }
NLOHMANN_VIEW_ALWAYS_INLINE void close() NLOHMANN_VIEW_ALWAYS_INLINE void close()
@@ -2238,7 +2251,7 @@ indent_done:
/// the literal `text` at p (N counts the terminating NUL) /// the literal `text` at p (N counts the terminating NUL)
template<std::size_t N> template<std::size_t N>
NLOHMANN_VIEW_ALWAYS_INLINE bool literal(const char (&text)[N], value_t k, std::uint8_t flags) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): a string literal NLOHMANN_VIEW_ALWAYS_INLINE bool literal(const char (&text)[N], node_kind k, std::uint8_t flags) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): a string literal
{ {
const std::size_t n = N - 1; const std::size_t n = N - 1;
if (NLOHMANN_VIEW_UNLIKELY(e - p < static_cast<std::ptrdiff_t>(n) || std::memcmp(p, text, n) != 0)) if (NLOHMANN_VIEW_UNLIKELY(e - p < static_cast<std::ptrdiff_t>(n) || std::memcmp(p, text, n) != 0))
@@ -2317,23 +2330,23 @@ indent_done:
is_float = true; is_float = true;
} }
value_t kind = value_t::number_float; node_kind kind = node_kind::number_float;
if (!is_float) if (!is_float)
{ {
// integers that do not fit become floats, as in parse() // integers that do not fit become floats, as in parse()
if (negative) if (negative)
{ {
kind = digits_exceed(int_start, int_digits, "9223372036854775808") ? value_t::number_float : value_t::number_integer; kind = digits_exceed(int_start, int_digits, "9223372036854775808") ? node_kind::number_float : node_kind::number_integer;
} }
else else
{ {
kind = digits_exceed(int_start, int_digits, "18446744073709551615") ? value_t::number_float : value_t::number_unsigned; kind = digits_exceed(int_start, int_digits, "18446744073709551615") ? node_kind::number_float : node_kind::number_unsigned;
} }
} }
// parse() rejects floats that overflow; only numbers whose magnitude // parse() rejects floats that overflow; only numbers whose magnitude
// could reach the largest FloatType (1e308 for double, 1e38 for // could reach the largest FloatType (1e308 for double, 1e38 for
// float) need the conversion // float) need the conversion
if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::int64_t>(int_digits) + exponent > std::numeric_limits<FloatType>::max_exponent10 - 8 && kind == value_t::number_float)) if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::int64_t>(int_digits) + exponent > std::numeric_limits<FloatType>::max_exponent10 - 8 && kind == node_kind::number_float))
{ {
if (builder::float_overflows(s, p)) if (builder::float_overflows(s, p))
{ {
@@ -2343,7 +2356,7 @@ indent_done:
} }
const auto layout = static_cast<std::uint16_t>((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8)); const auto layout = static_cast<std::uint16_t>((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8));
auto second = static_cast<std::uint64_t>(p - s); auto second = static_cast<std::uint64_t>(p - s);
if (kind != value_t::number_float) if (kind != node_kind::number_float)
{ {
// integers are converted now, while their digits are in cache // integers are converted now, while their digits are in cache
const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast<unsigned>(int_digits), e) const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast<unsigned>(int_digits), e)
@@ -2366,7 +2379,7 @@ indent_done:
p = scan_string_run<Value>(p, e); p = scan_string_run<Value>(p, e);
if (NLOHMANN_VIEW_LIKELY(cur() == '"')) if (NLOHMANN_VIEW_LIKELY(cur() == '"'))
{ {
push_node(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s)); push_node(node_kind::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
++p; ++p;
return true; return true;
} }
@@ -2376,7 +2389,7 @@ indent_done:
return false; return false;
} }
p = r.p; p = r.p;
push_node(value_t::string, node_flags::escaped, 0, r.start, r.len); push_node(node_kind::string, node_flags::escaped, 0, r.start, r.len);
return true; return true;
} }
}; };
@@ -3001,7 +3014,7 @@ NLOHMANN_JSON_NAMESPACE_END
// live in chunks that never move; strings and number tokens written by edits // live in chunks that never move; strings and number tokens written by edits
// live in the edit arena. An array/object whose elements change gets // live in the edit arena. An array/object whose elements change gets
// node_flags::moved: its elements then live in a separate sequence (a header // node_flags::moved: its elements then live in a separate sequence (a header
// node, then the entries), whose entries link to the values (kind_link). // node, then the entries), whose entries link to the values (node_kind::link).
NLOHMANN_JSON_NAMESPACE_BEGIN NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail namespace detail
@@ -3159,7 +3172,7 @@ inline node* block_of(document_data& d, node* n, std::size_t extra)
return nh; return nh;
} }
reserve_moved(d); // (so that set_moved() below cannot throw: the links are marked before) reserve_moved(d); // (so that set_moved() below cannot throw: the links are marked before)
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object); const bool object = n->kind == node_kind::object;
const std::size_t used = 1 + (static_cast<std::size_t>(n->len) * (object ? 2 : 1)); const std::size_t used = 1 + (static_cast<std::size_t>(n->len) * (object ? 2 : 1));
const std::size_t cap = used + extra; const std::size_t cap = used + extra;
node* const h = alloc_nodes(d, cap); node* const h = alloc_nodes(d, cap);
@@ -3225,7 +3238,7 @@ inline node* find_parent(const document_data& d, const node* target)
{ {
return nullptr; // LCOV_EXCL_LINE (the value is inside c) return nullptr; // LCOV_EXCL_LINE (the value is inside c)
} }
const bool object = c->kind == static_cast<std::uint8_t>(value_t::object); const bool object = c->kind == node_kind::object;
const node* down = nullptr; const node* down = nullptr;
for (const node* p = d.first_child_edited(c), *e = d.child_end_edited(c); p != e;) for (const node* p = d.first_child_edited(c), *e = d.child_end_edited(c); p != e;)
{ {
@@ -3692,7 +3705,7 @@ const node* element_at(const document_data& d, const node* array, std::size_t id
template<bool Editable> template<bool Editable>
const node* last_child(const document_data& d, const node* container) noexcept const node* last_child(const document_data& d, const node* container) noexcept
{ {
const std::size_t value_offset = container->kind == static_cast<std::uint8_t>(value_t::object) ? 1 : 0; const std::size_t value_offset = container->kind == node_kind::object ? 1 : 0;
const node* const end = navigation<Editable>::end(d, container); const node* const end = navigation<Editable>::end(d, container);
const node* last = navigation<Editable>::first(d, container); const node* last = navigation<Editable>::first(d, container);
if (value_offset == 0 && container->next == container->len + 1u && (!navigation<Editable>::editable() || (container->flags & node_flags::moved) == 0)) if (value_offset == 0 && container->next == container->len + 1u && (!navigation<Editable>::editable() || (container->flags & node_flags::moved) == 0))
@@ -3714,12 +3727,38 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/view/node.hpp> // #include <nlohmann/detail/view/node.hpp>
// #include <nlohmann/detail/view/view_mode.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include <cstdint> // uint8_t
// #include <nlohmann/json.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN NLOHMANN_JSON_NAMESPACE_BEGIN
template<typename BasicJsonType, bool Editable> /// whether a basic_json_document and its views can be edited
enum class view_mode : std::uint8_t
{
read_only, ///< parsed once, then only read (the default)
editable, ///< also has set(), push_back(), insert(), and erase()
};
template<typename BasicJsonType, view_mode Mode>
class basic_json_view; class basic_json_view;
NLOHMANN_JSON_NAMESPACE_END
NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail namespace detail
{ {
namespace view namespace view
@@ -3808,15 +3847,15 @@ class editor
View set(const View& object, string_view_t key, V&& value) View set(const View& object, string_view_t key, V&& value)
{ {
node* const o = own(object); node* const o = own(object);
if (o->kind != static_cast<std::uint8_t>(value_t::object) && o->kind != static_cast<std::uint8_t>(value_t::null)) if (o->kind != node_kind::object && o->kind != node_kind::null)
{ {
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name()); throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name());
} }
check_utf8(key.data(), key.size()); check_utf8(key.data(), key.size());
const encoded e = encode(std::forward<V>(value)); const encoded e = encode(std::forward<V>(value));
if (o->kind == static_cast<std::uint8_t>(value_t::null)) if (o->kind == node_kind::null)
{ {
become_empty(o, value_t::object); become_empty(o, node_kind::object);
} }
// an existing member: assign it (and drop later duplicates, so that // an existing member: assign it (and drop later duplicates, so that
// lookups, iteration, and materialize() agree) // lookups, iteration, and materialize() agree)
@@ -3859,7 +3898,7 @@ class editor
View set(const View& array, std::size_t idx, V&& value) View set(const View& array, std::size_t idx, V&& value)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array)) if (a->kind != node_kind::array)
{ {
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name()); throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name());
} }
@@ -3876,14 +3915,14 @@ class editor
View push_back(const View& array, V&& value) View push_back(const View& array, V&& value)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array) && a->kind != static_cast<std::uint8_t>(value_t::null)) if (a->kind != node_kind::array && a->kind != node_kind::null)
{ {
throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name()); throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name());
} }
const encoded e = encode(std::forward<V>(value)); const encoded e = encode(std::forward<V>(value));
if (a->kind == static_cast<std::uint8_t>(value_t::null)) if (a->kind == node_kind::null)
{ {
become_empty(a, value_t::array); become_empty(a, node_kind::array);
} }
node* const slot = new_slot(e); node* const slot = new_slot(e);
node* const h = block_of(m_doc, a, 1); node* const h = block_of(m_doc, a, 1);
@@ -3900,7 +3939,7 @@ class editor
View insert(const View& array, std::size_t idx, V&& value) View insert(const View& array, std::size_t idx, V&& value)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array)) if (a->kind != node_kind::array)
{ {
throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name()); throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name());
} }
@@ -3920,7 +3959,7 @@ class editor
std::size_t erase(const View& object, string_view_t key) std::size_t erase(const View& object, string_view_t key)
{ {
node* const o = own(object); node* const o = own(object);
if (o->kind != static_cast<std::uint8_t>(value_t::object)) if (o->kind != node_kind::object)
{ {
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name()); throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name());
} }
@@ -3938,7 +3977,7 @@ class editor
void erase(const View& array, std::size_t idx) void erase(const View& array, std::size_t idx)
{ {
node* const a = own(array); node* const a = own(array);
if (a->kind != static_cast<std::uint8_t>(value_t::array)) if (a->kind != node_kind::array)
{ {
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name()); throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name());
} }
@@ -4012,11 +4051,11 @@ class editor
} }
/// turn a null into an empty array/object in place /// turn a null into an empty array/object in place
static void become_empty(node* n, value_t k) noexcept static void become_empty(node* n, node_kind k) noexcept
{ {
const std::uint8_t linked = n->flags & node_flags::linked; const std::uint8_t linked = n->flags & node_flags::linked;
*n = node{}; *n = node{};
n->kind = static_cast<std::uint8_t>(k); n->kind = k;
n->flags = static_cast<std::uint8_t>(node_flags::is_new | linked); n->flags = static_cast<std::uint8_t>(node_flags::is_new | linked);
n->next = 1; n->next = 1;
} }
@@ -4093,8 +4132,8 @@ class editor
template<typename T> template<typename T>
struct is_view : std::false_type {}; struct is_view : std::false_type {};
template<typename J, bool E> template<typename J, view_mode M>
struct is_view<basic_json_view<J, E>> : std::true_type {}; struct is_view<basic_json_view<J, M>> : std::true_type {};
template<typename V> template<typename V>
encoded encode(V&& v) encoded encode(V&& v)
@@ -4109,8 +4148,8 @@ class editor
} }
/// a view of any document (copied; nothing is shared with it) /// a view of any document (copied; nothing is shared with it)
template<typename J, bool E> template<typename J, view_mode M>
encoded encode_impl(const basic_json_view<J, E>& v, encode_tag<0> /*view*/) encoded encode_impl(const basic_json_view<J, M>& v, encode_tag<0> /*view*/)
{ {
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr)) if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
{ {
@@ -4122,8 +4161,8 @@ class editor
r.scalar = copy_scalar(*v.m_doc, *v.m_node); r.scalar = copy_scalar(*v.m_doc, *v.m_node);
return r; return r;
} }
r.region = alloc_nodes(m_doc, count_nodes<E>(*v.m_doc, v.m_node)); r.region = alloc_nodes(m_doc, count_nodes<M == view_mode::editable>(*v.m_doc, v.m_node));
fill_nodes<E>(*v.m_doc, v.m_node, r.region); fill_nodes<M == view_mode::editable>(*v.m_doc, v.m_node, r.region);
edit_state_of(m_doc).regions.emplace(r.region, nullptr); edit_state_of(m_doc).regions.emplace(r.region, nullptr);
return r; return r;
} }
@@ -4145,14 +4184,14 @@ class editor
encoded encode_impl(std::nullptr_t /*unused*/, encode_tag<2> /*null*/) encoded encode_impl(std::nullptr_t /*unused*/, encode_tag<2> /*null*/)
{ {
encoded r; encoded r;
r.scalar = plain_node(value_t::null); r.scalar = plain_node(node_kind::null);
return r; return r;
} }
encoded encode_impl(bool b, encode_tag<3> /*boolean*/) encoded encode_impl(bool b, encode_tag<3> /*boolean*/)
{ {
encoded r; encoded r;
r.scalar = plain_node(value_t::boolean); r.scalar = plain_node(node_kind::boolean);
r.scalar.flags = static_cast<std::uint8_t>(r.scalar.flags | (b ? node_flags::is_true : 0)); r.scalar.flags = static_cast<std::uint8_t>(r.scalar.flags | (b ? node_flags::is_true : 0));
return r; return r;
} }
@@ -4181,10 +4220,10 @@ class editor
return encode_impl(BasicJsonType(std::forward<T>(x)), encode_tag<1> {}); return encode_impl(BasicJsonType(std::forward<T>(x)), encode_tag<1> {});
} }
static node plain_node(value_t k) noexcept static node plain_node(node_kind k) noexcept
{ {
node n{}; node n{};
n.kind = static_cast<std::uint8_t>(k); n.kind = k;
n.flags = node_flags::is_new; n.flags = node_flags::is_new;
return n; return n;
} }
@@ -4198,19 +4237,19 @@ class editor
template<typename T> template<typename T>
node number_node(T x, std::integral_constant<int, 1> /*signed*/) node number_node(T x, std::integral_constant<int, 1> /*signed*/)
{ {
return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(x)), value_t::number_integer); return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(x)), node_kind::number_integer);
} }
template<typename T> template<typename T>
node number_node(T x, std::integral_constant<int, 2> /*unsigned*/) node number_node(T x, std::integral_constant<int, 2> /*unsigned*/)
{ {
return integer_node(static_cast<std::uint64_t>(x), value_t::number_unsigned); return integer_node(static_cast<std::uint64_t>(x), node_kind::number_unsigned);
} }
/// an integer with its canonical token in the edit arena /// an integer with its canonical token in the edit arena
node integer_node(std::uint64_t bits, value_t k) node integer_node(std::uint64_t bits, node_kind k)
{ {
const bool negative = k == value_t::number_integer && static_cast<std::int64_t>(bits) < 0; const bool negative = k == node_kind::number_integer && static_cast<std::int64_t>(bits) < 0;
std::uint64_t magnitude = negative ? 0 - bits : bits; std::uint64_t magnitude = negative ? 0 - bits : bits;
std::array<char, 24> buf{}; std::array<char, 24> buf{};
char* p = buf.data() + buf.size(); // NOLINT(misc-const-correctness): digits are written through p char* p = buf.data() + buf.size(); // NOLINT(misc-const-correctness): digits are written through p
@@ -4229,7 +4268,7 @@ class editor
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited); n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
n.off = append_text(m_doc, p, len); n.off = append_text(m_doc, p, len);
// number_length() adds one for the sign of number_integer nodes // number_length() adds one for the sign of number_integer nodes
n.extra = static_cast<std::uint16_t>(k == value_t::number_integer ? len - 1 : len); n.extra = static_cast<std::uint16_t>(k == node_kind::number_integer ? len - 1 : len);
set_integer_bits(n, bits); set_integer_bits(n, bits);
return n; return n;
} }
@@ -4251,7 +4290,7 @@ class editor
{ {
text = BasicJsonType(x).dump(); text = BasicJsonType(x).dump();
} }
node n = plain_node(value_t::number_float); node n = plain_node(node_kind::number_float);
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited); n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
n.extra = 0xFFFFu; // (the digit layout is not recorded) n.extra = 0xFFFFu; // (the digit layout is not recorded)
n.off = append_text(m_doc, text.data(), text.size()); n.off = append_text(m_doc, text.data(), text.size());
@@ -4266,7 +4305,7 @@ class editor
{ {
throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
} }
node n = plain_node(value_t::string); node n = plain_node(node_kind::string);
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited); n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
n.off = append_text(m_doc, s, len); n.off = append_text(m_doc, s, len);
n.len = static_cast<std::uint32_t>(len); n.len = static_cast<std::uint32_t>(len);
@@ -4288,10 +4327,10 @@ class editor
return string_node(from.str(n), n.len); return string_node(from.str(n), n.len);
case value_t::number_integer: case value_t::number_integer:
case value_t::number_unsigned: case value_t::number_unsigned:
return integer_node(integer_bits(n), static_cast<value_t>(n.kind)); return integer_node(integer_bits(n), n.kind);
case value_t::number_float: case value_t::number_float:
{ {
node r = plain_node(value_t::number_float); node r = plain_node(node_kind::number_float);
r.flags = static_cast<std::uint8_t>(r.flags | node_flags::edited); r.flags = static_cast<std::uint8_t>(r.flags | node_flags::edited);
r.off = append_text(m_doc, from.str(n), n.len); r.off = append_text(m_doc, from.str(n), n.len);
r.len = n.len; r.len = n.len;
@@ -4300,7 +4339,7 @@ class editor
} }
case value_t::boolean: case value_t::boolean:
{ {
node r = plain_node(value_t::boolean); node r = plain_node(node_kind::boolean);
r.flags = static_cast<std::uint8_t>(r.flags | (n.flags & node_flags::is_true)); r.flags = static_cast<std::uint8_t>(r.flags | (n.flags & node_flags::is_true));
return r; return r;
} }
@@ -4310,7 +4349,7 @@ class editor
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
return plain_node(value_t::null); return plain_node(node_kind::null);
} }
} }
@@ -4319,17 +4358,17 @@ class editor
switch (j.type()) switch (j.type())
{ {
case value_t::null: case value_t::null:
return plain_node(value_t::null); return plain_node(node_kind::null);
case value_t::boolean: case value_t::boolean:
{ {
node r = plain_node(value_t::boolean); node r = plain_node(node_kind::boolean);
r.flags = static_cast<std::uint8_t>(r.flags | (j.template get<bool>() ? node_flags::is_true : 0)); r.flags = static_cast<std::uint8_t>(r.flags | (j.template get<bool>() ? node_flags::is_true : 0));
return r; return r;
} }
case value_t::number_integer: case value_t::number_integer:
return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(j.template get<number_integer_t>())), value_t::number_integer); return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(j.template get<number_integer_t>())), node_kind::number_integer);
case value_t::number_unsigned: case value_t::number_unsigned:
return integer_node(static_cast<std::uint64_t>(j.template get<number_unsigned_t>()), value_t::number_unsigned); return integer_node(static_cast<std::uint64_t>(j.template get<number_unsigned_t>()), node_kind::number_unsigned);
case value_t::number_float: case value_t::number_float:
return float_node(j.template get<number_float_t>()); return float_node(j.template get<number_float_t>());
case value_t::string: case value_t::string:
@@ -4369,7 +4408,7 @@ class editor
++r; ++r;
if (is_container(*n)) if (is_container(*n))
{ {
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::object)}); open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object});
} }
// the next value: close finished containers, then step over the key // the next value: close finished containers, then step over the key
for (;;) for (;;)
@@ -4416,9 +4455,9 @@ class editor
if (is_container(*n)) if (is_container(*n))
{ {
node* const self = out++; node* const self = out++;
*self = plain_node(static_cast<value_t>(n->kind)); *self = plain_node(n->kind);
self->len = n->len; self->len = n->len;
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::object), self}); open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object, self});
} }
else else
{ {
@@ -4507,7 +4546,7 @@ class editor
if (n->is_structured()) if (n->is_structured())
{ {
node* const self = out++; node* const self = out++;
*self = plain_node(n->type()); *self = plain_node(static_cast<node_kind>(n->type()));
self->len = static_cast<std::uint32_t>(n->size()); self->len = static_cast<std::uint32_t>(n->size());
open.push_back(frame{n->cbegin(), n->cend(), n->is_object(), self}); open.push_back(frame{n->cbegin(), n->cend(), n->is_object(), self});
} }
@@ -4903,7 +4942,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
r.off = (v->flags & (node_flags::moved | node_flags::is_new)) != 0 ? 0 : v->off; r.off = (v->flags & (node_flags::moved | node_flags::is_new)) != 0 ? 0 : v->off;
r.len = 0; // counted below r.len = 0; // counted below
r.next = 0; // set when the container is complete r.next = 0; // set when the container is complete
stack.push_back(frame{d.first_child_edited(v), d.child_end_edited(v), out.size(), 0, v->kind == static_cast<std::uint8_t>(value_t::object)}); stack.push_back(frame{d.first_child_edited(v), d.child_end_edited(v), out.size(), 0, v->kind == node_kind::object});
break; break;
case value_t::string: case value_t::string:
r = string_node(*v); r = string_node(*v);
@@ -4924,7 +4963,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
if (t[0] == 'n' || t[0] == 'i' || (v->len > 1 && t[1] == 'i')) if (t[0] == 'n' || t[0] == 'i' || (v->len > 1 && t[1] == 'i'))
{ {
r = node{}; // nan and infinity: null, as dump() writes them r = node{}; // nan and infinity: null, as dump() writes them
r.kind = static_cast<std::uint8_t>(value_t::null); r.kind = node_kind::null;
break; break;
} }
r.off = static_cast<std::uint32_t>(d.size + text_tail.size()); r.off = static_cast<std::uint32_t>(d.size + text_tail.size());
@@ -5007,7 +5046,7 @@ inline std::vector<std::uint8_t> save_image(const document_data& d)
// extra of an object, and the same document saves to the same bytes // extra of an object, and the same document saves to the same bytes
for (std::size_t i = 0; i < count; ++i) for (std::size_t i = 0; i < count; ++i)
{ {
if (nodes[i].kind == static_cast<std::uint8_t>(value_t::object) && nodes[i].extra != 0) if (nodes[i].kind == node_kind::object && nodes[i].extra != 0)
{ {
std::memset(o + (i * sizeof(node)) + offsetof(node, extra), 0, sizeof(nodes[i].extra)); std::memset(o + (i * sizeof(node)) + offsetof(node, extra), 0, sizeof(nodes[i].extra));
} }
@@ -5123,7 +5162,7 @@ inline bool float_token_finite(const unsigned char* s, std::size_t len, const nu
if (static_cast<std::int64_t>(t.int_digits) + t.exponent > 300) if (static_cast<std::int64_t>(t.int_digits) + t.exponent > 300)
{ {
node n{}; node n{};
n.kind = static_cast<std::uint8_t>(value_t::number_float); n.kind = node_kind::number_float;
n.len = static_cast<std::uint32_t>(len); n.len = static_cast<std::uint32_t>(len);
const auto v = float_value<double>(reinterpret_cast<const char*>(s), n); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) const auto v = float_value<double>(reinterpret_cast<const char*>(s), n); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
return v <= (std::numeric_limits<double>::max)() && v >= -(std::numeric_limits<double>::max)(); return v <= (std::numeric_limits<double>::max)() && v >= -(std::numeric_limits<double>::max)();
@@ -5144,7 +5183,7 @@ inline bool check_integer(const node& n, const unsigned char* text)
} }
// the token's value is the stored one; number_integer nodes of edits can // the token's value is the stored one; number_integer nodes of edits can
// be non-negative (as basic_json keeps the type of a value) // be non-negative (as basic_json keeps the type of a value)
const bool integer = n.kind == static_cast<std::uint8_t>(value_t::number_integer); const bool integer = n.kind == node_kind::number_integer;
if (t.is_float || t.int_digits > 20 || (t.negative && !integer)) if (t.is_float || t.int_digits > 20 || (t.negative && !integer))
{ {
return false; return false;
@@ -5326,7 +5365,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{ {
return false; return false;
} }
if (n.kind != static_cast<std::uint8_t>(value_t::number_float)) if (n.kind != node_kind::number_float)
{ {
return (n.extra >> 8u) == 0; return (n.extra >> 8u) == 0;
} }
@@ -5367,7 +5406,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
const node& n = nodes[i]; const node& n = nodes[i];
if (!stack.empty() && stack.back().object && stack.back().expect_key) if (!stack.empty() && stack.back().object && stack.back().expect_key)
{ {
if (n.kind != static_cast<std::uint8_t>(value_t::string) || !check_string(n)) if (n.kind != node_kind::string || !check_string(n))
{ {
return false; return false;
} }
@@ -5408,7 +5447,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{ {
// (the contents of float tokens are checked later; the token of an // (the contents of float tokens are checked later; the token of an
// integer has at most 256 characters) // integer has at most 256 characters)
if (n.kind == static_cast<std::uint8_t>(value_t::number_float)) if (n.kind == node_kind::number_float)
{ {
floats.push_back(float_range{n.off, n.len, n.extra}); floats.push_back(float_range{n.off, n.len, n.extra});
} }
@@ -5426,7 +5465,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{ {
return false; return false;
} }
stack.push_back(frame{i + n.next, n.len, 0, n.kind == static_cast<std::uint8_t>(value_t::object), true}); stack.push_back(frame{i + n.next, n.len, 0, n.kind == node_kind::object, true});
complete = false; complete = false;
break; break;
} }
@@ -5515,7 +5554,7 @@ inline void load_image(document_data& d, const std::uint8_t* image, std::size_t
for (std::size_t i = 0; i < count; ++i) for (std::size_t i = 0; i < count; ++i)
{ {
const node& n = d.nodes[i]; const node& n = d.nodes[i];
if (n.kind == static_cast<std::uint8_t>(value_t::object) && n.len >= document_data::index_min_members) if (n.kind == node_kind::object && n.len >= document_data::index_min_members)
{ {
d.large_objects.push_back(static_cast<std::uint32_t>(i)); d.large_objects.push_back(static_cast<std::uint32_t>(i));
} }
@@ -6108,7 +6147,7 @@ BasicJsonType materialize(const document_data& d, const node* n)
case value_t::object: case value_t::object:
case value_t::array: case value_t::array:
{ {
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object); const bool object = n->kind == node_kind::object;
if (object) if (object)
{ {
sax.start_object(n->len); sax.start_object(n->len);
@@ -7222,7 +7261,7 @@ class view_serializer
case value_t::object: case value_t::object:
case value_t::array: case value_t::array:
{ {
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object); const bool object = n->kind == node_kind::object;
room(8); room(8);
if (n->len == 0) if (n->len == 0)
{ {
@@ -7773,6 +7812,8 @@ struct is_direct_value<std::unordered_map<K, V, H, E, A>, BasicJsonType, View> :
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/view/view_mode.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN NLOHMANN_JSON_NAMESPACE_BEGIN
@@ -7782,13 +7823,16 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
Trivially copyable (two pointers). Valid as long as the document is alive and Trivially copyable (two pointers). Valid as long as the document is alive and
has not been re-parsed, and as long as a borrowed source text is alive. has not been re-parsed, and as long as a borrowed source text is alive.
*/ */
template<typename BasicJsonType, bool Editable = false> template<typename BasicJsonType, view_mode Mode = view_mode::read_only>
class basic_json_view class basic_json_view
{ {
/// whether this is a view of an editable document
static constexpr bool is_editable = Mode == view_mode::editable;
using node = detail::view::node; using node = detail::view::node;
using document_data = detail::view::document_data; using document_data = detail::view::document_data;
/// how the index is walked (with edits only for editable documents) /// how the index is walked (with edits only for editable documents)
using navigation = detail::view::navigation<Editable>; using navigation = detail::view::navigation<is_editable>;
public: public:
using value_t = detail::value_t; using value_t = detail::value_t;
@@ -8135,7 +8179,7 @@ class basic_json_view
if (is_structured() && m_node->len != 0) if (is_structured() && m_node->len != 0)
{ {
return unedited() ? basic_json_view(m_doc, detail::view::last_child<false>(*m_doc, m_node) + (is_object() ? 1 : 0)) return unedited() ? basic_json_view(m_doc, detail::view::last_child<false>(*m_doc, m_node) + (is_object() ? 1 : 0))
: basic_json_view(m_doc, navigation::value(detail::view::last_child<Editable>(*m_doc, m_node) + (is_object() ? 1 : 0))); : basic_json_view(m_doc, navigation::value(detail::view::last_child<is_editable>(*m_doc, m_node) + (is_object() ? 1 : 0)));
} }
return front(); return front();
} }
@@ -8193,7 +8237,7 @@ class basic_json_view
/// more than once); 0 if this is not an object /// more than once); 0 if this is not an object
size_type count(string_view_t key) const size_type count(string_view_t key) const
{ {
return is_object() ? detail::view::count_members<Editable>(*m_doc, m_node, key.data(), key.size()) : 0; return is_object() ? detail::view::count_members<is_editable>(*m_doc, m_node, key.data(), key.size()) : 0;
} }
size_type count(const char* key) const size_type count(const char* key) const
@@ -8335,7 +8379,7 @@ class basic_json_view
} }
else else
{ {
detail::view::view_serializer<BasicJsonType, Editable>(*m_doc, out, estimate, style).dump(m_node); detail::view::view_serializer<BasicJsonType, is_editable>(*m_doc, out, estimate, style).dump(m_node);
} }
return out; return out;
} }
@@ -8371,14 +8415,14 @@ class basic_json_view
} }
/// a view of an editable document compares with one of a read-only document /// a view of an editable document compares with one of a read-only document
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 > template < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b) friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
{ {
return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, E>>(b)); return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, M>>(b));
} }
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 > template < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b) friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
{ {
return !(a == b); return !(a == b);
} }
@@ -8416,7 +8460,7 @@ class basic_json_view
{ {
return BasicJsonType(value_t::discarded); return BasicJsonType(value_t::discarded);
} }
return unedited() ? detail::view::materialize<BasicJsonType, false>(*m_doc, m_node) : detail::view::materialize<BasicJsonType, Editable>(*m_doc, m_node); return unedited() ? detail::view::materialize<BasicJsonType, false>(*m_doc, m_node) : detail::view::materialize<BasicJsonType, is_editable>(*m_doc, m_node);
} }
/// byte offset of this value in the source text (for strings: of the /// byte offset of this value in the source text (for strings: of the
@@ -8429,8 +8473,8 @@ class basic_json_view
} }
private: private:
template<typename, bool> friend class basic_json_document; template<typename, view_mode> friend class basic_json_document;
template<typename, bool> friend class basic_json_view; template<typename, view_mode> friend class basic_json_view;
template<typename, typename> friend class detail::view::editor; template<typename, typename> friend class detail::view::editor;
friend iterator; friend iterator;
@@ -8446,11 +8490,11 @@ class basic_json_view
} }
/// the template argument as a runtime condition: testing it directly in /// the template argument as a runtime condition: testing it directly in
/// `Editable && ...` makes the condition constant, which MSVC reports as /// `is_editable && ...` makes the condition constant, which MSVC reports as
/// C4127 /// C4127
static bool editable() noexcept static bool editable() noexcept
{ {
return Editable; return is_editable;
} }
/// the number of source bytes of this value (estimated for values with /// the number of source bytes of this value (estimated for values with
@@ -8499,7 +8543,7 @@ class basic_json_view
const node* const k = detail::view::find_member<false>(*m_doc, m_node, key.data(), key.size()); const node* const k = detail::view::find_member<false>(*m_doc, m_node, key.data(), key.size());
return k != nullptr ? basic_json_view(m_doc, k + 1) : basic_json_view(); return k != nullptr ? basic_json_view(m_doc, k + 1) : basic_json_view();
} }
const node* const k = detail::view::find_member<Editable>(*m_doc, m_node, key.data(), key.size()); const node* const k = detail::view::find_member<is_editable>(*m_doc, m_node, key.data(), key.size());
return k != nullptr ? basic_json_view(m_doc, navigation::value(k + 1)) : basic_json_view(); return k != nullptr ? basic_json_view(m_doc, navigation::value(k + 1)) : basic_json_view();
} }
@@ -8513,7 +8557,7 @@ class basic_json_view
/// the member of this object with @a key (the last one if the key occurs more than once), or nullptr /// the member of this object with @a key (the last one if the key occurs more than once), or nullptr
NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key) const NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key) const
{ {
return find_key(key, std::integral_constant<bool, Editable> {}); return find_key(key, std::integral_constant<bool, is_editable> {});
} }
NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key, std::true_type /*editable*/) const NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key, std::true_type /*editable*/) const
@@ -8535,7 +8579,7 @@ class basic_json_view
{ {
return basic_json_view(m_doc, detail::view::element_at<false>(*m_doc, m_node, idx)); return basic_json_view(m_doc, detail::view::element_at<false>(*m_doc, m_node, idx));
} }
return basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx))); return basic_json_view(m_doc, navigation::value(detail::view::element_at<is_editable>(*m_doc, m_node, idx)));
} }
// --- get() dispatch --- // --- get() dispatch ---
@@ -8641,16 +8685,19 @@ Borrowed parses keep a pointer to the caller's text, which must outlive the
document. Owned parses (parse_copy, rvalue std::string, streams, and inputs document. Owned parses (parse_copy, rvalue std::string, streams, and inputs
that are not contiguous byte ranges) keep their own copy. that are not contiguous byte ranges) keep their own copy.
*/ */
template<typename BasicJsonType, bool Editable = false> template<typename BasicJsonType, view_mode Mode = view_mode::read_only>
class basic_json_document class basic_json_document
{ {
/// whether the document can be edited
static constexpr bool is_editable = Mode == view_mode::editable;
using document_data = detail::view::document_data; using document_data = detail::view::document_data;
static_assert(sizeof(typename BasicJsonType::number_integer_t) == 8 && sizeof(typename BasicJsonType::number_unsigned_t) == 8, static_assert(sizeof(typename BasicJsonType::number_integer_t) == 8 && sizeof(typename BasicJsonType::number_unsigned_t) == 8,
"json_view supports 64-bit integer types only"); "json_view supports 64-bit integer types only");
public: public:
using view_type = basic_json_view<BasicJsonType, Editable>; using view_type = basic_json_view<BasicJsonType, Mode>;
using value_t = detail::value_t; using value_t = detail::value_t;
/// an empty (discarded) document /// an empty (discarded) document
@@ -9046,7 +9093,7 @@ class basic_json_document
detail::view::editor<BasicJsonType, view_type> editor() detail::view::editor<BasicJsonType, view_type> editor()
{ {
static_assert(Editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, true> (json_editable_document)"); static_assert(is_editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, view_mode::editable> (json_editable_document)");
if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded)) if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded))
{ {
detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document"); detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document");
@@ -9323,21 +9370,21 @@ class basic_json_document
}; };
/// a parsed JSON text for json /// a parsed JSON text for json
using json_document = basic_json_document<json>; using json_document = basic_json_document<json, view_mode::read_only>;
/// a value of a json_document /// a value of a json_document
using json_view = basic_json_view<json>; using json_view = basic_json_view<json, view_mode::read_only>;
/// a parsed JSON text for ordered_json /// a parsed JSON text for ordered_json
using ordered_json_document = basic_json_document<ordered_json>; using ordered_json_document = basic_json_document<ordered_json, view_mode::read_only>;
/// a value of an ordered_json_document /// a value of an ordered_json_document
using ordered_json_view = basic_json_view<ordered_json>; using ordered_json_view = basic_json_view<ordered_json, view_mode::read_only>;
/// an editable parsed JSON text for json /// an editable parsed JSON text for json
using json_editable_document = basic_json_document<json, true>; using json_editable_document = basic_json_document<json, view_mode::editable>;
/// a value of a json_editable_document /// a value of a json_editable_document
using json_editable_view = basic_json_view<json, true>; using json_editable_view = basic_json_view<json, view_mode::editable>;
/// an editable parsed JSON text for ordered_json /// an editable parsed JSON text for ordered_json
using ordered_json_editable_document = basic_json_document<ordered_json, true>; using ordered_json_editable_document = basic_json_document<ordered_json, view_mode::editable>;
/// a value of an ordered_json_editable_document /// a value of an ordered_json_editable_document
using ordered_json_editable_view = basic_json_view<ordered_json, true>; using ordered_json_editable_view = basic_json_view<ordered_json, view_mode::editable>;
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
+2 -2
View File
@@ -592,7 +592,7 @@ TEST_CASE("json_view node integer bits")
}) })
{ {
CAPTURE(v) CAPTURE(v)
n.kind = 0x5A; n.kind = static_cast<nlohmann::detail::view::node_kind>(0x5A);
n.flags = 0xA5; n.flags = 0xA5;
n.extra = 0x1234; n.extra = 0x1234;
n.off = 0x89ABCDEFu; n.off = 0x89ABCDEFu;
@@ -601,7 +601,7 @@ TEST_CASE("json_view node integer bits")
CHECK(n.len == static_cast<std::uint32_t>(v)); CHECK(n.len == static_cast<std::uint32_t>(v));
CHECK(n.next == static_cast<std::uint32_t>(v >> 32)); CHECK(n.next == static_cast<std::uint32_t>(v >> 32));
// the other fields are untouched // the other fields are untouched
CHECK(n.kind == 0x5A); CHECK(static_cast<std::uint8_t>(n.kind) == 0x5A);
CHECK(n.flags == 0xA5); CHECK(n.flags == 0xA5);
CHECK(n.extra == 0x1234); CHECK(n.extra == 0x1234);
CHECK(n.off == 0x89ABCDEFu); CHECK(n.off == 0x89ABCDEFu);
+68 -1
View File
@@ -26,12 +26,79 @@ using nlohmann::json_editable_view;
#include <limits> #include <limits>
#include <map> #include <map>
#include <string> #include <string>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
// These tests were split off unit-json_view_edit.cpp, whose object file got too // These tests were split off unit-json_view_edit.cpp, whose object file got too
// large for the MinGW linker (see json_view_test_helpers.hpp). // large for the MinGW linker (see json_view_test_helpers.hpp).
namespace
{
// the three ways a function can accept views: editable ones, read-only ones, or both
template<typename BasicJsonType>
int accepts_editable(nlohmann::basic_json_view<BasicJsonType, nlohmann::view_mode::editable> /*unused*/)
{
return 1;
}
template<typename BasicJsonType>
int accepts_read_only(nlohmann::basic_json_view<BasicJsonType, nlohmann::view_mode::read_only> /*unused*/)
{
return 2;
}
template<typename BasicJsonType, nlohmann::view_mode Mode>
int accepts_any(nlohmann::basic_json_view<BasicJsonType, Mode> /*unused*/)
{
return Mode == nlohmann::view_mode::editable ? 3 : 4;
}
template<typename View>
auto overload(View v) -> decltype(accepts_editable(v))
{
return accepts_editable(v);
}
template<typename View>
auto overload(View v) -> decltype(accepts_read_only(v))
{
return accepts_read_only(v);
}
} // namespace
TEST_CASE("json_view modes")
{
using nlohmann::basic_json_document;
using nlohmann::basic_json_view;
using nlohmann::ordered_json;
using nlohmann::view_mode;
SECTION("read-only is the default, and the shortcuts name the modes")
{
static_assert(std::is_same<basic_json_view<json>, basic_json_view<json, view_mode::read_only>>::value, "default mode");
static_assert(std::is_same<basic_json_document<json>, basic_json_document<json, view_mode::read_only>>::value, "default mode");
static_assert(std::is_same<nlohmann::json_view, basic_json_view<json, view_mode::read_only>>::value, "json_view");
static_assert(std::is_same<json_editable_view, basic_json_view<json, view_mode::editable>>::value, "json_editable_view");
static_assert(std::is_same<json_editable_document, basic_json_document<json, view_mode::editable>>::value, "json_editable_document");
static_assert(std::is_same<nlohmann::ordered_json_editable_view, basic_json_view<ordered_json, view_mode::editable>>::value, "ordered_json_editable_view");
static_assert(std::is_same<json_editable_document::view_type, json_editable_view>::value, "view_type");
static_assert(std::is_same<json_document::view_type, nlohmann::json_view>::value, "view_type");
}
SECTION("a function template selects views by their mode")
{
const json_document d = json_document::parse("[1]");
const json_editable_document e = json_editable_document::parse("[1]");
const nlohmann::ordered_json_editable_document o = nlohmann::ordered_json_editable_document::parse("[1]");
CHECK(overload(e.root()) == 1);
CHECK(overload(o.root()) == 1);
CHECK(overload(d.root()) == 2);
CHECK(accepts_any(e.root()) == 3);
CHECK(accepts_any(d.root()) == 4);
}
}
TEST_CASE("json_view edits: views and values") TEST_CASE("json_view edits: views and values")
{ {
SECTION("a value that is no longer part of the document") SECTION("a value that is no longer part of the document")
@@ -100,7 +167,7 @@ TEST_CASE("json_view edits: views and values")
// doubles have their own path to the output; other float types are // doubles have their own path to the output; other float types are
// written as basic_json writes them, non-finite values as null // written as basic_json writes them, non-finite values as null
using json_float = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>; using json_float = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
using document_float = nlohmann::basic_json_document<json_float, true>; using document_float = nlohmann::basic_json_document<json_float, nlohmann::view_mode::editable>;
document_float d = document_float::parse("[1.5]"); document_float d = document_float::parse("[1.5]");
d.push_back(d.root(), std::numeric_limits<float>::quiet_NaN()); d.push_back(d.root(), std::numeric_limits<float>::quiet_NaN());
d.push_back(d.root(), -std::numeric_limits<float>::infinity()); d.push_back(d.root(), -std::numeric_limits<float>::infinity());
+9 -9
View File
@@ -480,7 +480,7 @@ TEST_CASE("json_view images: edited documents")
{ {
CAPTURE(i) CAPTURE(i)
CHECK((node_at(image, i).flags & nlohmann::detail::view::node_flags::linked) == 0); CHECK((node_at(image, i).flags & nlohmann::detail::view::node_flags::linked) == 0);
CHECK(node_at(image, i).kind != nlohmann::detail::view::kind_link); CHECK((node_at(image, i).kind != nlohmann::detail::view::node_kind::link));
} }
for (const image_check check : for (const image_check check :
{ {
@@ -810,13 +810,13 @@ TEST_CASE("json_view images: check")
{ {
rejected(corrupted(image, 12, [&](node & n) rejected(corrupted(image, 12, [&](node & n)
{ {
n.kind = kind; n.kind = static_cast<nlohmann::detail::view::node_kind>(kind);
}), true); }), true);
} }
// a key that is not a string // a key that is not a string
rejected(corrupted(image, 1, [](node & n) rejected(corrupted(image, 1, [](node & n)
{ {
n.kind = 0; n.kind = nlohmann::detail::view::node_kind::null;
n.len = 0; n.len = 0;
n.off = 0; n.off = 0;
}), true); }), true);
@@ -960,11 +960,11 @@ TEST_CASE("json_view images: check")
}), true); }), true);
rejected(corrupted(image, 0, [](node & n) rejected(corrupted(image, 0, [](node & n)
{ {
n.kind = 2; // an array: the "keys" are values, and the counts do not match n.kind = nlohmann::detail::view::node_kind::array; // an array: the "keys" are values, and the counts do not match
}), true); }), true);
const std::vector<std::uint8_t> as_array = corrupted(image, 16, [](node & n) const std::vector<std::uint8_t> as_array = corrupted(image, 16, [](node & n)
{ {
n.kind = 2; // {} as []: fine n.kind = nlohmann::detail::view::node_kind::array; // {} as []: fine
}); });
CHECK(load_result(as_array, image_check::full).empty()); CHECK(load_result(as_array, image_check::full).empty());
const std::string expected_dump = R"({"s":"x\"y","i":-12,"u":7,"f":1.5e+300,"b":true,"n":null,"a":["t",[]]})"; const std::string expected_dump = R"({"s":"x\"y","i":-12,"u":7,"f":1.5e+300,"b":true,"n":null,"a":["t",[]]})";
@@ -1013,13 +1013,13 @@ TEST_CASE("json_view images: check")
rejected(at_token(u6, 0, '8'), false); // 8 != 7 rejected(at_token(u6, 0, '8'), false); // 8 != 7
rejected(corrupted(image, 4, [](node & n) rejected(corrupted(image, 4, [](node & n)
{ {
n.kind = 6; // "-12" as unsigned: a sign n.kind = nlohmann::detail::view::node_kind::number_unsigned; // "-12" as unsigned: a sign
n.extra = 3; n.extra = 3;
}), false); }), false);
// a non-negative number_integer (as edits write it): fine // a non-negative number_integer (as edits write it): fine
const std::vector<std::uint8_t> positive = corrupted(image, 6, [](node & n) const std::vector<std::uint8_t> positive = corrupted(image, 6, [](node & n)
{ {
n.kind = 5; n.kind = nlohmann::detail::view::node_kind::number_integer;
n.extra = 0; n.extra = 0;
}); });
CHECK(load_result(positive, image_check::full).empty()); CHECK(load_result(positive, image_check::full).empty());
@@ -1027,7 +1027,7 @@ TEST_CASE("json_view images: check")
CHECK(pos.root()["u"].is_number_integer()); CHECK(pos.root()["u"].is_number_integer());
rejected(corrupted(image, 8, [](node & n) rejected(corrupted(image, 8, [](node & n)
{ {
n.kind = 6; // a float token as integer n.kind = nlohmann::detail::view::node_kind::number_unsigned; // a float token as integer
n.extra = 7; n.extra = 7;
}), false); }), false);
} }
@@ -1197,7 +1197,7 @@ TEST_CASE("json_view images: check")
{ {
std::vector<std::uint8_t> b = img; std::vector<std::uint8_t> b = img;
node n = node_at(b, i); node n = node_at(b, i);
n.kind = kind; n.kind = static_cast<nlohmann::detail::view::node_kind>(kind);
n.extra = extra; n.extra = extra;
set_node(b, i, n); set_node(b, i, n);
return load_result(b, image_check::full); return load_result(b, image_check::full);