diff --git a/BUILD.bazel b/BUILD.bazel
index b6b5152a2..2b1c9f146 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -90,6 +90,7 @@ cc_library(
"include/nlohmann/detail/view/simd.hpp",
"include/nlohmann/detail/view/string_ref.hpp",
"include/nlohmann/detail/view/value.hpp",
+ "include/nlohmann/detail/view/view_mode.hpp",
"include/nlohmann/json.hpp",
"include/nlohmann/json_fwd.hpp",
"include/nlohmann/json_literals.hpp",
diff --git a/docs/mkdocs/docs/api/basic_json_document/erase.md b/docs/mkdocs/docs/api/basic_json_document/erase.md
index 4cbc50d19..7d453addd 100644
--- a/docs/mkdocs/docs/api/basic_json_document/erase.md
+++ b/docs/mkdocs/docs/api/basic_json_document/erase.md
@@ -12,7 +12,7 @@ void erase(view_type array, I idx);
std::size_t erase(const json_pointer& ptr);
```
-Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
+Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `erase`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many
diff --git a/docs/mkdocs/docs/api/basic_json_document/index.md b/docs/mkdocs/docs/api/basic_json_document/index.md
index ea5f449db..b0743e4ec 100644
--- a/docs/mkdocs/docs/api/basic_json_document/index.md
+++ b/docs/mkdocs/docs/api/basic_json_document/index.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-template
+template
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
document holds through a `#!cpp std::unique_ptr`; this is also why views survive a move of the document.
-With `#!cpp Editable == true`, the document also offers [`set`](set.md), [`push_back`](push_back.md),
+With `#!cpp Mode == view_mode::editable`, the document also offers [`set`](set.md), [`push_back`](push_back.md),
[`insert`](insert.md), and [`erase`](erase.md) to change values in place, see [Edits](#edits) below. The source text
-itself is never written; a read-only document (`#!cpp Editable == false`, the default) does not carry any of the
+itself is never written; a read-only document (`#!cpp view_mode::read_only`, the default) does not carry any of the
bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp static_assert`).
## Template parameters
@@ -32,9 +32,10 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
[`ordered_json`](../ordered_json.md). Only 64-bit `number_integer_t`/`number_unsigned_t` types are supported; this
is checked with a `static_assert`.
-`Editable`
-: whether the document supports [`set`](set.md), [`push_back`](push_back.md), [`insert`](insert.md), and
- [`erase`](erase.md) (optional, `#!cpp false` by default). See [Edits](#edits) below.
+`Mode`
+: a [`view_mode`](../view_mode.md): `#!cpp view_mode::editable` if the document supports [`set`](set.md),
+ [`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md) (optional, `#!cpp view_mode::read_only`
+ by default). See [Edits](#edits) below.
## Specializations
@@ -47,7 +48,7 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
## Member types
-- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view`)
+- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view`)
- **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)
@@ -80,15 +81,15 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
### Edits
- [**set**](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers
- to (`#!cpp Editable` documents only)
-- [**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 (`#!cpp Editable` documents only)
+ to (editable documents only)
+- [**push_back**](push_back.md) - append to an array (editable documents only)
+- [**insert**](insert.md) - insert an element into an array before a given position (editable documents only)
- [**erase**](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
- (`#!cpp Editable` documents only)
+ (editable documents only)
## Edits
-An editable document (`#!cpp Editable == true`) can be changed after parsing, with [`set`](set.md),
+An editable document (`#!cpp view_mode::editable`) can be changed after parsing, with [`set`](set.md),
[`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md);
[`json_editable_document`](../json_editable_document.md) and
[`ordered_json_editable_document`](../ordered_json_editable_document.md) are the corresponding specializations. A few
diff --git a/docs/mkdocs/docs/api/basic_json_document/insert.md b/docs/mkdocs/docs/api/basic_json_document/insert.md
index b513dfc0d..883acabda 100644
--- a/docs/mkdocs/docs/api/basic_json_document/insert.md
+++ b/docs/mkdocs/docs/api/basic_json_document/insert.md
@@ -5,7 +5,7 @@ template
view_type insert(view_type array, I idx, V&& value);
```
-Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
+Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `insert`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
Inserts `value` into `array` as a new element before position `idx`, which must not be past the end
diff --git a/docs/mkdocs/docs/api/basic_json_document/push_back.md b/docs/mkdocs/docs/api/basic_json_document/push_back.md
index 37ce10bc5..96e3f113a 100644
--- a/docs/mkdocs/docs/api/basic_json_document/push_back.md
+++ b/docs/mkdocs/docs/api/basic_json_document/push_back.md
@@ -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,
strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
-Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
+Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `push_back`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
## Template parameters
diff --git a/docs/mkdocs/docs/api/basic_json_document/save.md b/docs/mkdocs/docs/api/basic_json_document/save.md
index f69d5c9f8..c47295048 100644
--- a/docs/mkdocs/docs/api/basic_json_document/save.md
+++ b/docs/mkdocs/docs/api/basic_json_document/save.md
@@ -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
[`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
-the same bytes -- also across `BasicJsonType` and `#!cpp Editable`, since the image reflects document order and
+the same bytes -- also across `BasicJsonType` and the [`view_mode`](../view_mode.md), since the image reflects document order and
values only, not which specialization produced them.
## Return value
diff --git a/docs/mkdocs/docs/api/basic_json_document/set.md b/docs/mkdocs/docs/api/basic_json_document/set.md
index ae26ed516..dd5d99611 100644
--- a/docs/mkdocs/docs/api/basic_json_document/set.md
+++ b/docs/mkdocs/docs/api/basic_json_document/set.md
@@ -18,7 +18,7 @@ template
view_type set(const json_pointer& ptr, V&& value);
```
-Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
+Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md))
has `set`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
1. Replaces the value `target` refers to with `value`.
diff --git a/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md b/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md
index df1e939fa..7ae259287 100644
--- a/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md
+++ b/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md
@@ -1,20 +1,32 @@
# nlohmann::basic_json_view::basic_json_view
```cpp
+// (1)
basic_json_view() noexcept = default;
+
+// (2) only for Mode == view_mode::editable
+basic_json_view(const basic_json_view& other) noexcept;
```
-Creates an invalid (discarded) view: [`type()`](type.md) is `#!cpp value_t::discarded` and
-[`is_discarded()`](is_discarded.md) is `#!cpp true`.
+1. Creates an invalid (discarded) view: [`type()`](type.md) is `#!cpp value_t::discarded` and
+ [`is_discarded()`](is_discarded.md) is `#!cpp true`.
+2. Converts a view of a read-only document into a view of an editable document (implicitly), so that a function
+ taking a [`json_editable_view`](../json_editable_view.md) accepts both. The result is a view of the same value of
+ the same read-only document. See [`view_mode`](../view_mode.md) for why there is no conversion the other way.
-This is the only constructor a caller can use directly. Every other view is obtained from a
+## Parameters
+
+`other` (in)
+: a view of a read-only document with the same `BasicJsonType`
+
+Apart from these, a view is obtained from a
[`basic_json_document`](../basic_json_document/index.md), via [`root()`](../basic_json_document/root.md) or by
navigating into a container with [`operator[]`](operator[].md), [`at`](at.md), [`front`](front.md), [`back`](back.md),
[`find`](find.md), or iteration.
## Exception safety
-No-throw guarantee: this constructor never throws exceptions.
+No-throw guarantee: these constructors never throw exceptions.
## Complexity
@@ -45,6 +57,7 @@ placeholder for "no value yet" and later be assigned a real view.
- [is_discarded](is_discarded.md) - return whether the view is invalid
- [root](../basic_json_document/root.md) - the view of a document's root value
+- [view_mode](../view_mode.md) - whether a document and its views can be edited
## Version history
diff --git a/docs/mkdocs/docs/api/basic_json_view/index.md b/docs/mkdocs/docs/api/basic_json_view/index.md
index 5f0d90694..5049e2242 100644
--- a/docs/mkdocs/docs/api/basic_json_view/index.md
+++ b/docs/mkdocs/docs/api/basic_json_view/index.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-template
+template
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).
`basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an
-**editable** document (`#!cpp Editable == true`) sees every edit made through
+**editable** document (`#!cpp view_mode::editable`) sees every edit made through
[`basic_json_document::set`](../basic_json_document/set.md) and
[`basic_json_document::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
@@ -49,9 +49,10 @@ still refers to it -- including ones taken before the change -- reads the new va
: a specialization of [`basic_json`](../basic_json/index.md), matching the
[`basic_json_document`](../basic_json_document/index.md) the view was taken from.
-`Editable`
-: whether the view is of an editable document, matching the [`basic_json_document`](../basic_json_document/index.md)
- it was taken from (optional, `#!cpp false` by default). See [Edits](../basic_json_document/index.md#edits).
+`Mode`
+: a [`view_mode`](../view_mode.md): whether the view is of an editable document, matching the
+ [`basic_json_document`](../basic_json_document/index.md) it was taken from (optional, `#!cpp view_mode::read_only` by
+ default). See [Edits](../basic_json_document/index.md#edits).
## Specializations
diff --git a/docs/mkdocs/docs/api/json_document.md b/docs/mkdocs/docs/api/json_document.md
index cfa22e80b..2da6fe548 100644
--- a/docs/mkdocs/docs/api/json_document.md
+++ b/docs/mkdocs/docs/api/json_document.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using json_document = basic_json_document;
+using json_document = basic_json_document;
```
This type is a [`basic_json_document`](basic_json_document/index.md) of the default [`json`](json.md)
diff --git a/docs/mkdocs/docs/api/json_editable_document.md b/docs/mkdocs/docs/api/json_editable_document.md
index ec5226b78..2acea534c 100644
--- a/docs/mkdocs/docs/api/json_editable_document.md
+++ b/docs/mkdocs/docs/api/json_editable_document.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using json_editable_document = basic_json_document;
+using json_editable_document = basic_json_document;
```
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the default
diff --git a/docs/mkdocs/docs/api/json_editable_view.md b/docs/mkdocs/docs/api/json_editable_view.md
index ddee4e372..37301d3d6 100644
--- a/docs/mkdocs/docs/api/json_editable_view.md
+++ b/docs/mkdocs/docs/api/json_editable_view.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using json_editable_view = basic_json_view;
+using json_editable_view = basic_json_view;
```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a
diff --git a/docs/mkdocs/docs/api/json_view.md b/docs/mkdocs/docs/api/json_view.md
index 89229ed6f..0998549d9 100644
--- a/docs/mkdocs/docs/api/json_view.md
+++ b/docs/mkdocs/docs/api/json_view.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using json_view = basic_json_view;
+using json_view = basic_json_view;
```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`json_document`](json_document.md).
diff --git a/docs/mkdocs/docs/api/ordered_json_document.md b/docs/mkdocs/docs/api/ordered_json_document.md
index b737c2e4d..40039774d 100644
--- a/docs/mkdocs/docs/api/ordered_json_document.md
+++ b/docs/mkdocs/docs/api/ordered_json_document.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using ordered_json_document = basic_json_document;
+using ordered_json_document = basic_json_document;
```
This type is a [`basic_json_document`](basic_json_document/index.md) of the [`ordered_json`](ordered_json.md)
diff --git a/docs/mkdocs/docs/api/ordered_json_editable_document.md b/docs/mkdocs/docs/api/ordered_json_editable_document.md
index cf86307e5..b47dfc3c2 100644
--- a/docs/mkdocs/docs/api/ordered_json_editable_document.md
+++ b/docs/mkdocs/docs/api/ordered_json_editable_document.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using ordered_json_editable_document = basic_json_document;
+using ordered_json_editable_document = basic_json_document;
```
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the
diff --git a/docs/mkdocs/docs/api/ordered_json_editable_view.md b/docs/mkdocs/docs/api/ordered_json_editable_view.md
index 1c9953d57..a42ca42f5 100644
--- a/docs/mkdocs/docs/api/ordered_json_editable_view.md
+++ b/docs/mkdocs/docs/api/ordered_json_editable_view.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using ordered_json_editable_view = basic_json_view;
+using ordered_json_editable_view = basic_json_view;
```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
diff --git a/docs/mkdocs/docs/api/ordered_json_view.md b/docs/mkdocs/docs/api/ordered_json_view.md
index 9df72040f..017f2e9a8 100644
--- a/docs/mkdocs/docs/api/ordered_json_view.md
+++ b/docs/mkdocs/docs/api/ordered_json_view.md
@@ -3,7 +3,7 @@
Defined in header ``
```cpp
-using ordered_json_view = basic_json_view;
+using ordered_json_view = basic_json_view;
```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
diff --git a/docs/mkdocs/docs/api/view_mode.md b/docs/mkdocs/docs/api/view_mode.md
new file mode 100644
index 000000000..c0339b5fb
--- /dev/null
+++ b/docs/mkdocs/docs/api/view_mode.md
@@ -0,0 +1,88 @@
+# nlohmann::view_mode
+
+Defined in header ``
+
+```cpp
+enum class view_mode : std::uint8_t
+{
+ read_only,
+ editable
+};
+```
+
+The second template parameter of [`basic_json_document`](basic_json_document/index.md) and
+[`basic_json_view`](basic_json_view/index.md): whether a document can be changed after parsing, and whether a view is
+of such a document.
+
+`read_only`
+: the default: the document is parsed once and then only read. It carries none of the bookkeeping edits need, and
+ calling [`set`](basic_json_document/set.md), [`push_back`](basic_json_document/push_back.md),
+ [`insert`](basic_json_document/insert.md), or [`erase`](basic_json_document/erase.md) on it fails to compile.
+
+`editable`
+: the document also offers [`set`](basic_json_document/set.md), [`push_back`](basic_json_document/push_back.md),
+ [`insert`](basic_json_document/insert.md), and [`erase`](basic_json_document/erase.md); its views see every edit,
+ see [Edits](basic_json_document/index.md#edits).
+
+The shortcuts name both modes: [`json_document`](json_document.md) and [`json_view`](json_view.md) are read-only,
+[`json_editable_document`](json_editable_document.md) and [`json_editable_view`](json_editable_view.md) are editable
+(and likewise for `ordered_json`).
+
+## Notes
+
+The mode is a compile-time property of the document's and the view's type, not a setting of a call. A read-only view
+reads a container in place, while a view of an editable document follows the links that edits leave behind. A view of
+a read-only document converts implicitly to a view of an editable document of the same `BasicJsonType`: a read-only
+document has no edits, so the converted view reads it like an editable document before its first edit (at the cost of
+one check per access). There is no conversion the other way, because a read-only view of an editable document would
+read wrong values after the next edit.
+
+A function that takes a [`json_editable_view`](json_editable_view.md) therefore accepts the views of both modes. A
+function template deduces the mode from its argument instead, without conversions, so it names the modes it accepts in
+its signature:
+
+```cpp
+// views of editable documents only
+template
+void f(basic_json_view v);
+
+// views of read-only documents only
+template
+void f(basic_json_view v);
+
+// either
+template
+void f(basic_json_view 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 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, one that accepts only views of
+ editable documents, and a function taking a `json_editable_view` that also accepts a view of a read-only document.
+
+ ```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`
+- [(constructor)](basic_json_view/basic_json_view.md) - the conversion from a read-only to an editable view
+
+## Version history
+
+Added in version 3.13.0.
diff --git a/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp b/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp
index 9d7821066..7089466e4 100644
--- a/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp
+++ b/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp
@@ -5,7 +5,7 @@ using nlohmann::ordered_json_editable_document;
int main()
{
- // ordered_json_editable_document is basic_json_document
+ // ordered_json_editable_document is basic_json_document
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
diff --git a/docs/mkdocs/docs/examples/view_mode.cpp b/docs/mkdocs/docs/examples/view_mode.cpp
new file mode 100644
index 000000000..5cdd7718b
--- /dev/null
+++ b/docs/mkdocs/docs/examples/view_mode.cpp
@@ -0,0 +1,36 @@
+#include
+#include
+
+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
+std::size_t count_members(basic_json_view v)
+{
+ return v.is_object() ? v.size() : 0;
+}
+
+// accepts only the views of editable documents
+template
+bool is_edited(basic_json_view v)
+{
+ return v.is_string() && v.get_string() == "edited";
+}
+
+// a function (no template) taking a json_editable_view also accepts the view of a read-only document
+std::size_t size_of(nlohmann::json_editable_view v)
+{
+ return v.size();
+}
+
+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';
+ std::cout << size_of(doc.root()) << ' ' << size_of(edoc.root()) << '\n';
+}
diff --git a/docs/mkdocs/docs/examples/view_mode.output b/docs/mkdocs/docs/examples/view_mode.output
new file mode 100644
index 000000000..0e56385a0
--- /dev/null
+++ b/docs/mkdocs/docs/examples/view_mode.output
@@ -0,0 +1,3 @@
+2 1
+true
+2 1
diff --git a/docs/mkdocs/docs/features/json_view.md b/docs/mkdocs/docs/features/json_view.md
index 4f0938273..4b5498af5 100644
--- a/docs/mkdocs/docs/features/json_view.md
+++ b/docs/mkdocs/docs/features/json_view.md
@@ -252,14 +252,14 @@ the stream's `width`/`fill` for indentation.
## Editing a document
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but
-not change it. [`basic_json_document`](../api/basic_json_document/index.md) -- more conveniently
+not change it. [`basic_json_document`](../api/basic_json_document/index.md) -- more conveniently
spelled [`json_editable_document`](../api/json_editable_document.md) or
[`ordered_json_editable_document`](../api/ordered_json_editable_document.md) -- also lets you
[`set`](../api/basic_json_document/set.md) a value, [`push_back`](../api/basic_json_document/push_back.md) onto or
[`insert`](../api/basic_json_document/insert.md) into an array, and [`erase`](../api/basic_json_document/erase.md)
an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
-`#!cpp Editable` defaults to `#!cpp false`, so `json_document`/`ordered_json_document` are unaffected -- they carry
+The [mode](../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so `json_document`/`ordered_json_document` are unaffected -- they carry
none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a
runtime one.
diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml
index f2b99cf3a..66bac7e80 100644
--- a/docs/mkdocs/mkdocs.yml
+++ b/docs/mkdocs/mkdocs.yml
@@ -411,6 +411,7 @@ nav:
- 'ordered_json_editable_view': api/ordered_json_editable_view.md
- 'ordered_json_view': api/ordered_json_view.md
- 'ordered_map': api/ordered_map.md
+ - 'view_mode': api/view_mode.md
- macros:
- 'Overview': api/macros/index.md
- 'JSON_ASSERT': api/macros/json_assert.md
diff --git a/include/nlohmann/detail/view/builder.hpp b/include/nlohmann/detail/view/builder.hpp
index 3e4ea268a..f7b560f50 100644
--- a/include/nlohmann/detail/view/builder.hpp
+++ b/include/nlohmann/detail/view/builder.hpp
@@ -639,11 +639,11 @@ run_found:
switch (cur())
{
case '{':
- open(value_t::object);
+ open(node_kind::object);
++p;
goto obj_first;
case '[':
- open(value_t::array);
+ open(node_kind::array);
++p;
goto arr_first;
default:
@@ -665,11 +665,11 @@ run_found:
if (NLOHMANN_VIEW_UNLIKELY(!string())) { return false; } \
break; \
case '{': \
- open(value_t::object); \
+ open(node_kind::object); \
++p; \
goto obj_first; \
case '[': \
- open(value_t::array); \
+ open(node_kind::array); \
++p; \
goto arr_first; \
case '-': \
@@ -886,17 +886,17 @@ root_done:
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_true()
{
- return literal("true", value_t::boolean, node_flags::is_true);
+ return literal("true", node_kind::boolean, node_flags::is_true);
}
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_false()
{
- return literal("false", value_t::boolean, 0);
+ return literal("false", node_kind::boolean, 0);
}
NLOHMANN_VIEW_ALWAYS_INLINE bool literal_null()
{
- return literal("null", value_t::null, 0);
+ return literal("null", node_kind::null, 0);
}
/// skip whitespace (and comments); false on a malformed comment
@@ -962,7 +962,7 @@ indent_done:
/// append a node: (kind, flags, extra, off) and the second word (len, or
/// an integer's value); two stores on little-endian targets
- NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(value_t k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
+ NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
{
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
{
@@ -978,7 +978,7 @@ indent_done:
std::memcpy(reinterpret_cast(n), &first, 8);
std::memcpy(reinterpret_cast(n) + 8, &second, 8);
#else
- n->kind = static_cast(k);
+ n->kind = k;
n->flags = flags;
n->extra = extra;
n->off = static_cast(off);
@@ -990,7 +990,7 @@ indent_done:
return n;
}
- NLOHMANN_VIEW_ALWAYS_INLINE void open(value_t k)
+ NLOHMANN_VIEW_ALWAYS_INLINE void open(node_kind k)
{
// (base is read after push_node(), which moves the node array when it
// grows; in one expression the order of the two is unspecified)
@@ -1017,7 +1017,7 @@ indent_done:
max_depth = (std::max)(max_depth, depth);
cur_idx = idx;
cur_count = 0;
- cur_is_object = k == value_t::object;
+ cur_is_object = k == node_kind::object;
}
NLOHMANN_VIEW_ALWAYS_INLINE void close()
@@ -1047,7 +1047,7 @@ indent_done:
/// the literal `text` at p (N counts the terminating NUL)
template
- NLOHMANN_VIEW_ALWAYS_INLINE bool literal(const char (&text)[N], value_t k, std::uint8_t flags) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): a string literal
+ NLOHMANN_VIEW_ALWAYS_INLINE bool literal(const char (&text)[N], node_kind k, std::uint8_t flags) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): a string literal
{
const std::size_t n = N - 1;
if (NLOHMANN_VIEW_UNLIKELY(e - p < static_cast(n) || std::memcmp(p, text, n) != 0))
@@ -1126,23 +1126,23 @@ indent_done:
is_float = true;
}
- value_t kind = value_t::number_float;
+ node_kind kind = node_kind::number_float;
if (!is_float)
{
// integers that do not fit become floats, as in parse()
if (negative)
{
- kind = digits_exceed(int_start, int_digits, "9223372036854775808") ? value_t::number_float : value_t::number_integer;
+ kind = digits_exceed(int_start, int_digits, "9223372036854775808") ? node_kind::number_float : node_kind::number_integer;
}
else
{
- kind = digits_exceed(int_start, int_digits, "18446744073709551615") ? value_t::number_float : value_t::number_unsigned;
+ kind = digits_exceed(int_start, int_digits, "18446744073709551615") ? node_kind::number_float : node_kind::number_unsigned;
}
}
// parse() rejects floats that overflow; only numbers whose magnitude
// could reach the largest FloatType (1e308 for double, 1e38 for
// float) need the conversion
- if (NLOHMANN_VIEW_UNLIKELY(static_cast(int_digits) + exponent > std::numeric_limits::max_exponent10 - 8 && kind == value_t::number_float))
+ if (NLOHMANN_VIEW_UNLIKELY(static_cast(int_digits) + exponent > std::numeric_limits::max_exponent10 - 8 && kind == node_kind::number_float))
{
if (builder::float_overflows(s, p))
{
@@ -1152,7 +1152,7 @@ indent_done:
}
const auto layout = static_cast((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8));
auto second = static_cast(p - s);
- if (kind != value_t::number_float)
+ if (kind != node_kind::number_float)
{
// integers are converted now, while their digits are in cache
const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast(int_digits), e)
@@ -1175,7 +1175,7 @@ indent_done:
p = scan_string_run(p, e);
if (NLOHMANN_VIEW_LIKELY(cur() == '"'))
{
- push_node(value_t::string, 0, 0, static_cast(s - b), static_cast(p - s));
+ push_node(node_kind::string, 0, 0, static_cast(s - b), static_cast(p - s));
++p;
return true;
}
@@ -1185,7 +1185,7 @@ indent_done:
return false;
}
p = r.p;
- push_node(value_t::string, node_flags::escaped, 0, r.start, r.len);
+ push_node(node_kind::string, node_flags::escaped, 0, r.start, r.len);
return true;
}
};
diff --git a/include/nlohmann/detail/view/document_data.hpp b/include/nlohmann/detail/view/document_data.hpp
index 55f9d8c76..afef5a7a2 100644
--- a/include/nlohmann/detail/view/document_data.hpp
+++ b/include/nlohmann/detail/view/document_data.hpp
@@ -232,7 +232,7 @@ struct document_data
/// following loads.
static NLOHMANN_VIEW_ALWAYS_INLINE const node* deref(const node* n) noexcept
{
- return NLOHMANN_VIEW_LIKELY(n->kind != kind_link) ? n : follow_link(n);
+ return NLOHMANN_VIEW_LIKELY(n->kind != node_kind::link) ? n : follow_link(n);
}
static NLOHMANN_VIEW_NOINLINE const node* follow_link(const node* n) noexcept
diff --git a/include/nlohmann/detail/view/edit.hpp b/include/nlohmann/detail/view/edit.hpp
index bad8d9da3..f47bf5985 100644
--- a/include/nlohmann/detail/view/edit.hpp
+++ b/include/nlohmann/detail/view/edit.hpp
@@ -26,12 +26,10 @@
#include
#include
#include
+#include
NLOHMANN_JSON_NAMESPACE_BEGIN
-template
-class basic_json_view;
-
namespace detail
{
namespace view
@@ -120,15 +118,15 @@ class editor
View set(const View& object, string_view_t key, V&& value)
{
node* const o = own(object);
- if (o->kind != static_cast(value_t::object) && o->kind != static_cast(value_t::null))
+ if (o->kind != node_kind::object && o->kind != node_kind::null)
{
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name());
}
check_utf8(key.data(), key.size());
const encoded e = encode(std::forward(value));
- if (o->kind == static_cast(value_t::null))
+ if (o->kind == node_kind::null)
{
- become_empty(o, value_t::object);
+ become_empty(o, node_kind::object);
}
// an existing member: assign it (and drop later duplicates, so that
// lookups, iteration, and materialize() agree)
@@ -171,7 +169,7 @@ class editor
View set(const View& array, std::size_t idx, V&& value)
{
node* const a = own(array);
- if (a->kind != static_cast(value_t::array))
+ if (a->kind != node_kind::array)
{
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name());
}
@@ -188,14 +186,14 @@ class editor
View push_back(const View& array, V&& value)
{
node* const a = own(array);
- if (a->kind != static_cast(value_t::array) && a->kind != static_cast(value_t::null))
+ if (a->kind != node_kind::array && a->kind != node_kind::null)
{
throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name());
}
const encoded e = encode(std::forward(value));
- if (a->kind == static_cast(value_t::null))
+ if (a->kind == node_kind::null)
{
- become_empty(a, value_t::array);
+ become_empty(a, node_kind::array);
}
node* const slot = new_slot(e);
node* const h = block_of(m_doc, a, 1);
@@ -212,7 +210,7 @@ class editor
View insert(const View& array, std::size_t idx, V&& value)
{
node* const a = own(array);
- if (a->kind != static_cast(value_t::array))
+ if (a->kind != node_kind::array)
{
throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name());
}
@@ -232,7 +230,7 @@ class editor
std::size_t erase(const View& object, string_view_t key)
{
node* const o = own(object);
- if (o->kind != static_cast(value_t::object))
+ if (o->kind != node_kind::object)
{
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name());
}
@@ -250,7 +248,7 @@ class editor
void erase(const View& array, std::size_t idx)
{
node* const a = own(array);
- if (a->kind != static_cast(value_t::array))
+ if (a->kind != node_kind::array)
{
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name());
}
@@ -324,11 +322,11 @@ class editor
}
/// turn a null into an empty array/object in place
- static void become_empty(node* n, value_t k) noexcept
+ static void become_empty(node* n, node_kind k) noexcept
{
const std::uint8_t linked = n->flags & node_flags::linked;
*n = node{};
- n->kind = static_cast(k);
+ n->kind = k;
n->flags = static_cast(node_flags::is_new | linked);
n->next = 1;
}
@@ -405,8 +403,8 @@ class editor
template
struct is_view : std::false_type {};
- template
- struct is_view> : std::true_type {};
+ template
+ struct is_view> : std::true_type {};
template
encoded encode(V&& v)
@@ -421,8 +419,8 @@ class editor
}
/// a view of any document (copied; nothing is shared with it)
- template
- encoded encode_impl(const basic_json_view& v, encode_tag<0> /*view*/)
+ template
+ encoded encode_impl(const basic_json_view& v, encode_tag<0> /*view*/)
{
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
{
@@ -434,8 +432,8 @@ class editor
r.scalar = copy_scalar(*v.m_doc, *v.m_node);
return r;
}
- r.region = alloc_nodes(m_doc, count_nodes(*v.m_doc, v.m_node));
- fill_nodes(*v.m_doc, v.m_node, r.region);
+ r.region = alloc_nodes(m_doc, count_nodes(*v.m_doc, v.m_node));
+ fill_nodes(*v.m_doc, v.m_node, r.region);
edit_state_of(m_doc).regions.emplace(r.region, nullptr);
return r;
}
@@ -457,14 +455,14 @@ class editor
encoded encode_impl(std::nullptr_t /*unused*/, encode_tag<2> /*null*/)
{
encoded r;
- r.scalar = plain_node(value_t::null);
+ r.scalar = plain_node(node_kind::null);
return r;
}
encoded encode_impl(bool b, encode_tag<3> /*boolean*/)
{
encoded r;
- r.scalar = plain_node(value_t::boolean);
+ r.scalar = plain_node(node_kind::boolean);
r.scalar.flags = static_cast(r.scalar.flags | (b ? node_flags::is_true : 0));
return r;
}
@@ -493,10 +491,10 @@ class editor
return encode_impl(BasicJsonType(std::forward(x)), encode_tag<1> {});
}
- static node plain_node(value_t k) noexcept
+ static node plain_node(node_kind k) noexcept
{
node n{};
- n.kind = static_cast(k);
+ n.kind = k;
n.flags = node_flags::is_new;
return n;
}
@@ -510,19 +508,19 @@ class editor
template
node number_node(T x, std::integral_constant /*signed*/)
{
- return integer_node(static_cast(static_cast(x)), value_t::number_integer);
+ return integer_node(static_cast(static_cast(x)), node_kind::number_integer);
}
template
node number_node(T x, std::integral_constant /*unsigned*/)
{
- return integer_node(static_cast(x), value_t::number_unsigned);
+ return integer_node(static_cast(x), node_kind::number_unsigned);
}
/// an integer with its canonical token in the edit arena
- node integer_node(std::uint64_t bits, value_t k)
+ node integer_node(std::uint64_t bits, node_kind k)
{
- const bool negative = k == value_t::number_integer && static_cast(bits) < 0;
+ const bool negative = k == node_kind::number_integer && static_cast(bits) < 0;
std::uint64_t magnitude = negative ? 0 - bits : bits;
std::array buf{};
char* p = buf.data() + buf.size(); // NOLINT(misc-const-correctness): digits are written through p
@@ -541,7 +539,7 @@ class editor
n.flags = static_cast(n.flags | node_flags::edited);
n.off = append_text(m_doc, p, len);
// number_length() adds one for the sign of number_integer nodes
- n.extra = static_cast(k == value_t::number_integer ? len - 1 : len);
+ n.extra = static_cast(k == node_kind::number_integer ? len - 1 : len);
set_integer_bits(n, bits);
return n;
}
@@ -563,7 +561,7 @@ class editor
{
text = BasicJsonType(x).dump();
}
- node n = plain_node(value_t::number_float);
+ node n = plain_node(node_kind::number_float);
n.flags = static_cast(n.flags | node_flags::edited);
n.extra = 0xFFFFu; // (the digit layout is not recorded)
n.off = append_text(m_doc, text.data(), text.size());
@@ -578,7 +576,7 @@ class editor
{
throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
}
- node n = plain_node(value_t::string);
+ node n = plain_node(node_kind::string);
n.flags = static_cast(n.flags | node_flags::edited);
n.off = append_text(m_doc, s, len);
n.len = static_cast(len);
@@ -600,10 +598,10 @@ class editor
return string_node(from.str(n), n.len);
case value_t::number_integer:
case value_t::number_unsigned:
- return integer_node(integer_bits(n), static_cast(n.kind));
+ return integer_node(integer_bits(n), n.kind);
case value_t::number_float:
{
- node r = plain_node(value_t::number_float);
+ node r = plain_node(node_kind::number_float);
r.flags = static_cast(r.flags | node_flags::edited);
r.off = append_text(m_doc, from.str(n), n.len);
r.len = n.len;
@@ -612,7 +610,7 @@ class editor
}
case value_t::boolean:
{
- node r = plain_node(value_t::boolean);
+ node r = plain_node(node_kind::boolean);
r.flags = static_cast(r.flags | (n.flags & node_flags::is_true));
return r;
}
@@ -622,7 +620,7 @@ class editor
case value_t::binary:
case value_t::discarded:
default:
- return plain_node(value_t::null);
+ return plain_node(node_kind::null);
}
}
@@ -631,17 +629,17 @@ class editor
switch (j.type())
{
case value_t::null:
- return plain_node(value_t::null);
+ return plain_node(node_kind::null);
case value_t::boolean:
{
- node r = plain_node(value_t::boolean);
+ node r = plain_node(node_kind::boolean);
r.flags = static_cast(r.flags | (j.template get() ? node_flags::is_true : 0));
return r;
}
case value_t::number_integer:
- return integer_node(static_cast(static_cast(j.template get())), value_t::number_integer);
+ return integer_node(static_cast(static_cast(j.template get())), node_kind::number_integer);
case value_t::number_unsigned:
- return integer_node(static_cast(j.template get()), value_t::number_unsigned);
+ return integer_node(static_cast(j.template get()), node_kind::number_unsigned);
case value_t::number_float:
return float_node(j.template get());
case value_t::string:
@@ -681,7 +679,7 @@ class editor
++r;
if (is_container(*n))
{
- open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast(value_t::object)});
+ open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object});
}
// the next value: close finished containers, then step over the key
for (;;)
@@ -728,9 +726,9 @@ class editor
if (is_container(*n))
{
node* const self = out++;
- *self = plain_node(static_cast(n->kind));
+ *self = plain_node(n->kind);
self->len = n->len;
- open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == static_cast(value_t::object), self});
+ open.push_back(frame{walk::first(d, n), walk::end(d, n), n->kind == node_kind::object, self});
}
else
{
@@ -819,7 +817,7 @@ class editor
if (n->is_structured())
{
node* const self = out++;
- *self = plain_node(n->type());
+ *self = plain_node(static_cast(n->type()));
self->len = static_cast(n->size());
open.push_back(frame{n->cbegin(), n->cend(), n->is_object(), self});
}
diff --git a/include/nlohmann/detail/view/edit_storage.hpp b/include/nlohmann/detail/view/edit_storage.hpp
index 9a45bfebd..e5b7ddb90 100644
--- a/include/nlohmann/detail/view/edit_storage.hpp
+++ b/include/nlohmann/detail/view/edit_storage.hpp
@@ -27,7 +27,7 @@
// 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
// node_flags::moved: its elements then live in a separate sequence (a header
-// node, then the entries), whose entries link to the values (kind_link).
+// node, then the entries), whose entries link to the values (node_kind::link).
NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
@@ -185,7 +185,7 @@ inline node* block_of(document_data& d, node* n, std::size_t extra)
return nh;
}
reserve_moved(d); // (so that set_moved() below cannot throw: the links are marked before)
- const bool object = n->kind == static_cast(value_t::object);
+ const bool object = n->kind == node_kind::object;
const std::size_t used = 1 + (static_cast(n->len) * (object ? 2 : 1));
const std::size_t cap = used + extra;
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)
}
- const bool object = c->kind == static_cast(value_t::object);
+ const bool object = c->kind == node_kind::object;
const node* down = nullptr;
for (const node* p = d.first_child_edited(c), *e = d.child_end_edited(c); p != e;)
{
diff --git a/include/nlohmann/detail/view/image.hpp b/include/nlohmann/detail/view/image.hpp
index af9994712..c13d269e1 100644
--- a/include/nlohmann/detail/view/image.hpp
+++ b/include/nlohmann/detail/view/image.hpp
@@ -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.len = 0; // counted below
r.next = 0; // set when the container is complete
- stack.push_back(frame{d.first_child_edited(v), d.child_end_edited(v), out.size(), 0, v->kind == static_cast(value_t::object)});
+ stack.push_back(frame{d.first_child_edited(v), d.child_end_edited(v), out.size(), 0, v->kind == node_kind::object});
break;
case value_t::string:
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'))
{
r = node{}; // nan and infinity: null, as dump() writes them
- r.kind = static_cast(value_t::null);
+ r.kind = node_kind::null;
break;
}
r.off = static_cast(d.size + text_tail.size());
@@ -221,7 +221,7 @@ inline std::vector save_image(const document_data& d)
// extra of an object, and the same document saves to the same bytes
for (std::size_t i = 0; i < count; ++i)
{
- if (nodes[i].kind == static_cast(value_t::object) && nodes[i].extra != 0)
+ if (nodes[i].kind == node_kind::object && nodes[i].extra != 0)
{
std::memset(o + (i * sizeof(node)) + offsetof(node, extra), 0, sizeof(nodes[i].extra));
}
@@ -337,7 +337,7 @@ inline bool float_token_finite(const unsigned char* s, std::size_t len, const nu
if (static_cast(t.int_digits) + t.exponent > 300)
{
node n{};
- n.kind = static_cast(value_t::number_float);
+ n.kind = node_kind::number_float;
n.len = static_cast(len);
const auto v = float_value(reinterpret_cast(s), n); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
return v <= (std::numeric_limits::max)() && v >= -(std::numeric_limits::max)();
@@ -358,7 +358,7 @@ inline bool check_integer(const node& n, const unsigned char* text)
}
// the token's value is the stored one; number_integer nodes of edits can
// be non-negative (as basic_json keeps the type of a value)
- const bool integer = n.kind == static_cast(value_t::number_integer);
+ const bool integer = n.kind == node_kind::number_integer;
if (t.is_float || t.int_digits > 20 || (t.negative && !integer))
{
return false;
@@ -540,7 +540,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{
return false;
}
- if (n.kind != static_cast(value_t::number_float))
+ if (n.kind != node_kind::number_float)
{
return (n.extra >> 8u) == 0;
}
@@ -581,7 +581,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
const node& n = nodes[i];
if (!stack.empty() && stack.back().object && stack.back().expect_key)
{
- if (n.kind != static_cast(value_t::string) || !check_string(n))
+ if (n.kind != node_kind::string || !check_string(n))
{
return false;
}
@@ -622,7 +622,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{
// (the contents of float tokens are checked later; the token of an
// integer has at most 256 characters)
- if (n.kind == static_cast(value_t::number_float))
+ if (n.kind == node_kind::number_float)
{
floats.push_back(float_range{n.off, n.len, n.extra});
}
@@ -640,7 +640,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
{
return false;
}
- stack.push_back(frame{i + n.next, n.len, 0, n.kind == static_cast(value_t::object), true});
+ stack.push_back(frame{i + n.next, n.len, 0, n.kind == node_kind::object, true});
complete = false;
break;
}
@@ -729,7 +729,7 @@ inline void load_image(document_data& d, const std::uint8_t* image, std::size_t
for (std::size_t i = 0; i < count; ++i)
{
const node& n = d.nodes[i];
- if (n.kind == static_cast(value_t::object) && n.len >= document_data::index_min_members)
+ if (n.kind == node_kind::object && n.len >= document_data::index_min_members)
{
d.large_objects.push_back(static_cast(i));
}
diff --git a/include/nlohmann/detail/view/lookup.hpp b/include/nlohmann/detail/view/lookup.hpp
index e2038a956..f288927bb 100644
--- a/include/nlohmann/detail/view/lookup.hpp
+++ b/include/nlohmann/detail/view/lookup.hpp
@@ -182,7 +182,7 @@ const node* element_at(const document_data& d, const node* array, std::size_t id
template
const node* last_child(const document_data& d, const node* container) noexcept
{
- const std::size_t value_offset = container->kind == static_cast(value_t::object) ? 1 : 0;
+ const std::size_t value_offset = container->kind == node_kind::object ? 1 : 0;
const node* const end = navigation::end(d, container);
const node* last = navigation::first(d, container);
if (value_offset == 0 && container->next == container->len + 1u && (!navigation::editable() || (container->flags & node_flags::moved) == 0))
diff --git a/include/nlohmann/detail/view/materialize.hpp b/include/nlohmann/detail/view/materialize.hpp
index 62adb30a9..1bab0e896 100644
--- a/include/nlohmann/detail/view/materialize.hpp
+++ b/include/nlohmann/detail/view/materialize.hpp
@@ -101,7 +101,7 @@ BasicJsonType materialize(const document_data& d, const node* n)
case value_t::object:
case value_t::array:
{
- const bool object = n->kind == static_cast(value_t::object);
+ const bool object = n->kind == node_kind::object;
if (object)
{
sax.start_object(n->len);
diff --git a/include/nlohmann/detail/view/node.hpp b/include/nlohmann/detail/view/node.hpp
index 7a05c8e0a..8987a8860 100644
--- a/include/nlohmann/detail/view/node.hpp
+++ b/include/nlohmann/detail/view/node.hpp
@@ -41,9 +41,22 @@ struct node_flags
static constexpr std::uint8_t linked = 32; ///< an entry of a moved sequence links to this value (editable documents): its extent in the parsed layout no longer matters
};
-/// kind of 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)
-constexpr std::uint8_t kind_link = 10;
+/// the kind of a node: the value_t of its value, or link
+enum class node_kind : std::uint8_t
+{
+ null = static_cast(value_t::null),
+ object = static_cast(value_t::object),
+ array = static_cast(value_t::array),
+ string = static_cast(value_t::string), // NOLINT(build/include_what_you_use): an enumerator, not std::string
+ boolean = static_cast(value_t::boolean),
+ number_integer = static_cast(value_t::number_integer),
+ number_unsigned = static_cast(value_t::number_unsigned),
+ number_float = static_cast(value_t::number_float),
+ /// an entry of an edited sequence that stands for a value stored elsewhere
+ /// (the address of the value's node is kept in the len/next bytes); placed
+ /// after value_t::discarded, which no node ever has
+ link = static_cast(value_t::discarded) + 1,
+};
/// 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
@@ -51,7 +64,7 @@ constexpr std::uint8_t kind_link = 10;
/// always the next one, and the token length follows from `extra`).
struct node
{
- std::uint8_t kind; ///< value_t, or kind_link
+ node_kind kind; ///< the kind of the value, or link
std::uint8_t flags; ///< node_flags
std::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
@@ -60,13 +73,13 @@ struct node
};
static_assert(sizeof(node) == 16, "node must stay 16 bytes");
-// the node kinds are value_t values; the test below (object and array are 1
-// and 2) and those of the number kinds depend on this numbering
-static_assert(static_cast(value_t::null) == 0 && static_cast(value_t::object) == 1
- && static_cast(value_t::array) == 2 && static_cast(value_t::string) == 3
- && static_cast(value_t::boolean) == 4 && static_cast(value_t::number_integer) == 5
- && static_cast(value_t::number_unsigned) == 6 && static_cast(value_t::number_float) == 7,
- "the node format depends on the numbering of value_t");
+// images store the node kinds as bytes, and the test below (object and array
+// are 1 and 2) and those of the number kinds depend on this numbering
+static_assert(static_cast(node_kind::null) == 0 && static_cast(node_kind::object) == 1
+ && static_cast(node_kind::array) == 2 && static_cast(node_kind::string) == 3
+ && static_cast(node_kind::boolean) == 4 && static_cast(node_kind::number_integer) == 5
+ && static_cast(node_kind::number_unsigned) == 6 && static_cast(node_kind::number_float) == 7,
+ "the node format depends on the numbering of node_kind");
NLOHMANN_VIEW_ALWAYS_INLINE bool is_container(const node& n) noexcept
{
@@ -86,7 +99,7 @@ inline void make_link(node& n, node* target) noexcept
{
target->flags = static_cast(target->flags | node_flags::linked);
n = node{};
- n.kind = kind_link;
+ n.kind = node_kind::link;
std::memcpy(reinterpret_cast(&n) + 8, static_cast(&target), sizeof(const node*)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
}
@@ -116,8 +129,8 @@ NLOHMANN_VIEW_ALWAYS_INLINE void set_integer_bits(node& n, std::uint64_t v) noex
/// token length of a number node
NLOHMANN_VIEW_ALWAYS_INLINE std::uint32_t number_length(const node& n) noexcept
{
- return n.kind == static_cast(value_t::number_float) ? n.len
- : (n.extra & 0xFFu) + (n.kind == static_cast(value_t::number_integer) ? 1u : 0u);
+ return n.kind == node_kind::number_float ? n.len
+ : (n.extra & 0xFFu) + (n.kind == node_kind::number_integer ? 1u : 0u);
}
/// estimated number of nodes for an input of `size` bytes (one node per ~12
diff --git a/include/nlohmann/detail/view/serializer.hpp b/include/nlohmann/detail/view/serializer.hpp
index 76df8f89b..b41ac683f 100644
--- a/include/nlohmann/detail/view/serializer.hpp
+++ b/include/nlohmann/detail/view/serializer.hpp
@@ -874,7 +874,7 @@ class view_serializer
case value_t::object:
case value_t::array:
{
- const bool object = n->kind == static_cast(value_t::object);
+ const bool object = n->kind == node_kind::object;
room(8);
if (n->len == 0)
{
diff --git a/include/nlohmann/detail/view/view_mode.hpp b/include/nlohmann/detail/view/view_mode.hpp
new file mode 100644
index 000000000..3aeccd0b9
--- /dev/null
+++ b/include/nlohmann/detail/view/view_mode.hpp
@@ -0,0 +1,52 @@
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include // uint8_t
+#include // false_type, true_type
+
+#include
+
+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
+class basic_json_view;
+
+namespace detail
+{
+namespace view
+{
+
+/// whether two view modes differ
+template
+struct is_other_mode : std::true_type {};
+
+template
+struct is_other_mode : std::false_type {};
+
+/// whether a view of mode From converts implicitly to one of mode To: a
+/// read-only document has no edits, so a view of it is read correctly as a
+/// view of an editable document that has not been edited yet
+template
+struct is_widening_mode : std::false_type {};
+
+template<>
+struct is_widening_mode : std::true_type {};
+
+} // namespace view
+} // namespace detail
+
+NLOHMANN_JSON_NAMESPACE_END
diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp
index 85026008a..42a8cb99e 100644
--- a/include/nlohmann/json_view.hpp
+++ b/include/nlohmann/json_view.hpp
@@ -84,6 +84,7 @@
#include
#include
#include
+#include
NLOHMANN_JSON_NAMESPACE_BEGIN
@@ -93,13 +94,16 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
Trivially copyable (two pointers). Valid as long as the document is alive and
has not been re-parsed, and as long as a borrowed source text is alive.
*/
-template
+template
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 document_data = detail::view::document_data;
/// how the index is walked (with edits only for editable documents)
- using navigation = detail::view::navigation;
+ using navigation = detail::view::navigation;
public:
using value_t = detail::value_t;
@@ -120,6 +124,15 @@ class basic_json_view
/// an invalid view (type() == value_t::discarded)
basic_json_view() noexcept = default;
+ /// a view of a read-only document as a view of an editable one, so that a
+ /// function taking an editable view accepts both (a read-only document has
+ /// no edits; the view reads it as an editable document before its first
+ /// edit, at the cost of one check per access)
+ template::value, int> = 0>
+ basic_json_view(const basic_json_view& other) noexcept // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
+ : m_doc(other.m_doc), m_node(other.m_node)
+ {}
+
//////////
// type //
//////////
@@ -446,7 +459,7 @@ class basic_json_view
if (is_structured() && m_node->len != 0)
{
return unedited() ? basic_json_view(m_doc, detail::view::last_child(*m_doc, m_node) + (is_object() ? 1 : 0))
- : basic_json_view(m_doc, navigation::value(detail::view::last_child(*m_doc, m_node) + (is_object() ? 1 : 0)));
+ : basic_json_view(m_doc, navigation::value(detail::view::last_child(*m_doc, m_node) + (is_object() ? 1 : 0)));
}
return front();
}
@@ -504,7 +517,7 @@ class basic_json_view
/// more than once); 0 if this is not an object
size_type count(string_view_t key) const
{
- return is_object() ? detail::view::count_members(*m_doc, m_node, key.data(), key.size()) : 0;
+ return is_object() ? detail::view::count_members(*m_doc, m_node, key.data(), key.size()) : 0;
}
size_type count(const char* key) const
@@ -646,7 +659,7 @@ class basic_json_view
}
else
{
- detail::view::view_serializer(*m_doc, out, estimate, style).dump(m_node);
+ detail::view::view_serializer(*m_doc, out, estimate, style).dump(m_node);
}
return out;
}
@@ -682,14 +695,14 @@ class basic_json_view
}
/// a view of an editable document compares with one of a read-only document
- template < bool E, typename std::enable_if < E != Editable, int >::type = 0 >
- friend bool operator==(const basic_json_view& a, const basic_json_view& b)
+ template::value, int> = 0>
+ friend bool operator==(const basic_json_view& a, const basic_json_view& b)
{
- return detail::view::equal(side(a), detail::view::view_side>(b));
+ return detail::view::equal(side(a), detail::view::view_side>(b));
}
- template < bool E, typename std::enable_if < E != Editable, int >::type = 0 >
- friend bool operator!=(const basic_json_view& a, const basic_json_view& b)
+ template::value, int> = 0>
+ friend bool operator!=(const basic_json_view& a, const basic_json_view& b)
{
return !(a == b);
}
@@ -727,7 +740,7 @@ class basic_json_view
{
return BasicJsonType(value_t::discarded);
}
- return unedited() ? detail::view::materialize(*m_doc, m_node) : detail::view::materialize(*m_doc, m_node);
+ return unedited() ? detail::view::materialize(*m_doc, m_node) : detail::view::materialize(*m_doc, m_node);
}
/// byte offset of this value in the source text (for strings: of the
@@ -740,8 +753,8 @@ class basic_json_view
}
private:
- template friend class basic_json_document;
- template friend class basic_json_view;
+ template friend class basic_json_document;
+ template friend class basic_json_view;
template friend class detail::view::editor;
friend iterator;
@@ -757,11 +770,11 @@ class basic_json_view
}
/// the template argument as a runtime condition: testing it directly in
- /// `Editable && ...` makes the condition constant, which MSVC reports as
+ /// `is_editable && ...` makes the condition constant, which MSVC reports as
/// C4127
static bool editable() noexcept
{
- return Editable;
+ return is_editable;
}
/// the number of source bytes of this value (estimated for values with
@@ -810,7 +823,7 @@ class basic_json_view
const node* const k = detail::view::find_member(*m_doc, m_node, key.data(), key.size());
return k != nullptr ? basic_json_view(m_doc, k + 1) : basic_json_view();
}
- const node* const k = detail::view::find_member(*m_doc, m_node, key.data(), key.size());
+ const node* const k = detail::view::find_member(*m_doc, m_node, key.data(), key.size());
return k != nullptr ? basic_json_view(m_doc, navigation::value(k + 1)) : basic_json_view();
}
@@ -824,7 +837,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
NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key) const
{
- return find_key(key, std::integral_constant {});
+ return find_key(key, std::integral_constant {});
}
NLOHMANN_VIEW_ALWAYS_INLINE const node* find_key(string_view_t key, std::true_type /*editable*/) const
@@ -846,7 +859,7 @@ class basic_json_view
{
return basic_json_view(m_doc, detail::view::element_at(*m_doc, m_node, idx));
}
- return basic_json_view(m_doc, navigation::value(detail::view::element_at(*m_doc, m_node, idx)));
+ return basic_json_view(m_doc, navigation::value(detail::view::element_at(*m_doc, m_node, idx)));
}
// --- get() dispatch ---
@@ -952,16 +965,19 @@ Borrowed parses keep a pointer to the caller's text, which must outlive the
document. Owned parses (parse_copy, rvalue std::string, streams, and inputs
that are not contiguous byte ranges) keep their own copy.
*/
-template
+template
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;
static_assert(sizeof(typename BasicJsonType::number_integer_t) == 8 && sizeof(typename BasicJsonType::number_unsigned_t) == 8,
"json_view supports 64-bit integer types only");
public:
- using view_type = basic_json_view;
+ using view_type = basic_json_view;
using value_t = detail::value_t;
/// an empty (discarded) document
@@ -1357,7 +1373,7 @@ class basic_json_document
detail::view::editor editor()
{
- static_assert(Editable, "only an editable document can be edited: use basic_json_document