Add json_document and json_view: node index, parser, and document

Add json_document and json_view, a read-only, zero-copy index of a
JSON text, as the first public slice of the zero-copy view (#5295).

A parse produces a flat array of 16-byte nodes in document order,
one per value and one per object key. Strings stay in the source
text; escaped strings are decoded into an arena. Integers are
converted while their digits are in the cache; floats keep only
their digit layout and are converted on read. Containers store the
size of their subtree, so a reader can step over one in constant
time. A document makes a handful of allocations, however many
values it has.

The parser accepts exactly what json::parse accepts, with every
combination of ignore_comments and ignore_trailing_commas, with and
without a trailing NUL, and under JSON_STRICT_NUL_HANDLING. It is
portable C++11 and does not depend on byte order.

basic_json_document adds parse, parse_copy, accept, read (reuses a
document's memory), root, is_discarded, source, owns_source,
node_count, memory_usage, and shrink_to_fit. basic_json_view adds
type, the is_* queries, operator bool, size, empty, materialize,
and source_offset. A parse error throws the same exception
basic_json::parse would throw for the same input, message and
position included.

detail::abi_config keeps JSON_STRICT_NUL_HANDLING and
JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON readable after json.hpp
undefines them, in the ABI namespace so they always match the
basic_json in use.

A NUL byte that ends a // comment is the end of the input, as in
parse() since #5696.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-07 16:42:22 +02:00
1 parent bfa4886f0f
commit 759dd2e1b1
146 files changed
+9611 -13

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a parser test suitable for fuzz testing. It checks that
json_document (the zero-copy, read-only view of a parsed JSON text declared in
json_view.hpp) agrees with basic_json on every input:
- json_document::accept(data) must equal json::accept(data)
- if the input is accepted, json_document::parse(data).root().materialize()
must equal json::parse(data)
- if the input is rejected, json_document::parse(data) (with exceptions
enabled) must throw a json::parse_error or json::out_of_range whose what()
is identical to the one json::parse(data) throws
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers.
*/
#include <cassert>
#include <string>
#include <nlohmann/json.hpp>
#include <nlohmann/json_view.hpp>
// the checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
using json_document = nlohmann::json_document;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
// json_document::accept only has a single-argument overload; wrap the raw
// bytes in a (borrowed) std::string so the same bytes can be handed to it
const std::string input(reinterpret_cast<const char*>(data), size); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
const bool accepted_by_json = json::accept(data, data + size);
const bool accepted_by_view = json_document::accept(input);
// json_document::accept must agree with json::accept on every input
assert(accepted_by_json == accepted_by_view);
if (accepted_by_json)
{
// both parsers must agree on the resulting value
json const j1 = json::parse(data, data + size);
json_document const doc = json_document::parse(input);
json const j2 = doc.root().materialize();
assert(j1 == j2);
}
else
{
// both parsers must reject the input the same way when exceptions are used
std::string expected_what;
bool json_threw = false;
try
{
static_cast<void>(json::parse(data, data + size));
}
catch (const json::parse_error& e)
{
expected_what = e.what();
json_threw = true;
}
catch (const json::out_of_range& e)
{
expected_what = e.what();
json_threw = true;
}
assert(json_threw);
bool view_threw = false;
try
{
static_cast<void>(json_document::parse(input));
}
catch (const json::parse_error& e)
{
assert(e.what() == expected_what);
view_threw = true;
}
catch (const json::out_of_range& e)
{
assert(e.what() == expected_what);
view_threw = true;
}
assert(view_threw);
}
// return 0 - non-zero return values are reserved for future use
return 0;
}