mirror of
https://github.com/nlohmann/json.git
synced 2026-10-25 15:28:54 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bc2f7d09d |
No files matched your search
@@ -90,7 +90,6 @@ 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 view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
|
Only an **editable** document (`#!cpp Editable == true`, 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, view_mode Mode = view_mode::read_only>
|
template<typename BasicJsonType, bool Editable = false>
|
||||||
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 Mode == view_mode::editable`, the document also offers [`set`](set.md), [`push_back`](push_back.md),
|
With `#!cpp Editable == true`, 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 view_mode::read_only`, the default) does not carry any of the
|
itself is never written; a read-only document (`#!cpp Editable == false`, 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,10 +32,9 @@ 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`.
|
||||||
|
|
||||||
`Mode`
|
`Editable`
|
||||||
: a [`view_mode`](../view_mode.md): `#!cpp view_mode::editable` if the document supports [`set`](set.md),
|
: whether the document supports [`set`](set.md), [`push_back`](push_back.md), [`insert`](insert.md), and
|
||||||
[`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md) (optional, `#!cpp view_mode::read_only`
|
[`erase`](erase.md) (optional, `#!cpp false` by default). See [Edits](#edits) below.
|
||||||
by default). See [Edits](#edits) below.
|
|
||||||
|
|
||||||
## Specializations
|
## Specializations
|
||||||
|
|
||||||
@@ -48,7 +47,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, Mode>`)
|
- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType, Editable>`)
|
||||||
- **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)
|
||||||
|
|
||||||
@@ -81,15 +80,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 (editable documents only)
|
to (`#!cpp Editable` documents only)
|
||||||
- [**push_back**](push_back.md) - append to an array (editable documents only)
|
- [**push_back**](push_back.md) - append to an array (`#!cpp Editable` documents only)
|
||||||
- [**insert**](insert.md) - insert an element into an array before a given position (editable documents only)
|
- [**insert**](insert.md) - insert an element into an array before a given position (`#!cpp 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
|
||||||
(editable documents only)
|
(`#!cpp Editable` documents only)
|
||||||
|
|
||||||
## Edits
|
## Edits
|
||||||
|
|
||||||
An editable document (`#!cpp view_mode::editable`) can be changed after parsing, with [`set`](set.md),
|
An editable document (`#!cpp Editable == true`) 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 view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
|
Only an **editable** document (`#!cpp Editable == true`, 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
|
||||||
|
|||||||
@@ -55,7 +55,10 @@ validate with overload 2 first, if the original vector must survive a failed loa
|
|||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
Throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if `image` is not one
|
On a big-endian target, throws [`type_error.320`](../../home/exceptions.md#jsonexceptiontype_error320) -- the same
|
||||||
|
exception [`save()`](save.md#exceptions) throws there, since the image format is little-endian only.
|
||||||
|
|
||||||
|
Otherwise throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if `image` is not one
|
||||||
`save()` could have written, or fails the requested `check`:
|
`save()` could have written, or fails the requested `check`:
|
||||||
|
|
||||||
| message | when |
|
| message | when |
|
||||||
@@ -92,9 +95,6 @@ are checked once, however many nodes refer to it.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
**Little-endian targets only.** On a big-endian target, `load()` does not exist, just like
|
|
||||||
[`save()`](save.md#notes): the image format is little-endian, so a call fails to compile.
|
|
||||||
|
|
||||||
### `image_check` { #image_check }
|
### `image_check` { #image_check }
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -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 view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
|
Only an **editable** document (`#!cpp Editable == true`, 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 the [`view_mode`](../view_mode.md), since the image reflects document order and
|
the same bytes -- also across `BasicJsonType` and `#!cpp Editable`, 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
|
||||||
@@ -31,6 +31,9 @@ Throws [`type_error.320`](../../home/exceptions.md#jsonexceptiontype_error320) i
|
|||||||
[discarded](is_discarded.md) -- a default-constructed document, or one a failed [`parse()`](parse.md)/
|
[discarded](is_discarded.md) -- a default-constructed document, or one a failed [`parse()`](parse.md)/
|
||||||
[`read()`](read.md) with `allow_exceptions == false` left discarded.
|
[`read()`](read.md) with `allow_exceptions == false` left discarded.
|
||||||
|
|
||||||
|
On a big-endian target, throws `type_error.320` with a different message instead: the image format is little-endian
|
||||||
|
only (see [Notes](#notes)).
|
||||||
|
|
||||||
Throws [`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416) if the node count, the text, or
|
Throws [`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416) if the node count, the text, or
|
||||||
the decoded strings of the image would individually reach 4 GiB -- the same 32-bit offsets
|
the decoded strings of the image would individually reach 4 GiB -- the same 32-bit offsets
|
||||||
[`parse()`](parse.md#exceptions) and, for edits, [`set`](set.md)/[`push_back`](push_back.md) are already limited to.
|
[`parse()`](parse.md#exceptions) and, for edits, [`set`](set.md)/[`push_back`](push_back.md) are already limited to.
|
||||||
@@ -41,6 +44,9 @@ the decoded strings of the image would individually reach 4 GiB -- the same 32-b
|
|||||||
[json.exception.type_error.320] cannot save a discarded json_document
|
[json.exception.type_error.320] cannot save a discarded json_document
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
|
[json.exception.type_error.320] json_document images need a little-endian target
|
||||||
|
```
|
||||||
|
```
|
||||||
[json.exception.out_of_range.416] images of 4 GiB or more are not supported by json_document
|
[json.exception.out_of_range.416] images of 4 GiB or more are not supported by json_document
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -64,9 +70,9 @@ else -- see [`load`'s Exceptions](load.md#exceptions).
|
|||||||
the *same* build on the *same* (little-endian) machine -- not as a long-term storage format. Keep the original
|
the *same* build on the *same* (little-endian) machine -- not as a long-term storage format. Keep the original
|
||||||
JSON text if you need to read a saved document back with a future library version.
|
JSON text if you need to read a saved document back with a future library version.
|
||||||
|
|
||||||
**Little-endian targets only.** The image is written as raw little-endian bytes, with no byte-swapping. On a
|
**Little-endian only.** The image is written as raw little-endian bytes, with no byte-swapping. `save()` (and
|
||||||
big-endian target, `save()` and [`load`](load.md) do not exist: a call fails to compile rather than produce bytes a
|
[`load`](load.md)) throw `type_error.320` on a big-endian target rather than silently produce bytes a big-endian
|
||||||
big-endian reader could not interpret correctly.
|
reader could not interpret correctly.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -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 view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
|
Only an **editable** document (`#!cpp Editable == true`, 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`.
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ Linear in [`node_count()`](node_count.md) plus the length of the decoded strings
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
The node index of a small document (an estimate of up to 1024 nodes, which is up to about 4 KiB of minified text) is
|
||||||
|
allocated together with the document's header, so that parsing needs one allocation. That part cannot be released
|
||||||
|
separately: `shrink_to_fit()` only trims a node index that has its own allocation, and the decoded strings.
|
||||||
|
|
||||||
!!! warning "Invalidates views"
|
!!! warning "Invalidates views"
|
||||||
|
|
||||||
Unlike moving the document, `shrink_to_fit()` **invalidates every view taken from this document before the
|
Unlike moving the document, `shrink_to_fit()` **invalidates every view taken from this document before 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
|
||||||
template<typename BasicJsonType, view_mode Mode = view_mode::read_only>
|
template<typename BasicJsonType, bool Editable = false>
|
||||||
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 view_mode::editable`) sees every edit made through
|
**editable** document (`#!cpp Editable == true`) 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,10 +49,9 @@ 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.
|
||||||
|
|
||||||
`Mode`
|
`Editable`
|
||||||
: a [`view_mode`](../view_mode.md): whether the view is of an editable document, matching the
|
: whether the view is of an editable document, matching the [`basic_json_document`](../basic_json_document/index.md)
|
||||||
[`basic_json_document`](../basic_json_document/index.md) it was taken from (optional, `#!cpp view_mode::read_only` by
|
it was taken from (optional, `#!cpp false` by default). See [Edits](../basic_json_document/index.md#edits).
|
||||||
default). See [Edits](../basic_json_document/index.md#edits).
|
|
||||||
|
|
||||||
## Specializations
|
## Specializations
|
||||||
|
|
||||||
|
|||||||
@@ -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, view_mode::editable>;
|
using json_editable_document = basic_json_document<json, true>;
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -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, view_mode::editable>;
|
using json_editable_view = basic_json_view<json, true>;
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -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, view_mode::editable>;
|
using ordered_json_editable_document = basic_json_document<ordered_json, true>;
|
||||||
```
|
```
|
||||||
|
|
||||||
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, view_mode::editable>;
|
using ordered_json_editable_view = basic_json_view<ordered_json, true>;
|
||||||
```
|
```
|
||||||
|
|
||||||
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,80 +0,0 @@
|
|||||||
# <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 part of the view's type, 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, nlohmann::view_mode::editable>
|
// ordered_json_editable_document is basic_json_document<nlohmann::ordered_json, true>
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
#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';
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
2 1
|
|
||||||
true
|
|
||||||
@@ -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, view_mode::editable>`](../api/basic_json_document/index.md) -- more conveniently
|
not change it. [`basic_json_document<BasicJsonType, true>`](../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.
|
||||||
|
|
||||||
The [mode](../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so `json_document`/`ordered_json_document` are unaffected -- they carry
|
`#!cpp Editable` defaults to `#!cpp false`, 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.
|
||||||
|
|
||||||
@@ -354,10 +354,9 @@ validation entirely, for an image trusted as much as the process's own memory. S
|
|||||||
!!! warning "Experimental"
|
!!! warning "Experimental"
|
||||||
|
|
||||||
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
||||||
stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the
|
stable. It is little-endian only, and tied to the library build that wrote it -- use it to cache a document or to
|
||||||
library build that wrote it -- use it to cache a document or to hand one to another process running the *same*
|
hand one to another process running the *same* build, not as a long-term storage format; keep the original JSON
|
||||||
build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by
|
text if a saved document needs to be readable by a future library version.
|
||||||
a future library version.
|
|
||||||
|
|
||||||
The idea of a document you can read without parsing comes from zero-copy formats such as
|
The idea of a document you can read without parsing comes from zero-copy formats such as
|
||||||
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
|
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
|
||||||
|
|||||||
@@ -853,12 +853,17 @@ from JSON text.
|
|||||||
|
|
||||||
[`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an
|
[`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an
|
||||||
[image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document.
|
[image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document.
|
||||||
|
[`save()`](../api/basic_json_document/save.md) and [`load()`](../api/basic_json_document/load.md) also throw this
|
||||||
|
exception on a big-endian target, since the image format is little-endian only.
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
```
|
```
|
||||||
[json.exception.type_error.320] cannot save a discarded json_document
|
[json.exception.type_error.320] cannot save a discarded json_document
|
||||||
```
|
```
|
||||||
|
```
|
||||||
|
[json.exception.type_error.320] json_document images need a little-endian target
|
||||||
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
|
|||||||
@@ -407,7 +407,6 @@ 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
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ enum class exception_id : int
|
|||||||
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
|
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
|
||||||
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
|
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
|
||||||
document_binary_value = 319, ///< a binary value is stored in a json_document
|
document_binary_value = 319, ///< a binary value is stored in a json_document
|
||||||
document_image_unsupported = 320, ///< a discarded json_document cannot be saved as an image
|
document_image_unsupported = 320, ///< a json_document image cannot be saved or loaded (discarded document, big-endian target)
|
||||||
discarded_value_used = 321, ///< a discarded value is used
|
discarded_value_used = 321, ///< a discarded value is used
|
||||||
// out_of_range
|
// out_of_range
|
||||||
array_index_out_of_range = 401, ///< an array index is out of range
|
array_index_out_of_range = 401, ///< an array index is out of range
|
||||||
|
|||||||
@@ -543,7 +543,15 @@ run_found:
|
|||||||
NLOHMANN_VIEW_NOINLINE void arena_grow(std::size_t n)
|
NLOHMANN_VIEW_NOINLINE void arena_grow(std::size_t n)
|
||||||
{
|
{
|
||||||
const std::size_t used = arena_used();
|
const std::size_t used = arena_used();
|
||||||
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, used + n + 256));
|
std::size_t wanted = used + n + 256;
|
||||||
|
if (m_doc.arena.empty())
|
||||||
|
{
|
||||||
|
// the first growth: a decoded string is never longer than its source,
|
||||||
|
// so a fraction of the input (capped, as the buffer is zero-filled)
|
||||||
|
// covers the escapes of a typical document without regrowing it
|
||||||
|
wanted = (std::max)(wanted, (std::min)(static_cast<std::size_t>(m_end - m_begin) / 4 + 16, std::size_t{4096}));
|
||||||
|
}
|
||||||
|
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, wanted));
|
||||||
m_arena_write = m_doc.arena.data() + used;
|
m_arena_write = m_doc.arena.data() + used;
|
||||||
m_arena_end = m_doc.arena.data() + m_doc.arena.size();
|
m_arena_end = m_doc.arena.data() + m_doc.arena.size();
|
||||||
}
|
}
|
||||||
@@ -978,7 +986,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<node_kind>(k);
|
n->kind = static_cast<std::uint8_t>(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);
|
||||||
|
|||||||
@@ -82,14 +82,17 @@ struct document_data
|
|||||||
};
|
};
|
||||||
std::unique_ptr<edit_state> edits; ///< created by the first edit
|
std::unique_ptr<edit_state> edits; ///< created by the first edit
|
||||||
|
|
||||||
|
/// the most nodes that are allocated together with the header (16 KiB)
|
||||||
|
static constexpr std::size_t max_inline_nodes = 1024;
|
||||||
|
|
||||||
/// One allocation for the header and room for `count` nodes, so that a
|
/// One allocation for the header and room for `count` nodes, so that a
|
||||||
/// small document needs one allocation instead of two. Large documents get
|
/// small document needs one allocation instead of two. Large documents get
|
||||||
/// a separate node array instead (so it can be trimmed). When a document
|
/// a separate node array instead (so it can be trimmed). When a document
|
||||||
/// outgrows the inline array, that array stays unused: at most 256 nodes
|
/// outgrows the inline array, that array stays unused: at most 1024 nodes
|
||||||
/// (4 KiB), and only for documents whose estimate was too small.
|
/// (16 KiB), and only for documents whose estimate was too small.
|
||||||
static document_data* create(std::size_t count)
|
static document_data* create(std::size_t count)
|
||||||
{
|
{
|
||||||
count = count <= 256 ? count : 0;
|
count = count <= max_inline_nodes ? count : 0;
|
||||||
void* mem = ::operator new (sizeof(document_data) + (count * sizeof(node)));
|
void* mem = ::operator new (sizeof(document_data) + (count * sizeof(node)));
|
||||||
auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter
|
auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter
|
||||||
// (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants)
|
// (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants)
|
||||||
@@ -232,7 +235,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 != node_kind::link) ? n : follow_link(n);
|
return NLOHMANN_VIEW_LIKELY(n->kind != 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
|
||||||
|
|||||||
@@ -26,10 +26,12 @@
|
|||||||
#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
|
||||||
@@ -118,13 +120,13 @@ 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 != node_kind::object && o->kind != node_kind::null)
|
if (o->kind != static_cast<std::uint8_t>(value_t::object) && o->kind != static_cast<std::uint8_t>(value_t::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 == node_kind::null)
|
if (o->kind == static_cast<std::uint8_t>(value_t::null))
|
||||||
{
|
{
|
||||||
become_empty(o, value_t::object);
|
become_empty(o, value_t::object);
|
||||||
}
|
}
|
||||||
@@ -169,7 +171,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 != node_kind::array)
|
if (a->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -186,12 +188,12 @@ 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 != node_kind::array && a->kind != node_kind::null)
|
if (a->kind != static_cast<std::uint8_t>(value_t::array) && a->kind != static_cast<std::uint8_t>(value_t::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 == node_kind::null)
|
if (a->kind == static_cast<std::uint8_t>(value_t::null))
|
||||||
{
|
{
|
||||||
become_empty(a, value_t::array);
|
become_empty(a, value_t::array);
|
||||||
}
|
}
|
||||||
@@ -210,7 +212,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 != node_kind::array)
|
if (a->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -230,7 +232,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 != node_kind::object)
|
if (o->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -248,7 +250,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 != node_kind::array)
|
if (a->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -326,7 +328,7 @@ class editor
|
|||||||
{
|
{
|
||||||
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<node_kind>(k);
|
n->kind = static_cast<std::uint8_t>(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;
|
||||||
}
|
}
|
||||||
@@ -403,8 +405,8 @@ class editor
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
struct is_view : std::false_type {};
|
struct is_view : std::false_type {};
|
||||||
|
|
||||||
template<typename J, view_mode M>
|
template<typename J, bool E>
|
||||||
struct is_view<basic_json_view<J, M>> : std::true_type {};
|
struct is_view<basic_json_view<J, E>> : std::true_type {};
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
encoded encode(V&& v)
|
encoded encode(V&& v)
|
||||||
@@ -419,8 +421,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, view_mode M>
|
template<typename J, bool E>
|
||||||
encoded encode_impl(const basic_json_view<J, M>& v, encode_tag<0> /*view*/)
|
encoded encode_impl(const basic_json_view<J, E>& v, encode_tag<0> /*view*/)
|
||||||
{
|
{
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
|
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
|
||||||
{
|
{
|
||||||
@@ -432,8 +434,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<M == view_mode::editable>(*v.m_doc, v.m_node));
|
r.region = alloc_nodes(m_doc, count_nodes<E>(*v.m_doc, v.m_node));
|
||||||
fill_nodes<M == view_mode::editable>(*v.m_doc, v.m_node, r.region);
|
fill_nodes<E>(*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;
|
||||||
}
|
}
|
||||||
@@ -494,7 +496,7 @@ class editor
|
|||||||
static node plain_node(value_t k) noexcept
|
static node plain_node(value_t k) noexcept
|
||||||
{
|
{
|
||||||
node n{};
|
node n{};
|
||||||
n.kind = static_cast<node_kind>(k);
|
n.kind = static_cast<std::uint8_t>(k);
|
||||||
n.flags = node_flags::is_new;
|
n.flags = node_flags::is_new;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@@ -679,7 +681,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 == node_kind::object});
|
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::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,7 +730,7 @@ class editor
|
|||||||
node* const self = out++;
|
node* const self = out++;
|
||||||
*self = plain_node(static_cast<value_t>(n->kind));
|
*self = plain_node(static_cast<value_t>(n->kind));
|
||||||
self->len = n->len;
|
self->len = n->len;
|
||||||
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object, self});
|
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::object), self});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 (node_kind::link).
|
// node, then the entries), whose entries link to the values (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 == node_kind::object;
|
const bool object = n->kind == static_cast<std::uint8_t>(value_t::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 == node_kind::object;
|
const bool object = c->kind == static_cast<std::uint8_t>(value_t::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;)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 == node_kind::object});
|
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)});
|
||||||
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 = node_kind::null;
|
r.kind = static_cast<std::uint8_t>(value_t::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());
|
||||||
@@ -182,10 +182,12 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NLOHMANN_VIEW_LITTLE_ENDIAN // (the image format is little-endian: no save() elsewhere)
|
|
||||||
/// the document as an image
|
/// the document as an image
|
||||||
inline std::vector<std::uint8_t> save_image(const document_data& d)
|
inline std::vector<std::uint8_t> save_image(const document_data& d)
|
||||||
{
|
{
|
||||||
|
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
|
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||||
|
#endif
|
||||||
const std::size_t arena_size = d.arena_size;
|
const std::size_t arena_size = d.arena_size;
|
||||||
std::vector<node> compacted;
|
std::vector<node> compacted;
|
||||||
std::string text_tail;
|
std::string text_tail;
|
||||||
@@ -219,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 == node_kind::object && nodes[i].extra != 0)
|
if (nodes[i].kind == static_cast<std::uint8_t>(value_t::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));
|
||||||
}
|
}
|
||||||
@@ -241,7 +243,6 @@ inline std::vector<std::uint8_t> save_image(const document_data& d)
|
|||||||
*o = 0;
|
*o = 0;
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/// the parts of a number token that the checks need
|
/// the parts of a number token that the checks need
|
||||||
struct number_token
|
struct number_token
|
||||||
@@ -336,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 = node_kind::number_float;
|
n.kind = static_cast<std::uint8_t>(value_t::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)();
|
||||||
@@ -357,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 == node_kind::number_integer;
|
const bool integer = n.kind == static_cast<std::uint8_t>(value_t::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;
|
||||||
@@ -539,7 +540,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (n.kind != node_kind::number_float)
|
if (n.kind != static_cast<std::uint8_t>(value_t::number_float))
|
||||||
{
|
{
|
||||||
return (n.extra >> 8u) == 0;
|
return (n.extra >> 8u) == 0;
|
||||||
}
|
}
|
||||||
@@ -580,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 != node_kind::string || !check_string(n))
|
if (n.kind != static_cast<std::uint8_t>(value_t::string) || !check_string(n))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -621,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 == node_kind::number_float)
|
if (n.kind == static_cast<std::uint8_t>(value_t::number_float))
|
||||||
{
|
{
|
||||||
floats.push_back(float_range{n.off, n.len, n.extra});
|
floats.push_back(float_range{n.off, n.len, n.extra});
|
||||||
}
|
}
|
||||||
@@ -639,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 == node_kind::object, true});
|
stack.push_back(frame{i + n.next, n.len, 0, n.kind == static_cast<std::uint8_t>(value_t::object), true});
|
||||||
complete = false;
|
complete = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -662,11 +663,13 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
|||||||
throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what));
|
throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NLOHMANN_VIEW_LITTLE_ENDIAN // (the image format is little-endian: no load() elsewhere)
|
|
||||||
/// Read an image into d. The text and the decoded strings stay in the image;
|
/// Read an image into d. The text and the decoded strings stay in the image;
|
||||||
/// the nodes are copied (so that they are aligned, and edits can change them).
|
/// the nodes are copied (so that they are aligned, and edits can change them).
|
||||||
inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check)
|
inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check)
|
||||||
{
|
{
|
||||||
|
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
|
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||||
|
#endif
|
||||||
if (image == nullptr || size < sizeof(image_header))
|
if (image == nullptr || size < sizeof(image_header))
|
||||||
{
|
{
|
||||||
throw_invalid_image("too short");
|
throw_invalid_image("too short");
|
||||||
@@ -726,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 == node_kind::object && n.len >= document_data::index_min_members)
|
if (n.kind == static_cast<std::uint8_t>(value_t::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));
|
||||||
}
|
}
|
||||||
@@ -735,7 +738,6 @@ inline void load_image(document_data& d, const std::uint8_t* image, std::size_t
|
|||||||
std::vector<std::uint32_t>().swap(d.large_objects); // (only needed while building)
|
std::vector<std::uint32_t>().swap(d.large_objects); // (only needed while building)
|
||||||
d.discarded = false;
|
d.discarded = false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
@@ -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 == node_kind::object ? 1 : 0;
|
const std::size_t value_offset = container->kind == static_cast<std::uint8_t>(value_t::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))
|
||||||
|
|||||||
@@ -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 == node_kind::object;
|
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object);
|
||||||
if (object)
|
if (object)
|
||||||
{
|
{
|
||||||
sax.start_object(n->len);
|
sax.start_object(n->len);
|
||||||
|
|||||||
@@ -41,21 +41,9 @@ 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
|
||||||
};
|
};
|
||||||
|
|
||||||
/// the kind of a node: the value_t of its value, or link
|
/// kind of an entry of an edited sequence that stands for a value stored
|
||||||
enum class node_kind : std::uint8_t
|
/// elsewhere (the address of the value's node is kept in the len/next bytes)
|
||||||
{
|
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),
|
|
||||||
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)
|
|
||||||
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
|
||||||
@@ -63,7 +51,7 @@ enum class node_kind : std::uint8_t
|
|||||||
/// 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
|
||||||
{
|
{
|
||||||
node_kind kind; ///< value_t, or link
|
std::uint8_t kind; ///< value_t, or kind_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
|
||||||
@@ -72,9 +60,8 @@ 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, and images store them as bytes; the test
|
// the node kinds are value_t values; the test below (object and array are 1
|
||||||
// below (object and array are 1 and 2) and those of the number kinds depend
|
// and 2) and those of the number kinds depend on this numbering
|
||||||
// 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>(value_t::null) == 0 && static_cast<std::uint8_t>(value_t::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>(value_t::array) == 2 && static_cast<std::uint8_t>(value_t::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>(value_t::boolean) == 4 && static_cast<std::uint8_t>(value_t::number_integer) == 5
|
||||||
@@ -99,7 +86,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 = node_kind::link;
|
n.kind = 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +116,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 == node_kind::number_float ? n.len
|
return n.kind == static_cast<std::uint8_t>(value_t::number_float) ? n.len
|
||||||
: (n.extra & 0xFFu) + (n.kind == node_kind::number_integer ? 1u : 0u);
|
: (n.extra & 0xFFu) + (n.kind == static_cast<std::uint8_t>(value_t::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
|
||||||
|
|||||||
@@ -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 == node_kind::object;
|
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object);
|
||||||
room(8);
|
room(8);
|
||||||
if (n->len == 0)
|
if (n->len == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | 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
|
|
||||||
@@ -84,7 +84,6 @@
|
|||||||
#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
|
||||||
|
|
||||||
@@ -94,16 +93,13 @@ 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, view_mode Mode = view_mode::read_only>
|
template<typename BasicJsonType, bool Editable = false>
|
||||||
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<is_editable>;
|
using navigation = detail::view::navigation<Editable>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_t = detail::value_t;
|
using value_t = detail::value_t;
|
||||||
@@ -450,7 +446,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<is_editable>(*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)));
|
||||||
}
|
}
|
||||||
return front();
|
return front();
|
||||||
}
|
}
|
||||||
@@ -508,7 +504,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<is_editable>(*m_doc, m_node, key.data(), key.size()) : 0;
|
return is_object() ? detail::view::count_members<Editable>(*m_doc, m_node, key.data(), key.size()) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_type count(const char* key) const
|
size_type count(const char* key) const
|
||||||
@@ -650,7 +646,7 @@ class basic_json_view
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::view::view_serializer<BasicJsonType, is_editable>(*m_doc, out, estimate, style).dump(m_node);
|
detail::view::view_serializer<BasicJsonType, Editable>(*m_doc, out, estimate, style).dump(m_node);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -686,14 +682,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 < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
|
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 >
|
||||||
friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
|
friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b)
|
||||||
{
|
{
|
||||||
return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, M>>(b));
|
return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, E>>(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
template < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
|
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 >
|
||||||
friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
|
friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b)
|
||||||
{
|
{
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
@@ -731,7 +727,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, is_editable>(*m_doc, m_node);
|
return unedited() ? detail::view::materialize<BasicJsonType, false>(*m_doc, m_node) : detail::view::materialize<BasicJsonType, 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
|
||||||
@@ -744,8 +740,8 @@ class basic_json_view
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename, view_mode> friend class basic_json_document;
|
template<typename, bool> friend class basic_json_document;
|
||||||
template<typename, view_mode> friend class basic_json_view;
|
template<typename, bool> 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;
|
||||||
|
|
||||||
@@ -761,11 +757,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
|
||||||
/// `is_editable && ...` makes the condition constant, which MSVC reports as
|
/// `Editable && ...` makes the condition constant, which MSVC reports as
|
||||||
/// C4127
|
/// C4127
|
||||||
static bool editable() noexcept
|
static bool editable() noexcept
|
||||||
{
|
{
|
||||||
return is_editable;
|
return 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
|
||||||
@@ -814,7 +810,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<is_editable>(*m_doc, m_node, key.data(), key.size());
|
const node* const k = detail::view::find_member<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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -828,7 +824,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, is_editable> {});
|
return find_key(key, std::integral_constant<bool, 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
|
||||||
@@ -850,7 +846,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<is_editable>(*m_doc, m_node, idx)));
|
return basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- get() dispatch ---
|
// --- get() dispatch ---
|
||||||
@@ -956,19 +952,16 @@ 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, view_mode Mode = view_mode::read_only>
|
template<typename BasicJsonType, bool Editable = false>
|
||||||
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, Mode>;
|
using view_type = basic_json_view<BasicJsonType, Editable>;
|
||||||
using value_t = detail::value_t;
|
using value_t = detail::value_t;
|
||||||
|
|
||||||
/// an empty (discarded) document
|
/// an empty (discarded) document
|
||||||
@@ -1182,10 +1175,6 @@ class basic_json_document
|
|||||||
/// how load() checks an image (full, bounds, or none)
|
/// how load() checks an image (full, bounds, or none)
|
||||||
using image_check = detail::view::image_check;
|
using image_check = detail::view::image_check;
|
||||||
|
|
||||||
#if NLOHMANN_VIEW_LITTLE_ENDIAN
|
|
||||||
// (the image format is little-endian: big-endian targets have no save()
|
|
||||||
// and load(), so that a call fails to compile instead of always throwing)
|
|
||||||
|
|
||||||
/// The document as an image that load() reads without parsing: the node
|
/// The document as an image that load() reads without parsing: the node
|
||||||
/// index, the text, and the decoded strings. An edited document is
|
/// index, the text, and the decoded strings. An edited document is
|
||||||
/// written in its current state (floats that are not finite become null,
|
/// written in its current state (floats that are not finite become null,
|
||||||
@@ -1227,7 +1216,6 @@ class basic_json_document
|
|||||||
detail::view::load_image(*d.m_data, d.m_data->owned_image.data(), d.m_data->owned_image.size(), check);
|
detail::view::load_image(*d.m_data, d.m_data->owned_image.data(), d.m_data->owned_image.size(), check);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// edits //
|
// edits //
|
||||||
@@ -1369,7 +1357,7 @@ class basic_json_document
|
|||||||
|
|
||||||
detail::view::editor<BasicJsonType, view_type> editor()
|
detail::view::editor<BasicJsonType, view_type> editor()
|
||||||
{
|
{
|
||||||
static_assert(is_editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, view_mode::editable> (json_editable_document)");
|
static_assert(Editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, true> (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");
|
||||||
@@ -1654,13 +1642,13 @@ using ordered_json_document = basic_json_document<ordered_json>;
|
|||||||
/// 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>;
|
||||||
/// an editable parsed JSON text for json
|
/// an editable parsed JSON text for json
|
||||||
using json_editable_document = basic_json_document<json, view_mode::editable>;
|
using json_editable_document = basic_json_document<json, true>;
|
||||||
/// a value of a json_editable_document
|
/// a value of a json_editable_document
|
||||||
using json_editable_view = basic_json_view<json, view_mode::editable>;
|
using json_editable_view = basic_json_view<json, true>;
|
||||||
/// 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, view_mode::editable>;
|
using ordered_json_editable_document = basic_json_document<ordered_json, true>;
|
||||||
/// 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, view_mode::editable>;
|
using ordered_json_editable_view = basic_json_view<ordered_json, true>;
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
|||||||
Generated
+1
-1
@@ -5565,7 +5565,7 @@ enum class exception_id : int
|
|||||||
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
|
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
|
||||||
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
|
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
|
||||||
document_binary_value = 319, ///< a binary value is stored in a json_document
|
document_binary_value = 319, ///< a binary value is stored in a json_document
|
||||||
document_image_unsupported = 320, ///< a discarded json_document cannot be saved as an image
|
document_image_unsupported = 320, ///< a json_document image cannot be saved or loaded (discarded document, big-endian target)
|
||||||
discarded_value_used = 321, ///< a discarded value is used
|
discarded_value_used = 321, ///< a discarded value is used
|
||||||
// out_of_range
|
// out_of_range
|
||||||
array_index_out_of_range = 401, ///< an array index is out of range
|
array_index_out_of_range = 401, ///< an array index is out of range
|
||||||
|
|||||||
Generated
+92
-131
@@ -239,21 +239,9 @@ 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
|
||||||
};
|
};
|
||||||
|
|
||||||
/// the kind of a node: the value_t of its value, or link
|
/// kind of an entry of an edited sequence that stands for a value stored
|
||||||
enum class node_kind : std::uint8_t
|
/// elsewhere (the address of the value's node is kept in the len/next bytes)
|
||||||
{
|
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),
|
|
||||||
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)
|
|
||||||
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
|
||||||
@@ -261,7 +249,7 @@ enum class node_kind : std::uint8_t
|
|||||||
/// 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
|
||||||
{
|
{
|
||||||
node_kind kind; ///< value_t, or link
|
std::uint8_t kind; ///< value_t, or kind_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
|
||||||
@@ -270,9 +258,8 @@ 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, and images store them as bytes; the test
|
// the node kinds are value_t values; the test below (object and array are 1
|
||||||
// below (object and array are 1 and 2) and those of the number kinds depend
|
// and 2) and those of the number kinds depend on this numbering
|
||||||
// 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>(value_t::null) == 0 && static_cast<std::uint8_t>(value_t::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>(value_t::array) == 2 && static_cast<std::uint8_t>(value_t::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>(value_t::boolean) == 4 && static_cast<std::uint8_t>(value_t::number_integer) == 5
|
||||||
@@ -297,7 +284,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 = node_kind::link;
|
n.kind = 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,8 +314,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 == node_kind::number_float ? n.len
|
return n.kind == static_cast<std::uint8_t>(value_t::number_float) ? n.len
|
||||||
: (n.extra & 0xFFu) + (n.kind == node_kind::number_integer ? 1u : 0u);
|
: (n.extra & 0xFFu) + (n.kind == static_cast<std::uint8_t>(value_t::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
|
||||||
@@ -410,14 +397,17 @@ struct document_data
|
|||||||
};
|
};
|
||||||
std::unique_ptr<edit_state> edits; ///< created by the first edit
|
std::unique_ptr<edit_state> edits; ///< created by the first edit
|
||||||
|
|
||||||
|
/// the most nodes that are allocated together with the header (16 KiB)
|
||||||
|
static constexpr std::size_t max_inline_nodes = 1024;
|
||||||
|
|
||||||
/// One allocation for the header and room for `count` nodes, so that a
|
/// One allocation for the header and room for `count` nodes, so that a
|
||||||
/// small document needs one allocation instead of two. Large documents get
|
/// small document needs one allocation instead of two. Large documents get
|
||||||
/// a separate node array instead (so it can be trimmed). When a document
|
/// a separate node array instead (so it can be trimmed). When a document
|
||||||
/// outgrows the inline array, that array stays unused: at most 256 nodes
|
/// outgrows the inline array, that array stays unused: at most 1024 nodes
|
||||||
/// (4 KiB), and only for documents whose estimate was too small.
|
/// (16 KiB), and only for documents whose estimate was too small.
|
||||||
static document_data* create(std::size_t count)
|
static document_data* create(std::size_t count)
|
||||||
{
|
{
|
||||||
count = count <= 256 ? count : 0;
|
count = count <= max_inline_nodes ? count : 0;
|
||||||
void* mem = ::operator new (sizeof(document_data) + (count * sizeof(node)));
|
void* mem = ::operator new (sizeof(document_data) + (count * sizeof(node)));
|
||||||
auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter
|
auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter
|
||||||
// (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants)
|
// (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants)
|
||||||
@@ -560,7 +550,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 != node_kind::link) ? n : follow_link(n);
|
return NLOHMANN_VIEW_LIKELY(n->kind != 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
|
||||||
@@ -1747,7 +1737,15 @@ run_found:
|
|||||||
NLOHMANN_VIEW_NOINLINE void arena_grow(std::size_t n)
|
NLOHMANN_VIEW_NOINLINE void arena_grow(std::size_t n)
|
||||||
{
|
{
|
||||||
const std::size_t used = arena_used();
|
const std::size_t used = arena_used();
|
||||||
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, used + n + 256));
|
std::size_t wanted = used + n + 256;
|
||||||
|
if (m_doc.arena.empty())
|
||||||
|
{
|
||||||
|
// the first growth: a decoded string is never longer than its source,
|
||||||
|
// so a fraction of the input (capped, as the buffer is zero-filled)
|
||||||
|
// covers the escapes of a typical document without regrowing it
|
||||||
|
wanted = (std::max)(wanted, (std::min)(static_cast<std::size_t>(m_end - m_begin) / 4 + 16, std::size_t{4096}));
|
||||||
|
}
|
||||||
|
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, wanted));
|
||||||
m_arena_write = m_doc.arena.data() + used;
|
m_arena_write = m_doc.arena.data() + used;
|
||||||
m_arena_end = m_doc.arena.data() + m_doc.arena.size();
|
m_arena_end = m_doc.arena.data() + m_doc.arena.size();
|
||||||
}
|
}
|
||||||
@@ -2182,7 +2180,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<node_kind>(k);
|
n->kind = static_cast<std::uint8_t>(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);
|
||||||
@@ -3014,7 +3012,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 (node_kind::link).
|
// node, then the entries), whose entries link to the values (kind_link).
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -3172,7 +3170,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 == node_kind::object;
|
const bool object = n->kind == static_cast<std::uint8_t>(value_t::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);
|
||||||
@@ -3238,7 +3236,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 == node_kind::object;
|
const bool object = c->kind == static_cast<std::uint8_t>(value_t::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;)
|
||||||
{
|
{
|
||||||
@@ -3705,7 +3703,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 == node_kind::object ? 1 : 0;
|
const std::size_t value_offset = container->kind == static_cast<std::uint8_t>(value_t::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))
|
||||||
@@ -3727,38 +3725,12 @@ 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
|
||||||
|
|
||||||
/// whether a basic_json_document and its views can be edited
|
template<typename BasicJsonType, bool Editable>
|
||||||
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
|
||||||
@@ -3847,13 +3819,13 @@ 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 != node_kind::object && o->kind != node_kind::null)
|
if (o->kind != static_cast<std::uint8_t>(value_t::object) && o->kind != static_cast<std::uint8_t>(value_t::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 == node_kind::null)
|
if (o->kind == static_cast<std::uint8_t>(value_t::null))
|
||||||
{
|
{
|
||||||
become_empty(o, value_t::object);
|
become_empty(o, value_t::object);
|
||||||
}
|
}
|
||||||
@@ -3898,7 +3870,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 != node_kind::array)
|
if (a->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -3915,12 +3887,12 @@ 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 != node_kind::array && a->kind != node_kind::null)
|
if (a->kind != static_cast<std::uint8_t>(value_t::array) && a->kind != static_cast<std::uint8_t>(value_t::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 == node_kind::null)
|
if (a->kind == static_cast<std::uint8_t>(value_t::null))
|
||||||
{
|
{
|
||||||
become_empty(a, value_t::array);
|
become_empty(a, value_t::array);
|
||||||
}
|
}
|
||||||
@@ -3939,7 +3911,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 != node_kind::array)
|
if (a->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -3959,7 +3931,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 != node_kind::object)
|
if (o->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -3977,7 +3949,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 != node_kind::array)
|
if (a->kind != static_cast<std::uint8_t>(value_t::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());
|
||||||
}
|
}
|
||||||
@@ -4055,7 +4027,7 @@ class editor
|
|||||||
{
|
{
|
||||||
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<node_kind>(k);
|
n->kind = static_cast<std::uint8_t>(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;
|
||||||
}
|
}
|
||||||
@@ -4132,8 +4104,8 @@ class editor
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
struct is_view : std::false_type {};
|
struct is_view : std::false_type {};
|
||||||
|
|
||||||
template<typename J, view_mode M>
|
template<typename J, bool E>
|
||||||
struct is_view<basic_json_view<J, M>> : std::true_type {};
|
struct is_view<basic_json_view<J, E>> : std::true_type {};
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
encoded encode(V&& v)
|
encoded encode(V&& v)
|
||||||
@@ -4148,8 +4120,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, view_mode M>
|
template<typename J, bool E>
|
||||||
encoded encode_impl(const basic_json_view<J, M>& v, encode_tag<0> /*view*/)
|
encoded encode_impl(const basic_json_view<J, E>& v, encode_tag<0> /*view*/)
|
||||||
{
|
{
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
|
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
|
||||||
{
|
{
|
||||||
@@ -4161,8 +4133,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<M == view_mode::editable>(*v.m_doc, v.m_node));
|
r.region = alloc_nodes(m_doc, count_nodes<E>(*v.m_doc, v.m_node));
|
||||||
fill_nodes<M == view_mode::editable>(*v.m_doc, v.m_node, r.region);
|
fill_nodes<E>(*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;
|
||||||
}
|
}
|
||||||
@@ -4223,7 +4195,7 @@ class editor
|
|||||||
static node plain_node(value_t k) noexcept
|
static node plain_node(value_t k) noexcept
|
||||||
{
|
{
|
||||||
node n{};
|
node n{};
|
||||||
n.kind = static_cast<node_kind>(k);
|
n.kind = static_cast<std::uint8_t>(k);
|
||||||
n.flags = node_flags::is_new;
|
n.flags = node_flags::is_new;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@@ -4408,7 +4380,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 == node_kind::object});
|
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::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 (;;)
|
||||||
@@ -4457,7 +4429,7 @@ class editor
|
|||||||
node* const self = out++;
|
node* const self = out++;
|
||||||
*self = plain_node(static_cast<value_t>(n->kind));
|
*self = plain_node(static_cast<value_t>(n->kind));
|
||||||
self->len = n->len;
|
self->len = n->len;
|
||||||
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object, self});
|
open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast<std::uint8_t>(value_t::object), self});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -4942,7 +4914,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 == node_kind::object});
|
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)});
|
||||||
break;
|
break;
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
r = string_node(*v);
|
r = string_node(*v);
|
||||||
@@ -4963,7 +4935,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 = node_kind::null;
|
r.kind = static_cast<std::uint8_t>(value_t::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,10 +4979,12 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NLOHMANN_VIEW_LITTLE_ENDIAN // (the image format is little-endian: no save() elsewhere)
|
|
||||||
/// the document as an image
|
/// the document as an image
|
||||||
inline std::vector<std::uint8_t> save_image(const document_data& d)
|
inline std::vector<std::uint8_t> save_image(const document_data& d)
|
||||||
{
|
{
|
||||||
|
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
|
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||||
|
#endif
|
||||||
const std::size_t arena_size = d.arena_size;
|
const std::size_t arena_size = d.arena_size;
|
||||||
std::vector<node> compacted;
|
std::vector<node> compacted;
|
||||||
std::string text_tail;
|
std::string text_tail;
|
||||||
@@ -5044,7 +5018,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 == node_kind::object && nodes[i].extra != 0)
|
if (nodes[i].kind == static_cast<std::uint8_t>(value_t::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));
|
||||||
}
|
}
|
||||||
@@ -5066,7 +5040,6 @@ inline std::vector<std::uint8_t> save_image(const document_data& d)
|
|||||||
*o = 0;
|
*o = 0;
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/// the parts of a number token that the checks need
|
/// the parts of a number token that the checks need
|
||||||
struct number_token
|
struct number_token
|
||||||
@@ -5161,7 +5134,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 = node_kind::number_float;
|
n.kind = static_cast<std::uint8_t>(value_t::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)();
|
||||||
@@ -5182,7 +5155,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 == node_kind::number_integer;
|
const bool integer = n.kind == static_cast<std::uint8_t>(value_t::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;
|
||||||
@@ -5364,7 +5337,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (n.kind != node_kind::number_float)
|
if (n.kind != static_cast<std::uint8_t>(value_t::number_float))
|
||||||
{
|
{
|
||||||
return (n.extra >> 8u) == 0;
|
return (n.extra >> 8u) == 0;
|
||||||
}
|
}
|
||||||
@@ -5405,7 +5378,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 != node_kind::string || !check_string(n))
|
if (n.kind != static_cast<std::uint8_t>(value_t::string) || !check_string(n))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -5446,7 +5419,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 == node_kind::number_float)
|
if (n.kind == static_cast<std::uint8_t>(value_t::number_float))
|
||||||
{
|
{
|
||||||
floats.push_back(float_range{n.off, n.len, n.extra});
|
floats.push_back(float_range{n.off, n.len, n.extra});
|
||||||
}
|
}
|
||||||
@@ -5464,7 +5437,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 == node_kind::object, true});
|
stack.push_back(frame{i + n.next, n.len, 0, n.kind == static_cast<std::uint8_t>(value_t::object), true});
|
||||||
complete = false;
|
complete = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -5487,11 +5460,13 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
|||||||
throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what));
|
throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NLOHMANN_VIEW_LITTLE_ENDIAN // (the image format is little-endian: no load() elsewhere)
|
|
||||||
/// Read an image into d. The text and the decoded strings stay in the image;
|
/// Read an image into d. The text and the decoded strings stay in the image;
|
||||||
/// the nodes are copied (so that they are aligned, and edits can change them).
|
/// the nodes are copied (so that they are aligned, and edits can change them).
|
||||||
inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check)
|
inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check)
|
||||||
{
|
{
|
||||||
|
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
|
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||||
|
#endif
|
||||||
if (image == nullptr || size < sizeof(image_header))
|
if (image == nullptr || size < sizeof(image_header))
|
||||||
{
|
{
|
||||||
throw_invalid_image("too short");
|
throw_invalid_image("too short");
|
||||||
@@ -5551,7 +5526,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 == node_kind::object && n.len >= document_data::index_min_members)
|
if (n.kind == static_cast<std::uint8_t>(value_t::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));
|
||||||
}
|
}
|
||||||
@@ -5560,7 +5535,6 @@ inline void load_image(document_data& d, const std::uint8_t* image, std::size_t
|
|||||||
std::vector<std::uint32_t>().swap(d.large_objects); // (only needed while building)
|
std::vector<std::uint32_t>().swap(d.large_objects); // (only needed while building)
|
||||||
d.discarded = false;
|
d.discarded = false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@@ -6145,7 +6119,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 == node_kind::object;
|
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object);
|
||||||
if (object)
|
if (object)
|
||||||
{
|
{
|
||||||
sax.start_object(n->len);
|
sax.start_object(n->len);
|
||||||
@@ -7259,7 +7233,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 == node_kind::object;
|
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object);
|
||||||
room(8);
|
room(8);
|
||||||
if (n->len == 0)
|
if (n->len == 0)
|
||||||
{
|
{
|
||||||
@@ -7810,8 +7784,6 @@ 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
|
||||||
|
|
||||||
@@ -7821,16 +7793,13 @@ 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, view_mode Mode = view_mode::read_only>
|
template<typename BasicJsonType, bool Editable = false>
|
||||||
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<is_editable>;
|
using navigation = detail::view::navigation<Editable>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_t = detail::value_t;
|
using value_t = detail::value_t;
|
||||||
@@ -8177,7 +8146,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<is_editable>(*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)));
|
||||||
}
|
}
|
||||||
return front();
|
return front();
|
||||||
}
|
}
|
||||||
@@ -8235,7 +8204,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<is_editable>(*m_doc, m_node, key.data(), key.size()) : 0;
|
return is_object() ? detail::view::count_members<Editable>(*m_doc, m_node, key.data(), key.size()) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_type count(const char* key) const
|
size_type count(const char* key) const
|
||||||
@@ -8377,7 +8346,7 @@ class basic_json_view
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::view::view_serializer<BasicJsonType, is_editable>(*m_doc, out, estimate, style).dump(m_node);
|
detail::view::view_serializer<BasicJsonType, Editable>(*m_doc, out, estimate, style).dump(m_node);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -8413,14 +8382,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 < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
|
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 >
|
||||||
friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
|
friend bool operator==(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b)
|
||||||
{
|
{
|
||||||
return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, M>>(b));
|
return detail::view::equal<BasicJsonType>(side(a), detail::view::view_side<BasicJsonType, basic_json_view<BasicJsonType, E>>(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
template < view_mode M, typename std::enable_if < M != Mode, int >::type = 0 >
|
template < bool E, typename std::enable_if < E != Editable, int >::type = 0 >
|
||||||
friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, M>& b)
|
friend bool operator!=(const basic_json_view& a, const basic_json_view<BasicJsonType, E>& b)
|
||||||
{
|
{
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
@@ -8458,7 +8427,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, is_editable>(*m_doc, m_node);
|
return unedited() ? detail::view::materialize<BasicJsonType, false>(*m_doc, m_node) : detail::view::materialize<BasicJsonType, 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
|
||||||
@@ -8471,8 +8440,8 @@ class basic_json_view
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename, view_mode> friend class basic_json_document;
|
template<typename, bool> friend class basic_json_document;
|
||||||
template<typename, view_mode> friend class basic_json_view;
|
template<typename, bool> 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;
|
||||||
|
|
||||||
@@ -8488,11 +8457,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
|
||||||
/// `is_editable && ...` makes the condition constant, which MSVC reports as
|
/// `Editable && ...` makes the condition constant, which MSVC reports as
|
||||||
/// C4127
|
/// C4127
|
||||||
static bool editable() noexcept
|
static bool editable() noexcept
|
||||||
{
|
{
|
||||||
return is_editable;
|
return 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
|
||||||
@@ -8541,7 +8510,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<is_editable>(*m_doc, m_node, key.data(), key.size());
|
const node* const k = detail::view::find_member<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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8555,7 +8524,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, is_editable> {});
|
return find_key(key, std::integral_constant<bool, 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
|
||||||
@@ -8577,7 +8546,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<is_editable>(*m_doc, m_node, idx)));
|
return basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- get() dispatch ---
|
// --- get() dispatch ---
|
||||||
@@ -8683,19 +8652,16 @@ 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, view_mode Mode = view_mode::read_only>
|
template<typename BasicJsonType, bool Editable = false>
|
||||||
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, Mode>;
|
using view_type = basic_json_view<BasicJsonType, Editable>;
|
||||||
using value_t = detail::value_t;
|
using value_t = detail::value_t;
|
||||||
|
|
||||||
/// an empty (discarded) document
|
/// an empty (discarded) document
|
||||||
@@ -8909,10 +8875,6 @@ class basic_json_document
|
|||||||
/// how load() checks an image (full, bounds, or none)
|
/// how load() checks an image (full, bounds, or none)
|
||||||
using image_check = detail::view::image_check;
|
using image_check = detail::view::image_check;
|
||||||
|
|
||||||
#if NLOHMANN_VIEW_LITTLE_ENDIAN
|
|
||||||
// (the image format is little-endian: big-endian targets have no save()
|
|
||||||
// and load(), so that a call fails to compile instead of always throwing)
|
|
||||||
|
|
||||||
/// The document as an image that load() reads without parsing: the node
|
/// The document as an image that load() reads without parsing: the node
|
||||||
/// index, the text, and the decoded strings. An edited document is
|
/// index, the text, and the decoded strings. An edited document is
|
||||||
/// written in its current state (floats that are not finite become null,
|
/// written in its current state (floats that are not finite become null,
|
||||||
@@ -8954,7 +8916,6 @@ class basic_json_document
|
|||||||
detail::view::load_image(*d.m_data, d.m_data->owned_image.data(), d.m_data->owned_image.size(), check);
|
detail::view::load_image(*d.m_data, d.m_data->owned_image.data(), d.m_data->owned_image.size(), check);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// edits //
|
// edits //
|
||||||
@@ -9096,7 +9057,7 @@ class basic_json_document
|
|||||||
|
|
||||||
detail::view::editor<BasicJsonType, view_type> editor()
|
detail::view::editor<BasicJsonType, view_type> editor()
|
||||||
{
|
{
|
||||||
static_assert(is_editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, view_mode::editable> (json_editable_document)");
|
static_assert(Editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, true> (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");
|
||||||
@@ -9381,13 +9342,13 @@ using ordered_json_document = basic_json_document<ordered_json>;
|
|||||||
/// 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>;
|
||||||
/// an editable parsed JSON text for json
|
/// an editable parsed JSON text for json
|
||||||
using json_editable_document = basic_json_document<json, view_mode::editable>;
|
using json_editable_document = basic_json_document<json, true>;
|
||||||
/// a value of a json_editable_document
|
/// a value of a json_editable_document
|
||||||
using json_editable_view = basic_json_view<json, view_mode::editable>;
|
using json_editable_view = basic_json_view<json, true>;
|
||||||
/// 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, view_mode::editable>;
|
using ordered_json_editable_document = basic_json_document<ordered_json, true>;
|
||||||
/// 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, view_mode::editable>;
|
using ordered_json_editable_view = basic_json_view<ordered_json, true>;
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
|||||||
@@ -666,6 +666,61 @@ TEST_CASE("json_view")
|
|||||||
CHECK(small.root().materialize() == json::parse("[1,[2,3],{\"a\":\"b\\n\"}]"));
|
CHECK(small.root().materialize() == json::parse("[1,[2,3],{\"a\":\"b\\n\"}]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("block sizes")
|
||||||
|
{
|
||||||
|
// documents whose node estimate lies around the size that is allocated
|
||||||
|
// with the header, and whose decoded strings need the first growth of
|
||||||
|
// the buffer, in all sizes around the points where the policies change
|
||||||
|
for (const int count :
|
||||||
|
{
|
||||||
|
1, 2, 10, 50, 100, 255, 256, 257, 500, 1000, 1023, 1024, 1025, 2000, 5000
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// strings with escapes (decoded text, minified)
|
||||||
|
std::string escaped = "[";
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
escaped += (i != 0 ? ",\"a\\n" : "\"a\\n") + std::to_string(i) + "\\u00e4\"";
|
||||||
|
}
|
||||||
|
escaped += ']';
|
||||||
|
json_document e = json_document::parse(escaped);
|
||||||
|
CHECK(e.root().materialize() == json::parse(escaped));
|
||||||
|
e.shrink_to_fit();
|
||||||
|
CHECK(e.root().materialize() == json::parse(escaped));
|
||||||
|
|
||||||
|
// dense numbers (more nodes per byte than the estimate expects)
|
||||||
|
std::string dense = "[";
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
dense += (i != 0 ? ",1" : "1");
|
||||||
|
}
|
||||||
|
dense += ']';
|
||||||
|
json_document n = json_document::parse(dense);
|
||||||
|
CHECK(n.node_count() == static_cast<std::size_t>(count) + 1);
|
||||||
|
CHECK(n.root().materialize() == json::parse(dense));
|
||||||
|
n.shrink_to_fit();
|
||||||
|
CHECK(n.root().size() == static_cast<std::size_t>(count));
|
||||||
|
|
||||||
|
// the same documents, pretty-printed
|
||||||
|
const std::string pretty = json::parse(escaped).dump(2);
|
||||||
|
json_document p = json_document::parse(pretty);
|
||||||
|
CHECK(p.root().materialize() == json::parse(escaped));
|
||||||
|
}
|
||||||
|
|
||||||
|
// a large document with a single escape does not need a buffer of the
|
||||||
|
// size of its input
|
||||||
|
std::string large = "[\"a\\n\"";
|
||||||
|
for (int i = 0; i < 100000; ++i)
|
||||||
|
{
|
||||||
|
large += ",1";
|
||||||
|
}
|
||||||
|
large += ']';
|
||||||
|
const json_document l = json_document::parse(large);
|
||||||
|
CHECK(l.root().size() == 100001);
|
||||||
|
CHECK(l.root()[0].get_string() == "a\n");
|
||||||
|
CHECK(l.root()[100000].get<int>() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("source offsets")
|
SECTION("source offsets")
|
||||||
{
|
{
|
||||||
const std::string text = R"( {"key": "value", "escaped": "a\nb", "n": 42})";
|
const std::string text = R"( {"key": "value", "escaped": "a\nb", "n": 42})";
|
||||||
|
|||||||
@@ -592,7 +592,7 @@ TEST_CASE("json_view node integer bits")
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(v)
|
CAPTURE(v)
|
||||||
n.kind = static_cast<nlohmann::detail::view::node_kind>(0x5A);
|
n.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(static_cast<std::uint8_t>(n.kind) == 0x5A);
|
CHECK(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);
|
||||||
|
|||||||
@@ -26,79 +26,12 @@ 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")
|
||||||
@@ -167,7 +100,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, nlohmann::view_mode::editable>;
|
using document_float = nlohmann::basic_json_document<json_float, true>;
|
||||||
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());
|
||||||
|
|||||||
@@ -28,25 +28,11 @@ using nlohmann::detail::view::node;
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <test_data.hpp>
|
#include <test_data.hpp>
|
||||||
|
|
||||||
namespace json_view_image_test
|
|
||||||
{
|
|
||||||
// whether a document type has save() and load() (only on little-endian targets)
|
|
||||||
template<typename T, typename = void>
|
|
||||||
struct has_save : std::false_type {};
|
|
||||||
template<typename T>
|
|
||||||
struct has_save<T, decltype(static_cast<void>(std::declval<const T&>().save()))> : std::true_type {};
|
|
||||||
template<typename T, typename = void>
|
|
||||||
struct has_load : std::false_type {};
|
|
||||||
template<typename T>
|
|
||||||
struct has_load<T, decltype(static_cast<void>(T::load(std::declval<const std::vector<std::uint8_t>&>())))> : std::true_type {};
|
|
||||||
} // namespace json_view_image_test
|
|
||||||
|
|
||||||
#if !(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
#if !(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -185,9 +171,6 @@ std::uint32_t rng()
|
|||||||
|
|
||||||
TEST_CASE("json_view images: round trips")
|
TEST_CASE("json_view images: round trips")
|
||||||
{
|
{
|
||||||
static_assert(json_view_image_test::has_save<json_document>::value && json_view_image_test::has_load<json_document>::value, "save() and load() on a little-endian target");
|
|
||||||
static_assert(json_view_image_test::has_save<ordered_json_editable_document>::value && json_view_image_test::has_load<ordered_json_editable_document>::value, "save() and load() on a little-endian target");
|
|
||||||
|
|
||||||
SECTION("small documents")
|
SECTION("small documents")
|
||||||
{
|
{
|
||||||
for (const char* text :
|
for (const char* text :
|
||||||
@@ -497,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::node_kind::link));
|
CHECK(node_at(image, i).kind != nlohmann::detail::view::kind_link);
|
||||||
}
|
}
|
||||||
for (const image_check check :
|
for (const image_check check :
|
||||||
{
|
{
|
||||||
@@ -827,13 +810,13 @@ TEST_CASE("json_view images: check")
|
|||||||
{
|
{
|
||||||
rejected(corrupted(image, 12, [&](node & n)
|
rejected(corrupted(image, 12, [&](node & n)
|
||||||
{
|
{
|
||||||
n.kind = static_cast<nlohmann::detail::view::node_kind>(kind);
|
n.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 = nlohmann::detail::view::node_kind::null;
|
n.kind = 0;
|
||||||
n.len = 0;
|
n.len = 0;
|
||||||
n.off = 0;
|
n.off = 0;
|
||||||
}), true);
|
}), true);
|
||||||
@@ -977,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 = nlohmann::detail::view::node_kind::array; // an array: the "keys" are values, and the counts do not match
|
n.kind = 2; // 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 = nlohmann::detail::view::node_kind::array; // {} as []: fine
|
n.kind = 2; // {} 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",[]]})";
|
||||||
@@ -1030,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 = nlohmann::detail::view::node_kind::number_unsigned; // "-12" as unsigned: a sign
|
n.kind = 6; // "-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 = nlohmann::detail::view::node_kind::number_integer;
|
n.kind = 5;
|
||||||
n.extra = 0;
|
n.extra = 0;
|
||||||
});
|
});
|
||||||
CHECK(load_result(positive, image_check::full).empty());
|
CHECK(load_result(positive, image_check::full).empty());
|
||||||
@@ -1044,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 = nlohmann::detail::view::node_kind::number_unsigned; // a float token as integer
|
n.kind = 6; // a float token as integer
|
||||||
n.extra = 7;
|
n.extra = 7;
|
||||||
}), false);
|
}), false);
|
||||||
}
|
}
|
||||||
@@ -1214,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 = static_cast<nlohmann::detail::view::node_kind>(kind);
|
n.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);
|
||||||
@@ -1292,12 +1275,8 @@ TEST_CASE("json_view images: damaged images")
|
|||||||
|
|
||||||
TEST_CASE("json_view images: big-endian targets")
|
TEST_CASE("json_view images: big-endian targets")
|
||||||
{
|
{
|
||||||
// the image format is little-endian: save() and load() do not exist here
|
|
||||||
static_assert(!json_view_image_test::has_save<json_document>::value, "no save() on a big-endian target");
|
|
||||||
static_assert(!json_view_image_test::has_load<json_document>::value, "no load() on a big-endian target");
|
|
||||||
static_assert(!json_view_image_test::has_save<json_editable_document>::value, "no save() on a big-endian target");
|
|
||||||
const json_document d = json_document::parse("[1]");
|
const json_document d = json_document::parse("[1]");
|
||||||
CHECK(d.root().size() == 1);
|
CHECK_THROWS_WITH_AS(d.save(), "[json.exception.type_error.320] json_document images need a little-endian target", json::type_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in new issue
Block a user