Commit Graph

5228 Commits

Author SHA1 Message Date
Niels Lohmann
3c5c000408 Address the clang-tidy findings of the structural edit tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:18 +02:00
Niels Lohmann
1e212bc50c Benchmark editing documents with json_view, yyjson, Boost.JSON, and json
bench_edit.cpp joins the comparison: parse, apply the same logical edits
with each library's own API (a handful at fixed places, or one in every
record), and serialize; all outputs must describe the same value.
compare.py builds and runs it with the other two programs.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:17 +02:00
Niels Lohmann
5d25f863c0 Add insert() and erase() to editable documents
- insert(array, index, value): insert before an element (index <= size)
- erase(object, key): remove all members with the key; returns their
  number
- erase(array, index): remove an element
- erase(json_pointer): remove the member or element a pointer names

The errors are those of basic_json (type_error.307/309,
out_of_range.401/403/405). A view of an erased value keeps its last value,
and views of other values keep referring to them when elements move.

Tests: the differential test now also inserts and erases members and
elements, directly and through JSON pointers; plus the errors, views
across inserts and erasures, duplicate keys, and large objects.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:16 +02:00
Niels Lohmann
ada8f17230 Document how editable json_documents use the node index
The node index section on the architecture page says how edits are kept
(the edit buffer, moved elements, links, and new nodes), and the feature
page links to it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:15 +02:00
Niels Lohmann
266c2249fe Cover the edit storage in tests
Test edits of an empty document and assignments through a view of a
value that is no longer part of the document; copy the entries of a
block through deref() (one path for links and values); mark the
4 GiB limit and the returns of find_parent() that no document reaches.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:15 +02:00
Niels Lohmann
2e5419eebc Label the pages and examples of the editable aliases
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:14 +02:00
Niels Lohmann
7e577fb2d4 Document editable json_documents
- API pages for set and push_back of basic_json_document and for the
  editable aliases; the class overviews name the Editable parameter
- type_error.319 on the exceptions page
- the feature page explains editing, and the examples show when it
  helps: a configuration file changed without reformatting it

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:13 +02:00
Niels Lohmann
baf4ccfda8 Address the clang-tidy findings of the editable documents
Pick the overloads of encode() with a first_true trait instead of
nested conditionals, name the pointer type in the copies of links,
mark the owning pointers of the edit storage, and compare doubles
by their bits in the tests.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:11 +02:00
Niels Lohmann
76897a9b9b Add editable documents with set() and push_back()
basic_json_document<BasicJsonType, true> (json_editable_document,
ordered_json_editable_document) can be edited:

- set(view, value): replace a value
- set(object, key, value): assign a member, or add it (a null becomes an
  object); with duplicate keys, the first is assigned and the others go
- set(array, index, value): assign an element
- set(json_pointer, value): the member, element, or ("-", or the size of
  the array) the end of an array a pointer names
- push_back(array, value): append (a null becomes an array)

Values are views (of any document, copied), BasicJsonType values, and
everything BasicJsonType can be constructed from. The source text is never
written, and the parsed index never moves: new values and element
sequences go to storage owned by the document (edit_storage.hpp), so views
stay valid, and a view keeps referring to its value (after an assignment,
it sees the new one). Read-only documents are unchanged; editing one does
not compile.

Errors are those of basic_json where the operation corresponds
(type_error.305/308, out_of_range.401/403/405, parse_error.106/109); a view
of another document is invalid_iterator.202. Strings are checked for UTF-8
when they enter the document, with the type_error.316 that
basic_json::dump() throws for the same string, so that a document only
holds valid UTF-8. Binary values cannot be stored (the new
type_error.319), and edits of 4 GiB or more end with out_of_range.416.
Views of editable and read-only documents compare with each other.

Tests (unit-json_view_edit.cpp): random assignments, member and element
changes, copies within and between documents, and pushes, applied to an
ordered_json_editable_document and to the ordered_json value; after every
edit both must serialize (also indented and with ensure_ascii),
materialize, compare, and read back the same. Further: the errors, strings
that stay valid while the edit arena grows, numbers (NaN, infinities,
extremes; number_format::source), nulls that become containers, the root
replaced, duplicate keys, values of other documents, large objects, and
documents reused with read().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:11 +02:00
Niels Lohmann
2b1a720fc0 Walk the index of json_view through a navigation policy
Preparation for editable documents, without a change in behavior: views and
documents get a template parameter Editable (false by default), and every
walk over the index (iterators, lookups, dump(), materialize()) goes
through detail::view::navigation<Editable>. For read-only documents it is
the plain node array, as before, so they compile without any of the edit
handling. For editable documents it also follows the representation of
edits, which this commit defines:

- node flags `edited` (a string or number token in the edit arena),
  `moved` (the elements of an array/object live in a separate sequence),
  and `is_new` (no source position), and link nodes (kind_link) that
  stand for a value stored elsewhere
- document_data::edit_state: the moved sequences, the storage of new
  values, and the edit arena

materialize() now keeps a frame per open container instead of returning
to the end of a closed one, as the serializer does, so that it can
follow moved sequences. Floats whose token lives in the edit arena (also
"nan", "inf", "-inf") are converted out of line. dump() copies only
strings of the source without escaping, and shrink_to_fit() leaves the
node array in place once there are edits, as they link into it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:10 +02:00
Niels Lohmann
95c2fe08a6 Document the hash index of json_view on the architecture page
The node index section says what extra holds for objects and how large
objects are indexed.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:09 +02:00
Niels Lohmann
0d59ae5d49 Address the clang-tidy findings of the object index tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:08 +02:00
Niels Lohmann
61b104606d Index the large objects of json_view
Lookups in objects are linear, as for ordered_json. Objects with 128
members or more now get a hash table after parsing (open addressing; the
first of duplicate keys is kept, as for the linear search), so that
operator[], at(), find(), contains(), count(), value(), and JSON pointers
take constant time on average in them; the idea of switching to a hash
table for large objects is Boost.JSON's. The parser notes such objects when
it closes them (out of line, so that the parse loop only has a call for
it), and the object node keeps the number of its table.

Looking up each key of an object with 10,000 members: 59.8 ms -> 0.16 ms.
Parsing (json_document::parse, best of 7, separate processes): most files
within 1%; canada +5%, mesh.pretty +3%, citm +3%.

Tests: objects with 127, 128, 129, and 10,000 members (escaped, empty,
and duplicate keys, missing keys, comparisons), nested large objects, and
documents reused with read().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:07 +02:00
Niels Lohmann
57b5d56522 Address the clang-tidy findings of the SIMD scan
Hold the UTF-8 lookup tables in std::array, compute the length of a
sequence without nested conditionals, and use std::array in the tests.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:06 +02:00
Niels Lohmann
ebfbbf29cb Scan the strings of json_view with NEON and SSE2
Long runs of string bytes are scanned 16 at a time with NEON (AArch64, with
GCC and Clang) and SSE2 (x86-64): both belong to the baseline instruction
sets. A signed compare with 0x20 finds control characters and non-ASCII
bytes at once. Keys keep 16 table checks before the vector loop (their
lengths repeat from record to record, so the branches predict well);
string values have 8, as their lengths vary more.

Non-ASCII text is validated 16 bytes at a time with the "lookup4" check of
simdjson (J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One
Instruction Per Byte", 2021): with NEON, and on x86-64 with SSSE3 if
JSON_VIEW_USE_SSSE3 is defined (SSSE3 is not part of x86-64, and the code
must not depend on the flags of a translation unit). JSON_VIEW_NO_SIMD
selects the portable code. The vector code sits in
detail/view/simd.hpp; the same input is accepted either way.

json_document::parse, best of 7 runs in separate processes (M1 Max):
poet.json (CJK text) -72%, random.json -25%, twitter.json -22%,
gsoc-2018.json -20%, semanticscholar -19%, github_events -11%,
apache_builds -9.5%, canada/citm -5/-6%; lottie +4%, tree-pretty +2.5%.

Tests: every two-byte sequence and three- and four-byte sequences with
continuation bytes at the edges of their ranges, at every offset around
the vector blocks of keys and values, cut short, and long runs of text
with a damaged byte, against json::accept and json::parse. CMake builds
the parser tests again with JSON_VIEW_NO_SIMD, and on x86-64 with
JSON_VIEW_USE_SSSE3 and -mssse3; the macros are documented.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:06 +02:00
Niels Lohmann
a491cc7663 Run the json_view comparison on GitHub runners on demand
A workflow runs compare.py with pinned downloads on GitHub-hosted
Ubuntu runners and shows the results as the job summary and as an
artifact: started by hand (workflow_dispatch: x86-64 or AArch64, GCC or
Clang), or when a pull request gets the label "benchmark" (both
architectures, GCC). The label trigger gives numbers before the
workflow is on the default branch, which workflow_dispatch needs.
Shared runners are noisy, so the numbers show where json_view stands on
another architecture; published numbers still need a quiet machine.

compare.py takes the CPU name from lscpu where /proc/cpuinfo has none
(AArch64 Linux), and falls back to the architecture. Checked in Linux
containers (AArch64, Clang 15 and GCC 9, offline with the pinned
archives).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:05 +02:00
Niels Lohmann
6536c1b869 Pin the downloads of the json_view comparison
compare.py --download now checks the SHA-256 of the yyjson 0.13.0,
simdjson 4.6.11, and Boost 1.92.0 archives. It unpacks each archive
once (Boost's directory is boost_1_92_0) and, where Python supports it,
with the 'data' filter.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:04 +02:00
Niels Lohmann
038f448dec Compare json_view with yyjson, simdjson, and Boost.JSON
tests/benchmarks/json_view/ holds the comparison with other libraries,
which is not built by CMake or run by CI:

- bench_view.cpp: parse, traverse, select, and dump of twitter,
  citm_catalog, canada, jeopardy, a single tweet, and a JSON-RPC request,
  with json_view, yyjson, simdjson (DOM and On-Demand), Boost.JSON, and
  json::parse; all engines must agree on every document before anything
  is timed, and run interleaved in every round
- bench_corpus.cpp: parse, traverse, and dump of any list of files
- compare.py: builds both against include/ with the libraries of the
  system (or pinned downloads), runs them, and writes the results with
  what is needed to reproduce them (date, commit, CPU, OS, compiler,
  flags, library versions) to results/<date>-<host>.md and .csv; only the
  Python 3 standard library is used
- README.md: how to run it, what is measured, and which features the
  engines have, so the numbers can be read correctly

Boost.JSON is optional (JSON_VIEW_BENCH_BOOST).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:03 +02:00
Niels Lohmann
bf6b5b4719 Convert the floats of json_view from the digit layout
The parser records where the integer digits, the fraction digits, and the
exponent of a float token are. For doubles with at most 19 digits, the
value is now read from that layout: the digits eight at a time, without
scanning the token, and rounded with Clinger's fast path where both
operands are exact, else with the Eisel-Lemire algorithm (which needs no
fallback for up to 19 digits). Both round correctly, so the values are
those of parse(); other tokens and types keep the library's conversion.

get<double>(), materialize(), dump(), and comparisons use it. Traversing
canada.json (111,000 floats, every number converted): 0.53 -> 0.86 GB/s.

Tests add tokens around the limits (19 and 20 digits, 2^53, 10^22) to the
bit-for-bit comparison with parse().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:02 +02:00
Niels Lohmann
1c7041908d Address the cpplint findings of json_view's comparisons
compare.hpp includes <string> (build/include_what_you_use).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:01 +02:00
Niels Lohmann
20fd4c6a8b Address the clang-tidy findings of the comparisons
Separate the comparison of discarded values from the other types, so
that the conditional chain has no repeated branch bodies, and mark
the deliberate comparisons of views with empty containers in the
tests.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:01 +02:00
Niels Lohmann
f57de1aa0a Document the comparisons of json_view
- API pages for operator== and operator!= of basic_json_view, linked
  both ways with the basic_json pages
- the feature page and the class overview list the comparisons
- the examples show when the view helps: detecting a changed document
  without building json values

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:03:00 +02:00
Niels Lohmann
401af52511 Add comparisons to json_view
basic_json_view gains operator== and operator!= with other views and with
basic_json values. Two views are equal if the values parse() would
produce for them are equal by basic_json's operator==: numbers compare by
value across their types, and objects by their members, with duplicate
keys resolved as parse() resolves them (the last value, at the position of
the first key). Objects are compared in member order if the object type
keeps an order (ordered_json), by key otherwise, as basic_json does.
Discarded views compare as discarded basic_json values do, which follows
JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON. Nothing is materialized except
single numbers, and the walk is iterative.

Tests compare the results for pairs of 1,200 generated documents (also
written differently: sorted keys, canonical numbers) with those of
basic_json, for json and ordered_json, plus numbers, duplicate keys,
member order, discarded values, and 100,000 levels of nesting.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:59 +02:00
Niels Lohmann
ecce914701 Mark the cases of the view's serializer that tests cannot reach
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:58 +02:00
Niels Lohmann
1318023103 Address the clang-tidy findings of dump()
The output buffer initializes its members in the initializer list, and the
escaping has no nested conditional operators; the test marks a fixed seed.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:57 +02:00
Niels Lohmann
0192171c37 Document dump() of json_view
- API pages for dump, number_format, and operator<< of basic_json_view,
  linked both ways with the basic_json pages
- the feature page describes document order and number_format::source
- the examples show when the view helps: forwarding part of a message
  and writing numbers exactly as they were read

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:56 +02:00
Niels Lohmann
1ef0582ed7 Add dump() to json_view
basic_json_view::dump(indent, indent_char, ensure_ascii, number_format)
writes the text of a value as ordered_json::parse(text).dump() writes it
for the same arguments: members in document order (all of them, should a
key occur more than once), strings escaped by the same rules and with the
library's scanning kernels, floats with the library's conversion, and
integers copied from the source, where they are canonical except "-0".
With number_format::source, numbers are copied as they appear in the
source ("1.50", "1E2", "-0", all digits of long integers). operator<<
takes the indentation from the stream width, as for basic_json.

The writer (detail/view/serializer.hpp) writes through a raw pointer into
a string sized from the source extent of the value, and walks the index
iteratively, so the nesting depth is limited by memory only.

Tests compare the output of 2,000 generated documents with
ordered_json::dump() for several indentations and ensure_ascii, strings
with every kind of escape, numbers (5,000 random doubles, float as
number_float_t), duplicate keys, 100,000 levels of nesting, and streams.
ViewDump joins the benchmarks.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:56 +02:00
Niels Lohmann
2aedf52d54 Inline get() of arithmetic values of json_view
get<T>() of arithmetic types is inlined down to the conversion, so that
its checks of the node kind merge with those of the caller, and reading
an integer needs no call. Traversing every value: citm_catalog -6%,
marine_ik -5%, numbers and twitter -3%, mesh -2.5%, canada -1% (and
more above the float conversion from the digit layout: citm_catalog
-14%, marine_ik -11%).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:55 +02:00
Niels Lohmann
bc36fd323b Address the clang-tidy findings of values and JSON pointers
get_string() and number_token() return braced lists; the test compares
floats by their bit patterns instead of with memcmp, uses std::any_of, and
marks a fixed seed, a default member initializer (needed by GCC's
-Weffc++), and a string search.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:54 +02:00
Niels Lohmann
9856da2561 Document values and JSON pointers of json_view
- API pages for get, get_to, get_string, number_token, and value of
  basic_json_view; JSON pointer overloads of operator[], at, and
  contains; links both ways with the basic_json pages
- the feature page describes which conversions copy nothing
- the examples show when the view helps: strings without copies, numbers
  exactly as written, and paths into a large text

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:53 +02:00
Niels Lohmann
85f414ae7a Add values and JSON pointers to json_view
basic_json_view gains get<T>(), get_to(), value() with keys and JSON
pointers, and operator[], at(), and contains() with JSON pointers, plus
two functions basic_json has no counterpart for:

- get_string(): the string without a copy (a string_view into the source,
  or into the decoded strings for strings with escapes)
- number_token(): the text of a number as it appears in the source

get<T>() converts arithmetic types, strings (also string_view_t),
std::nullptr_t, std::vector, maps with string keys, and views directly;
floats are converted from the digit layout recorded by the parser with the
library's conversion chain, so the values are bit-identical to parse().
Other types, including user types with from_json(), go through
materialize().

The exceptions are those of basic_json, message included. Where const
basic_json has undefined behavior (a missing key or an index out of range
with operator[] and a JSON pointer), the result is a discarded view;
value() returns the default wherever basic_json catches out_of_range, and
contains() never throws. Array indices of JSON pointers follow
json_pointer's rules (parse_error.106/109, out_of_range.404/410).

Tests compare the conversions of 2,000 generated documents, 20,000 float
tokens (double and float, bit for bit), and every JSON pointer of 1,000
documents with basic_json, and the exceptions for malformed pointers.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:52 +02:00
Niels Lohmann
358970ee7f Give code outside basic_json the reference tokens of a json_pointer
detail::json_pointer_access returns the reference tokens of a pointer, so
that code resolving pointers without a basic_json value (such as the
zero-copy view) does not have to parse to_string() again. No change in
behavior.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:51 +02:00
Niels Lohmann
daad8ea9b5 Address the clang-tidy findings of element access and iteration
Marks the default initializer of the item's index string (needed by GCC's
-Weffc++) and, in the test, an escaped literal and a comparison of find()
with end(), which is what the test is about.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:51 +02:00
Niels Lohmann
5a9f59a688 Document element access, lookup, and iteration of json_view
- API pages for operator[], at, front, back, find, contains, count,
  begin, end, cbegin, cend, items, and type_name of basic_json_view,
  linked both ways with the basic_json pages
- the feature page and size() describe document order and duplicate
  keys
- the examples show when the view helps: reading a few fields of a large
  text, probing optional members, and members in source order

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:50 +02:00
Niels Lohmann
f80a1a17a9 Add element access, lookup, and iteration to json_view
basic_json_view gains the read-only access functions of basic_json:
operator[] and at() with keys and indices, front(), back(), find(),
contains(), count(), begin()/end(), items() (with structured bindings from
C++17 on), and type_name(). They throw the exceptions (ids and messages)
that the const functions of basic_json throw; where basic_json has
undefined behavior (operator[] with a missing key or an index out of
range, front()/back() of an empty container), the view returns a
discarded view or throws invalid_iterator.214.

Objects are iterated in document order, and all members are visited. With
duplicate keys, lookups find the first member, so that a lookup can stop
at the first match; parse() keeps the last value. Keys of up to 16 bytes
are compared with two overlapping loads instead of memcmp, and most keys
are rejected by their length alone, from the index.

The iterators and items live in detail/view/iterator.hpp, the lookups in
detail/view/lookup.hpp. Tests compare every element and member of 2,000
generated documents with ordered_json, keys of every length around the
load sizes, the exceptions against const basic_json, and the iterators.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:49 +02:00
Niels Lohmann
857953b39a Name JSON types without a basic_json value
basic_json::type_name() now calls detail::value_type_name(value_t), so
that code which reports types without a basic_json value at hand, such as
the zero-copy view, uses the same names in its exception messages. No
change in behavior.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:48 +02:00
Niels Lohmann
1b6a58a17e Document the node index of json_view on the architecture page
A new section describes the 16-byte node: its fields, how integers,
floats, and object members are stored, how views navigate without
pointers, and a worked example. The feature page and the pages of
basic_json_document and node_count link to it where they mention the
16 bytes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:47 +02:00
Niels Lohmann
0b04e36106 Address the cpplint findings of json_view's materialize()
materialize.hpp includes <string> (build/include_what_you_use).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:47 +02:00
Niels Lohmann
207fc01576 Mark the code of json_view that tests cannot reach
The 4 GiB limit and the fallback for an input that parse() accepts but
the view rejects (a bug) are excluded from the coverage; shrink_to_fit()
of an empty document is tested.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:46 +02:00
Niels Lohmann
aa1a0c7561 Address the clang-tidy findings of json_document and json_view
- the input dispatch takes byte ranges by const reference and reads the
  size once (which also settles a finding of the static analyzer); input
  adapters are taken by value
- the classification of inputs keeps its nested conditional operators, a
  constant expression of C++11 (NOLINT)
- the test's C arrays, fixed seed, and escaped literals are marked, as in
  the other tests

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:45 +02:00
Niels Lohmann
c8ee344008 Benchmark json_document
benchmarks_view.cpp adds ViewParse, ViewRead (a reused document),
ViewParseIndented, ViewAccept, and ViewMaterialize on the files of
ParseString, so that each row can be read against the json::parse row of
the same file; benchmarks.cpp gains Accept (json::accept) as the
counterpart of ViewAccept. The view benchmarks are built only if the
header directory has json_view.hpp, so that older versions can still be
benchmarked.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:44 +02:00
Niels Lohmann
54455afa40 Document json_document and json_view
- API pages for basic_json_document and basic_json_view, one per member,
  and for the four aliases, each with an example
- features/json_view.md: the problem the view solves, ownership and
  lifetime, what matches basic_json::parse() and what differs, and when
  to choose json, ordered_json, SAX, or the view
- the examples show why one would use the view, not only how: borrowed
  vs. owned input, reading a few fields and materializing one subtree,
  reusing a document across many messages
- registered in the mkdocs navigation, llms.txt, the docset, the
  exceptions page (out_of_range.416), architecture.md, the integration
  page, and the README; the yyjson credit is added to the README and
  license.md

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 14:02:43 +02:00
Niels Lohmann
7db6b3340d Add a fuzzer for json_document
fuzzer-parse_json_view.cpp checks for every input that
json_document::accept agrees with json::accept, that an accepted input
materializes to the value json::parse returns, and that a rejected input
makes both throw the same exception with the same message. It is built
like the other fuzzers (tests/Makefile, and the root Makefile's
fuzz_testing_json_view target, which starts from the JSON test corpus) and
listed in tests/fuzzing.md.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:54:08 +02:00
Niels Lohmann
f5f8b19364 Export json_document and json_view from the C++20 module
The nlohmann.json module includes json_view.hpp in its global module
fragment and exports basic_json_document, basic_json_view, and the four
aliases next to basic_json, json, and ordered_json. features/modules.md
lists them, and tests/module_cpp20 parses a document, so that a missing
export fails the ci_module_cpp20 job.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:54:07 +02:00
Niels Lohmann
d2a4398d5a Build, install, and check json_view.hpp
The new single header goes through the same checks and install steps as
json.hpp and json_fwd.hpp:

- cmake/ci.cmake: ci_test_amalgamation regenerates, formats, and compares
  json_view.hpp as well
- check_amalgamation.yml: the pull request check does the same; it runs
  develop's tools, so it needs config_json_view.json on develop first
- meson.build: installs single_include/nlohmann/json_view.hpp
- gen_bazel_build_file.cmake, BUILD.bazel: json_view.hpp joins the
  single-header target; the glob of the other target already covers the
  new headers
- labeler.yml: an "aspect: json_view" label for the header, its
  detail/view headers, tests, and documentation

The CMake install rules for include/ and single_include/, the REUSE
catch-all, and Package.swift cover the new files without changes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:54:06 +02:00
Niels Lohmann
7b30f0acb2 Test json_document and json_view
unit-json_view.cpp: type queries, size, and empty against basic_json;
materialize() against parse() (json and ordered_json, generated documents,
duplicate keys, 100,000 levels of nesting, parent pointers with
JSON_DIAGNOSTICS); parse errors and their messages equal to parse() for
malformed inputs and all option combinations; NUL and BOM; borrowed and
owned inputs (strings, C strings, literals, vectors, string_view, streams,
wide strings, parse_copy, and iterator ranges over pointers, vectors,
strings, and lists); reuse with read(); moves; shrink_to_fit() of the index
and of the decoded strings; source offsets.

unit-json_view_macros.cpp includes the header without
JSON_TEST_KEEP_MACROS, as users do: the view must not depend on the macros
json.hpp undefines, and must not leak its own.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:54:05 +02:00
Niels Lohmann
be837d5470 Add json_document and json_view: parse, accept, types, materialize
The public classes of the zero-copy view (#5295), in the new header
<nlohmann/json_view.hpp>:

- basic_json_document<BasicJsonType>: parse (borrowing contiguous byte
  inputs, owning rvalue strings, streams, and other inputs), parse_copy,
  accept, read, root, is_discarded, source, owns_source, node_count,
  memory_usage, shrink_to_fit
- basic_json_view<BasicJsonType>: type and the is_* queries, size, empty,
  materialize (the value parse() would produce, built by the same SAX
  handler), source_offset
- the aliases json_document, json_view, ordered_json_document, and
  ordered_json_view

A parse error throws the exception basic_json::parse would throw for the
same input: the library parser is run on the failing input, so messages,
positions, and exception ids are the same. Inputs of 4 GiB or more are
rejected with out_of_range.416.

The single header single_include/nlohmann/json_view.hpp keeps including
json.hpp; make amalgamate, check-amalgamation, include.zip, and release
handle it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:54:04 +02:00
Niels Lohmann
27468fa77e Test the C++11 stand-in for std::string_view of json_view
Six members of string_ref (length, begin, end, operator[], operator!=,
and operator<<) were not reached before C++17, where string_ref is
std::string_view.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:53:48 +02:00
Niels Lohmann
a49d6e2e3b Remove the eight-digit case of the view's digit parser
Since whole blocks of eight digits are read directly, parse_upto8() only
gets fewer than eight digits; its eight-digit case was dead code.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:52:19 +02:00
Niels Lohmann
58ac756134 Address the cpplint findings of the view's parser
The exponent of the overflow check is an std::int64_t instead of a long
(runtime/int).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:34:11 +02:00