Compare commits

...
Author SHA1 Message Date
Niels Lohmann bf6c1f39d2 Read BSON documents without recursing per nesting level
An embedded document (record type 0x03) or array (0x04) was read by calling
back into the document reader, which read its element list, which called the
element reader again for the next embedded one. The native call stack
therefore grew with the nesting depth of the input, and about seven bytes buy
a level, so a document of a few hundred kilobytes crashes the process
(#5104). This is the last of the four binary formats to still do that.

Apply the same shape as the other three: open_bson_document() reads the size
prefix and opens the document, parse_bson_element_internal() calls it for both
record types instead of recursing, and parse_bson_internal() loops over the
element list of whichever document is innermost, closing it when its
terminator is reached and resuming the one below.

check_bson_document_size() is unchanged, and so is when it runs: a document is
still measured from the byte before its size prefix to the byte after its
terminator, and still reported before the end event. The frame carries those
two values, which is what a per-document check needs once the reads are
interleaved rather than nested. Nothing else about the element reader changes.

unit-bson passes unchanged. Round trips through to_bson of nested objects,
arrays, arrays of objects and mixed nesting are identical to the previous
commit, as are the errors for a truncated document, an unsupported record
type, a negative size and a size that does not match, including their byte
offsets. A 30,000-level document built by to_bson is now read to completion
where it used to crash.

Note for sequencing: #5185 changes parse_bson_internal(), the element list and
the array reader, which are the functions this commit restructures. It should
land first; this commit then keeps its checks and moves them onto the loop.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 15:47:12 +02:00
Niels Lohmann 1cba195509 Read UBJSON and BJData containers without recursing per nesting level
get_ubjson_array() and get_ubjson_object() read their elements by calling
back into the value reader, which called them again for a nested container,
so the native call stack grew with the nesting depth of the input. '[' alone
opens a container, so half a million of them crashes the process before the
input runs out (#5104). The optimized forms reach the same path through a
size or type annotation, and in plain UBJSON '[' and '{' are permitted as the
type of an optimized container, so "[$[#i\x01" repeated nests just as deeply
at six bytes a level.

Both readers now only open their container, and parse_ubjson_internal() loops:
it closes the containers that have ended, claims the next element of the
innermost one, reads its key when it is an object, and works out the marker
of the value to read next. That last part is where the formats differ, and
the loop follows what the four element loops used to do:

  - a sized, typed container gives its elements no marker of their own
  - a sized, untyped container reads one for each element
  - a container that ends at a marker has the byte already, from the test
    against ']' or '}'; for an object it is the first byte of the key

The ND-array wrapper and the 'B' binary shortcut stay as they are. Both read
a complete value rather than opening a container, and their elements are
always scalars: BJData does not permit '[' or '{' as an optimized type, which
is also why only plain UBJSON needed the type-marker case above.

A container of no-ops keeps its behaviour of holding no elements while still
announcing its declared size to the SAX parser, by opening it and then
setting its count to zero.

unit-ubjson and unit-bjdata pass unchanged, 1.39 million assertions between
them, and a behaviour comparison against the previous commit over every
container form -- sized, unsized, typed, untyped, empty, no-op, ND-array,
binary, and the forms nested inside one another -- gives identical values,
error codes, messages and byte offsets. 500,000 levels of each vector now
report a parse error instead of crashing, and a well-formed 100,000-level
value is read to completion.

The driver costs about 3 % on parsing 60,000 small objects and one array of a
million integers, for the reason given in the previous commit; reading the
frame once per element rather than per branch halved what it cost before.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 15:45:43 +02:00
Niels Lohmann 1df654d19d Read CBOR containers and tags without recursing per nesting level
get_cbor_array() and get_cbor_object() read their elements by calling back
into the value reader, which called them again for a nested container, and a
tag was handled by reading the tagged value the same way. All three cost
native stack, and all three cost a single byte to encode: 0x9F opens an
indefinite-length array, 0x81 a one-element array, and 0xC2 is a tag. Half a
million of any of them crashes the process before the input runs out (#5104).

Apply the shape the MessagePack reader already uses: the open containers live
on the heap stack, parse_cbor_value() reads a single value and only opens a
container rather than reading it to its end, and parse_cbor_internal() loops,
resuming the innermost container after each element.

Two things are specific to CBOR. An indefinite-length container ends at a
break marker rather than at a count, and testing for that marker consumes a
byte which is the first byte of the next element when it is not one; the
frame's count is npos for those, and the driver tracks whether the next value
starts at a fresh byte. And a tag is not a value of its own: instead of
reading the tagged value by recursing, the value reader reports that a tag was
read and the driver reads on, so a chain of tags costs no stack at all.

The switch that decodes a value is unchanged apart from the twelve container
cases and the two tag sites. Verified against the previous commit over
definite and indefinite arrays and maps, all four counted forms, empty
containers, nesting of the forms inside each other, truncated inputs, and all
three tag handlers: identical values, error codes, messages and byte offsets.
500,000 levels of each of the three vectors now report parse_error.110 instead
of crashing, and a well-formed 200,000-level value is read to completion.

On performance: the driver does per element what a counted loop used to do
per container, and CBOR pays for it more than MessagePack because the value
reader also has to be told whether to fetch a byte. Parsing 60,000 small
objects and one array of a million integers is 3 to 4 % slower than the
recursive reader, measured over five alternating runs. Against develop the
same two inputs are about 44 % faster, because the entry point no longer
copies the value it parsed; the earlier commit in this series is what pays
for that.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 15:45:33 +02:00
Niels Lohmann 90bd0f7fec Read MessagePack containers without recursing per nesting level
get_msgpack_array() and get_msgpack_object() read their elements by calling
back into parse_msgpack_internal(), which calls them again for a nested
container. The native call stack therefore grew with the nesting depth of the
input, and each level costs only one byte to encode: 0x91 is a one-element
array, so a few hundred thousand of them crash the process before any of the
input is rejected (#5104).

Keep the open containers on a heap stack instead, the way
parser::sax_parse_internal() has always done for JSON text. A frame records
how many elements are left and whether to close with end_object() or
end_array(); parse_msgpack_value() reads a single value and, for a container,
only opens it; and parse_msgpack_internal() loops, resuming the innermost
container after each element and closing it when its count runs out. Whether
the value that was begun is complete is answered by the stack being empty, so
no separate bookkeeping is needed.

The switch that decodes a value is untouched apart from the six container
cases, which now call enter_container() rather than a reader that loops. That
keeps this diff to the control flow and leaves the decoding of every other
type byte-identical.

enter_container() is the only place a binary reader emits start_object() or
start_array(), so a check that rejects a container can be added there once and
is guaranteed to run before the start event. The frame type and the stack are
shared, ready for the other three formats.

Verified against develop over empty, nested, counted (array 16/32, map 16/32)
and truncated inputs: identical values, error codes, messages and byte
offsets. 300,000 levels now report parse_error.110 instead of crashing, and a
well-formed 300,000-level value is read to completion through the SAX
interface, where develop crashes.

Reading such a value into a basic_json needs the return-by-move change as
well, without which the recursive copy constructor overflows on the way out;
that is the parent commit, and the test for the value path covers the two
together. Timing is unchanged: parsing 60,000 small objects and one array of
a million integers is within run-to-run noise of develop either way.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 14:08:26 +02:00
Niels Lohmann 3d89ac50a0 Split unit-regression2.cpp so the MinGW linker can relocate it
Linking test-regression2 with clang and MinGW fails with

    relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'

once the translation unit grows past a certain size: the code can no longer
reach the read-only data it references within the range of a 32-bit
relocation. The file is one of the largest in the test suite and had been
sitting just under that limit, so an unrelated change elsewhere in the
library is enough to tip it over. It is already the second such file --
unit-regression1.cpp was split for size before -- and windows.yml already
carries a workaround for the same limit hitting the debug sections of this
same target, where -g0 was enough because that relocation was against
`.debug_line'. This one is against `.rdata', which no compiler flag avoids.

Move the second half of the regression tests, and the helper types only they
use, into unit-regression3.cpp. The sections are independent -- every
statement in "regression tests 2" was already inside a SECTION -- so they
move unchanged, and the counts confirm nothing was lost: 168 assertions
before the split, 50 plus 118 after.

The result is that both files are comfortably smaller than the one that used
to link, measured with clang at -O1 for C++20:

                        read-only data        text     object
    before                      58,233   1,287,764  3,158,120
    unit-regression2.cpp        48,161   1,012,988  2,522,296
    unit-regression3.cpp        41,710     772,704  1,878,880

No CMake change is needed: tests/CMakeLists.txt globs src/unit-*.cpp, so the
new file is picked up and built for every standard like its siblings.

CONTRIBUTING.md pointed contributors at unit-regression2.cpp for new bug
tests; it now points at the smaller file and says why the two exist, so the
split does not quietly undo itself.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 14:06:58 +02:00
Niels Lohmann ee69490b28 Bound UBJSON optimized arrays of a valueless type
An element of type 'Z' (null), 'T' (true) or 'F' (false) is encoded by its
type marker alone, so an optimized UBJSON array of one of those has no
payload: reading an element consumes no input at all. Its declared count is
therefore the only thing that decides how much is allocated, and nothing
bounded it. "[$Z#l" and a four-byte count is nine bytes of input describing
two billion values; #2793 reports 35 GB and 150 seconds from ten bytes, and
OSS-Fuzz has an out-of-memory and a timeout report for the same shape.

Every other type costs at least one byte per element, so the end of the input
bounds it. 'N' (no-op) is already skipped rather than stored. Objects are not
affected either: each element is preceded by its key, which costs bytes. And
BJData already refuses these markers as an optimized type, so this is a plain
UBJSON matter.

Reject a count above 1,048,576 elements for those three types with
out_of_range.408, the code this reader already uses for a declared size it
will not honour. The check runs before the SAX start event, so no container
is opened and then abandoned.

Rejecting on the read side alone would break the guarantee that anything
to_ubjson() writes can be read back, and would trip the round-trip assertion
in fuzzer-parse_ubjson.cpp. So the writer falls back to the unoptimized
encoding, one byte per element, for arrays of these types above the same
limit. Its decision depends only on the array's size, which is identical for
a value and for anything parsed back from it, so the round trip is stable.

No existing test changes: the largest such count in the test suite is 65,793.
The excessive-size test that already used this shape still passes, now
rejected a little earlier than by the max_size() check it used to reach.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 07:50:29 +02:00
Niels Lohmann 3970ecccd1 Reject a nested BJData ndarray dimension vector where it is read
get_ubjson_size_type() takes an inside_ndarray parameter saying whether it is
being called for an ndarray's dimension vector, where another ndarray is not
allowed. It then seeded the flag it passes down to get_ubjson_size_value()
with `false` rather than with that parameter, and only consulted
inside_ndarray afterwards, on the '$' branch.

So on the '#' branch nothing stopped the descent: every "#[" pair of an input
like "[" followed by "#[#[#[..." opened another dimension vector, several
native stack frames deeper each time, and the recursion was only reported on
the way back out. 100,000 pairs crash the process. This is #5104 again, in a
path that has nothing to do with containers.

Seed the flag with inside_ndarray, which is what get_ubjson_size_value()
documents it wants: "for input, `true` means already inside an ndarray vector
or ndarray dimension is not allowed". The nested '[' is then refused where it
is read, so the length of the chain no longer matters.

Both post-checks gain `&& !inside_ndarray`, because an ndarray was found
*here* only if the flag flipped -- get_ubjson_size_value() only ever returns
`true` when its initial value was `false`, as its documentation says. With
that, the "ndarray can not be recursive" branch is unreachable: a recursive
ndarray is now caught one level earlier, and reported as "ndarray dimensional
vector is not allowed" like every other nested dimension vector.

Three existing expectations move accordingly (vR2, vR4, vR6). All three now
fail earlier, and all three now report the same error that vR1, vR5 and vH
already reported for the same shape, which is the more consistent outcome.
Everything else is unchanged: valid 1D and 2D ndarrays, optimized containers
and plain arrays produce identical results, and unit-ubjson is untouched.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 07:50:28 +02:00
Niels Lohmann 00e5d21041 Stop CBOR indefinite-length strings from recursing per chunk
get_cbor_string() and get_cbor_binary() handled the indefinite-length forms
(0x7F and 0x5F) by calling themselves once per chunk. Each chunk therefore
cost a native stack frame, and since a chunk may itself be an indefinite-
length string, an input of repeated 0x7F bytes reached one frame per input
byte: 200,000 of them crash the process with SIGSEGV before a single byte is
rejected. This is the same defect as #5104, in a path the container-level
work does not touch.

Count the open levels instead of recursing through them. That is enough here
because every chunk is appended to the same result -- get_bytes() writes at
result.size() -- so there is no per-level state to keep. The temporary chunk
string and its copy into the result go away with the recursion.

The definite-length cases move to get_cbor_string_chunk() and
get_cbor_binary_chunk() unchanged, including their error messages, which
still name 0x7F and 0x5F because those are handled one level up.

Behaviour is unchanged. Comparing against develop over the interesting byte
sequences -- empty, single-chunk, nested, over-closed and truncated forms,
both strings and byte arrays, and an indefinite-length map key -- produces
identical values, error codes, messages and byte offsets. The 200,000-level
input now reports parse_error.110 at byte 200001 instead of crashing.

Note that nesting these is not valid CBOR: RFC 8949, Section 3.2.3 forbids
it. This does not change that either way -- it has always been accepted, and
rejecting it is a separate decision (#5317, #5325). Should it be rejected
later, that is now one condition on the level counter rather than a change to
the control flow.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 07:50:27 +02:00
Niels Lohmann 755c547003 Return the parsed value by move from from_cbor() and friends
The binary entry points end with

    return res ? result : basic_json(value_t::discarded);

The condition operator's second operand is an lvalue, so this is not a case
where the return value can be elided or implicitly moved from: every
successful from_cbor(), from_msgpack(), from_ubjson(), from_bjdata() and
from_bson() call deep-copies the value it just parsed, and then destroys the
original.

The copy is not cheap, and it is not incidental: basic_json's copy
constructor walks the whole value. Parsing a 2 MB CBOR document with 60,000
objects, median of 25 runs, clang 17 -O3:

    from_cbor      26.99 ms  ->  14.65 ms
    from_msgpack   26.82 ms  ->  14.82 ms

Moving instead of copying is the entire change; the parsed value is not used
again after the return expression is evaluated.

There is a second reason to prefer the move. The copy constructor recurses
once per nesting level, so the copy is also a stack-overflow path on the
return side, on a value the reader has already accepted. That is currently
masked because the readers themselves recurse and overflow first (#5104), but
it has to be fixed for making them iterative to have any effect.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-07 07:50:09 +02:00
Qatadaha Bin Matloob 09b6b6b5ba Fix to_bjdata() emitting unparsable output when _ArraySize_ is not an array (#5455)
* Fix to_bjdata() emitting unparsable output when _ArraySize_ is not an array

write_bjdata_ndarray() never checked that _ArraySize_ is an array. The shape
is written verbatim as the header length, so a null shape emitted 'Z' and an
object shape emitted '{' after the '#', neither of which from_bjdata()
accepts, and the round-trip guarantee in the BJData docs was broken.

Both slipped through the existing validation: for null, empty() is true so
the element count starts at 0 and the per-dimension loop never runs, and for
an object the loop walks its values, which can satisfy the non-negative
integer check. When _ArrayData_ then matched that count, the writer took the
ndarray path.

Require the shape to be an array, so anything else falls back to a plain
object encoding that round-trips, as the fallback rule in the docs already
specifies.

Signed-off-by: qatcod <79017227+qatcod@users.noreply.github.com>

* Document that _ArraySize_ must be an array in the ndarray requirements

The list at bjdata.md is the exhaustive set of conditions for the ndarray
encoding, but it only implied this one through 'every entry of'.

Signed-off-by: qatcod <79017227+qatcod@users.noreply.github.com>

---------

Signed-off-by: qatcod <79017227+qatcod@users.noreply.github.com>
2026-09-05 20:30:22 +02:00
Niels Lohmann dd26d3ff07 docs: add 41 customers and rebuild the overview image (#5463)
* docs: add 89 customers and sort all sections

Extend the customers page from 136 to 225 entries. New entries were
found by searching vendor open-source notices and by inspecting
dependency manifests in public repositories.

Every added entry was verified to link either to a page that credits
the library, or to an open source repository where its use is directly
visible (a vendored copy, a build manifest, or an include). Candidates
whose only evidence was a transitive dependency (via ICU or
KDDockWidgets), packaging metadata, or an unused vendored file were
dropped rather than listed.

Also sort every section alphabetically ignoring case, and keep the
Peregrine lunar lander first under "Space Exploration".

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: add 41 customers and rebuild the overview image

The list was assembled by searching for names one at a time, which is why it
had plateaued. These additions come from enumerating instead: a public code
search returning every repository that references the library, and the reverse
build-dependencies of the Debian and Ubuntu source indices.

Every addition cites a first-party call site rather than a repository root,
and every evidence URL was checked to resolve. Several prominent candidates
were rejected on exactly that test:

  - Node.js reaches the library only through deps/icu-small, the inherited
    attribution ICU carries into everything that ships it.
  - Visual Studio Code's only hits are copies of the library's own headers
    used as sample C++ in the Copilot extension's test fixtures.
  - OSS-Fuzz fuzzes the library rather than calling it; projects/json/ is the
    library's own OSS-Fuzz integration.
  - libuv matched only its AUTHORS file, which lists a contributor by name.
  - simdjson and LLVM matched only benchmarks and a mangled-symbol test.

Two entries were already present under a different name and are merged rather
than duplicated: PrestoDB, and DB Browser for SQLite under its repository name.
GitHub CodeQL's citation moves from the repository root to shared/cpp/Diagnostics.h,
which includes the header directly.

The image is rebuilt with 236 logos over 17 rows. It is smaller than the one it
replaces, 1.02 MB against 1.35 MB, despite carrying 100 more marks.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-05 11:27:57 +02:00
17 changed files with 2968 additions and 1620 deletions
+3 -1
View File
@@ -108,7 +108,9 @@ The tests are located in [`tests/src/unit-*.cpp`](https://github.com/nlohmann/js
are structured along the features of the library or the nature of the tests. Usually, it should be clear from the
context which existing file needs to be extended, and only very few cases require creating new test files.
When fixing a bug, edit `unit-regression2.cpp` and add a section referencing the fixed issue.
When fixing a bug, edit `unit-regression3.cpp` and add a section referencing the fixed issue.
`unit-regression2.cpp` holds the older tests; the two files exist because a single one grew large enough for the
MinGW linker to fail relocating it, so please keep adding to the smaller file rather than growing one of them.
#### Exceptions
@@ -125,6 +125,7 @@ The library uses the following mapping from JSON values types to BJData types ac
- `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`,
`double`, `char`, or `byte`,
- `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length,
- every entry of `"_ArraySize_"` is a non-negative integer, and their product is representable as a `std::size_t`,
- `"_ArrayData_"` holds exactly that many elements, and
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"` (a floating-point number for
@@ -69,6 +69,12 @@ The library uses the following mapping from JSON values types to UBJSON types ac
Note that `use_size = true` alone may result in larger representations - the benefit of this parameter is that the
receiving side is immediately informed on the number of elements of the container.
An array whose type marker is `Z` (null), `T` (true) or `F` (false) stores no payload at all, because the marker
already is the value. Its declared count is therefore the only thing that decides how much memory the receiving side
allocates, and a handful of bytes can describe billions of elements. `from_ubjson` rejects such an array with
[`out_of_range.408`](../../home/exceptions.md#jsonexceptionout_of_range408) when the count exceeds 1,048,576, and
`to_ubjson` writes longer arrays of these types without the annotation, so any value it produces can be read back.
!!! info "Binary values"
If the JSON data contains the binary type, the value stored is a list of integers, as suggested by the UBJSON
+138 -8
View File
@@ -8,41 +8,72 @@ the result of an internet search. If you know further customers of the library,
## Space Exploration
- [**Peregrine Lunar Lander Flight 01**](https://en.wikipedia.org/wiki/Peregrine_Mission_One) - The library was used for payload management in the **Peregrine Moon Lander**, developed by **Astrobotic Technology** and launched as part of NASA's **Commercial Lunar Payload Services (CLPS)** program. After six days in orbit, the spacecraft was intentionally redirected into Earth's atmosphere, where it burned up over the Pacific Ocean on **January 18, 2024**.
- [**NASA Unsteady Pressure-Sensitive Paint Processing**](https://github.com/nasa/upsp-processing), NASA software for processing high-speed video recordings of wind tunnel tests on launch vehicle and aircraft models
- [**Terma TEMU**](https://temu.terma.com/docs/public/temu-release-notes/latest/copying/json-for-modern-cpp.html), an emulator of spacecraft on-board computers used to develop and validate flight software for European space missions
## Automotive
- [**Alexa Auto SDK**](https://github.com/alexa/alexa-auto-sdk), a software development kit enabling the integration of Alexa into automotive systems
- [**Apollo**](https://github.com/ApolloAuto/apollo), a framework for building autonomous driving systems
- [**Automotive Grade Linux (AGL)**](https://download.automotivelinux.org/AGL/release/jellyfish/latest/qemux86-64/deploy/licenses/nlohmann-json/), a collaborative open-source platform for automotive software development
- [**Autoware**](https://github.com/autowarefoundation/autoware_universe), an open-source software stack for autonomous driving built on ROS 2
- [**Eclipse S-CORE**](https://github.com/eclipse-score/nlohmann_json), an open-source software platform for the software-defined vehicle backed by major automotive manufacturers and suppliers
- [**Genesis Motor** (infotainment)](http://webmanual.genesis.com/ccIC/AVNT/JW/KOR/English/reference010.html), a luxury automotive brand
- [**Hyundai** (infotainment)](https://www.hyundai.com/wsvc/ww/download.file.do?id=/content/hyundai/ww/data/opensource/data/GN7-2022/licenseCode/info), a global automotive brand
- [**Kia** (infotainment)](http://webmanual.kia.com/PREM_GEN6/AVNT/RJPE/KOR/Korean/reference010.html), a global automotive brand
- [**Mercedes-Benz Operating System (MB.OS)**](https://group.mercedes-benz.com/careers/about-us/mercedes-benz-operating-system/), a core component of the vehicle software ecosystem from Mercedes-Benz
- [**NVIDIA DRIVE OS**](https://developer.nvidia.com/docs/drive/drive-os/6.0.5/public/driveworks-nvcgf/dwx_open_source_attribution.html), the operating system and DriveWorks SDK powering NVIDIA's platform for autonomous vehicles
- [**Rivian** (infotainment)](https://assets.ctfassets.net/2md5qhoeajym/3cwyo4eoufk4yingUwusFt/ded2c47da620fdfc99c88c7156d2c1d8/In-Vehicle_OSS_Attribution_2024__11-24_.pdf), an electric vehicle manufacturer
- [**Suzuki** (infotainment)](https://www.globalsuzuki.com/motorcycle/ipc/oss/oss_48KA_00.pdf), a global automotive and motorcycle manufacturer
## Gaming and Entertainment
- [**Anno 117: Pax Romana**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a city-building strategy game set in the Roman Empire
- [**Assassin's Creed: Mirage**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a stealth-action game set in the Middle East, focusing on the journey of a young assassin with classic parkour and stealth mechanics
- [**Battlefield 6**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a military first-person shooter known for its large-scale multiplayer battles
- [**Battlefield: REDSEC**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a free-to-play battle royale experience set in the Battlefield universe
- [**BioMenace: Remastered**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a remaster of the classic side-scrolling platform shooter
- [**Chasm: The Rift**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a first-person shooter blending horror and adventure, where players navigate dark realms and battle monsters
- [**College Football 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a college football simulation game featuring gameplay that mimics real-life college teams and competitions
- [**College Football 26**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a college football simulation game featuring licensed teams and stadiums
- [**College Football 27**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), the latest installment of the college football simulation series
- [**Concepts**](https://concepts.app/en/licenses), a digital sketching app designed for creative professionals, offering flexible drawing tools for illustration, design, and brainstorming
- [**Depthkit**](https://www.depthkit.tv/third-party-licenses), a tool for creating and capturing volumetric video, enabling immersive 3D experiences and interactive content
- [**Dune: Awakening**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an open-world survival MMO set on the desert planet Arrakis
- [**EA Sports FC 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an association football simulation with club, career, and online modes
- [**EA Sports FC 26**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), the latest installment of the association football simulation series
- [**EA Sports UFC 6**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a mixed martial arts fighting simulation
- [**FiveM**](https://github.com/citizenfx/fivem), a modification framework for Grand Theft Auto V that powers custom multiplayer servers
- [**FLUX:: Immersive**](https://doc.flux.audio/syrah/Credits.html), a suite of professional audio processing and immersive mixing plugins used in music and post-production
- [**IMG.LY**](https://img.ly/acknowledgements), a platform offering creative tools and SDKs for integrating advanced image and video editing in applications
- [**immersivetech**](https://immersitech.io/open-source-third-party-software/), a technology company focused on immersive experiences, providing tools and solutions for virtual and augmented reality applications
- [**Kodi**](https://github.com/xbmc/xbmc/blob/master/xbmc/utils/JSONVariantWriter.cpp), a home theater and media center application
- [**LOOT**](https://loot.readthedocs.io/_/downloads/en/0.13.0/pdf/), a tool for optimizing the load order of game plugins, commonly used in The Elder Scrolls and Fallout series
- [**LunaTranslator**](https://github.com/HIllya51/LunaTranslator/blob/main/src/NativeImpl/LunaSubprocess/aspatch.cpp), a real-time translation tool for visual novels
- [**MaaAssistantArknights**](https://github.com/MaaAssistantArknights/MaaAssistantArknights/blob/dev-v2/src/MaaCore/Vision/Roguelike/BlackFlow/BlackFlowMapAnalyzer.cpp), an automation assistant for the mobile game Arknights
- [**Madden NFL 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a sports simulation game capturing the excitement of American football with realistic gameplay and team management features
- [**Madden NFL 26**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an American football simulation with franchise and team management modes
- [**Madden NFL 27**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), the latest installment of the American football simulation series
- [**Marne**](https://marne.io/licenses), an unofficial private server platform for hosting custom Battlefield 1 game experiences
- [**Minecraft**](https://www.minecraft.net/zh-hant/attribution), a popular sandbox video game
- [**Mumble**](https://github.com/mumble-voip/mumble), a low-latency, open-source voice chat application widely used by gaming communities
- [**NHL 22**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a hockey simulation game offering realistic gameplay, team management, and various modes to enhance the hockey experience
- [**OBS Studio**](https://github.com/obsproject/obs-studio), a free and open-source suite for video recording and live streaming
- [**OpenRCT2**](https://github.com/OpenRCT2/OpenRCT2/blob/develop/src/openrct2/core/JsonFwd.hpp), an open source re-implementation of RollerCoaster Tycoon 2
- [**Pixelpart**](https://pixelpart.net/documentation/book/third-party.html), a 2D animation and video compositing software that allows users to create animated graphics and visual effects with a focus on simplicity and ease of use
- [**Razer Cortex**](https://mysupport.razer.com/app/answers/detail/a_id/14146/~/open-source-software-for-razer-software), a gaming performance optimizer and system booster designed to enhance the gaming experience
- [**Red Dead Redemption II**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an open-world action-adventure game following an outlaw's story in the late 1800s, emphasizing deep storytelling and immersive gameplay
- [**RetroArch**](https://github.com/libretro/RetroArch), a frontend for emulators, game engines, and media players built on the libretro API
- [**shadPS4**](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/user_manager.h), a PlayStation 4 emulator for Windows, Linux and macOS
- [**skate.**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a free-to-play skateboarding game set in an open world
- [**Snapchat**](https://www.snap.com/terms/license-android), a multimedia messaging and augmented reality app for communication and entertainment
- [**Steel Century Groove**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an action game released in 2026
- [**Sunshine**](https://github.com/LizardByte/Sunshine/blob/master/src/confighttp.h), a self-hosted game streaming host compatible with Moonlight clients
- [**Tactics Ogre: Reborn**](https://www.square-enix-games.com/en_US/documents/tactics-ogre-reborn-pc-installer-software-and-associated-plug-ins-disclosure), a tactical role-playing game featuring strategic battles and deep storytelling elements
- [**Throne and Liberty**](https://www.amazon.com/gp/help/customer/display.html?nodeId=T7fLNw5oAevCMtJFPj&pop-up=1), an MMORPG that offers an expansive fantasy world with dynamic gameplay and immersive storytelling
- [**Unity Vivox**](https://docs.unity3d.com/Packages/com.unity.services.vivox@15.1/license/Third%20Party%20Notices.html), a communication service that enables voice and text chat functionality in multiplayer games developed with Unity
- [**xemu**](https://github.com/xemu-project/xemu), an emulator of the original Xbox console
- [**Zool: Redimensioned**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a modern reimagining of the classic platformer featuring fast-paced gameplay and vibrant environments
- [**immersivetech**](https://immersitech.io/open-source-third-party-software/), a technology company focused on immersive experiences, providing tools and solutions for virtual and augmented reality applications
## Consumer Electronics
@@ -50,109 +81,195 @@ the result of an internet search. If you know further customers of the library,
- [**Canon CanoScan LIDE**](https://carolburo.com/wp-content/uploads/2024/06/LiDE400_OnlineManual_Win_FR_V02.pdf), a series of flatbed scanners offering high-resolution image scanning for home and office use
- [**Canon PIXMA Printers**](https://www.mediaexpert.pl/products/files/73/7338196/Instrukcja-obslugi-CANON-Pixma-TS7450i.pdf), a line of all-in-one inkjet printers known for high-quality printing and wireless connectivity
- [**Cisco Webex Desk Camera**](https://www.cisco.com/c/dam/en_us/about/doing_business/open_source/docs/CiscoWebexDeskCamera-23-1622100417.pdf), a video camera designed for professional-quality video conferencing and remote collaboration
- [**DJI Edge SDK**](https://github.com/dji-sdk/Edge-SDK-V2-Demo), the reference applications for DJI's Edge SDK, used to build edge computing services on DJI drone docks
- [**Elgato Stream Deck**](https://github.com/elgatosf/streamdeck-obs-plugin2), a family of programmable control surfaces for content creators and their plugin ecosystem
- [**Instagrid**](https://instagrid.co/intellectual-property/foss), a manufacturer of portable, high-performance battery systems for professional mobile power supply
- [**iRobot**](https://iot-content.irobot.com/iw/sfsites/c/cms/delivery/media/MCKRLTPDJSSJBNJKDA5SG5UVVIIQ), a manufacturer of autonomous home robots including the Roomba vacuum cleaner range
- [**Logitech Logi Bolt**](https://opensource.logitech.com/wiki/Logi_BoltApp/), the management application for Logitech's secure wireless connectivity technology
- [**Novitus**](https://novitus.pl/licencjepensource), a manufacturer of fiscal cash registers and point-of-sale devices
- [**Philips Hue Personal Wireless Lighting**](http://2ak5ape.257.cz/), a smart lighting system for customizable and wireless home illumination
- [**Ray-Ban Meta Smart glasses**](https://www.meta.com/de/en/legal/smart-glasses/third-party-notices-android/03/), a pair of smart glasses designed for capturing photos and videos with integrated connectivity and social features
- [**Razer Synapse**](https://mysupport.razer.com/app/answers/detail/a_id/14146/~/open-source-software-for-razer-software), a unified configuration software enabling hardware customization for Razer devices
- [**Sharp Professional Displays**](https://jp.sharp/restricted/business/lcd-display/cms/images/source_pnla862/PN-LA652_752_862_LicenseInformation.pdf), a range of large-format interactive displays for business and education
- [**Siemens SINEMA Remote Connect**](https://cache.industry.siemens.com/dl/files/790/109793790/att_1054961/v2/OSS_SINEMA-RC_86.pdf), a remote connectivity solution for monitoring and managing industrial networks and devices securely
- [**Skydio**](https://pages.skydio.com/rs/784-TUF-591/images/Open%20Source%20Software%20Notice%20v0.2.html), a manufacturer of autonomous drones for inspection, public safety, and defense applications
- [**Sony PlayStation 4**](https://doc.dl.playstation.net/doc/ps4-oss/index.html), a gaming console developed by Sony that offers a wide range of games and multimedia entertainment features
- [**Sony Spatial Reality Display**](https://www.sony.co.jp/en/Products/Developer-Spatial-Reality-display/download/dcc-tools/blender-plugin/SpatiaRealityDisplayPluginforPreviewBL_Manual.pdf), a glasses-free stereoscopic 3D display and its plugins for Blender, 3ds Max, and ZBrush
- [**Sony Virtual Webcam Driver for Remote Camera**](https://helpguide.sony.net/rc/vwd/v1/zh-cn/print.pdf), a software driver that enables the use of Sony cameras as virtual webcams for video conferencing and streaming
- [**Yamaha Clavinova**](https://usa.yamaha.com/files/download/other_assets/1/2298171/CLP-800_oss_license.pdf), a series of digital pianos combining acoustic piano feel with digital sound technology
## Operating Systems
## Operating Systems and Platforms
- [**Apple iOS and macOS**](https://www.apple.com/macos), a family of operating systems developed by Apple, including iOS for mobile devices and macOS for desktop computers
- [**Chromium**](https://chromium.googlesource.com/chromium/src/+/main/third_party/nlohmann_json/), the open-source browser project that Google Chrome, Microsoft Edge, and many other browsers are built on, where the library is used as data container for on-device model execution
- [**Google Fuchsia**](https://fuchsia.googlesource.com/third_party/json/), an open-source operating system developed by Google, designed to be secure, updatable, and adaptable across various devices
- [**LG webOS**](https://github.com/webosose/com.webos.service.camera), a Linux-based operating system used in LG smart TVs, signage, and embedded devices
- [**Microsoft Azure Linux**](https://github.com/microsoft/azurelinux), a Linux distribution developed by Microsoft for Azure infrastructure and edge workloads
- [**OpenHarmony**](https://github.com/openharmony/third_party_json), an open-source operating system for smart devices and the foundation of HarmonyOS
- [**SerenityOS**](https://github.com/SerenityOS/serenity), an open-source operating system that aims to provide a simple and beautiful user experience with a focus on simplicity and elegance
- [**Windows Subsystem for Linux**](https://github.com/microsoft/WSL), a compatibility layer that runs Linux environments natively on Windows
- [**Yocto**](http://ftp.emacinc.com/openembedded-sw/kirkstone-icop-5.15-kirkstone-6.0/archive-2024-10/pn8m-090t-ppc/licenses/nlohmann-json/), a Linux-based build system for creating custom operating systems and software distributions, tailored for embedded devices and IoT applications
## Development Tools and IDEs
- [**Accentize SpectralBalance**](https://www.accentize.com/products/SpectralBalanceManual.pdf), an adaptive speech analysis tool designed to enhance audio quality by optimizing frequency balance in recordings
- [**Airbus Ghidralligator**](https://www.cyber.airbus.com/en/newsroom/stories/2025-06-ghidralligator), a Ghidra-based emulator from Airbus CyberSecurity used to fuzz and analyse embedded firmware
- [**Apache brpc**](https://github.com/apache/brpc/blob/master/src/butil/iobuf.h), an industrial-grade remote procedure call framework for C++
- [**Arm Compiler for Linux**](https://documentation-service.arm.com/static/66558e9d876c8d213b7843e4), a software development toolchain for compiling and optimizing applications on Arm-based Linux systems
- [**BBEdit**](https://s3.amazonaws.com/BBSW-download/BBEdit_15.1.2_User_Manual.pdf), a professional text and code editor for macOS
- [**CoderPad**](https://coderpad.io), a collaborative coding platform that enables real-time code interviews and assessments for developers; the library is included in every CoderPad instance and can be accessed with a simple `#include "json.hpp"`
- [**Codon**](https://github.com/exaloop/codon/blob/develop/jupyter/jupyter.h), an ahead-of-time compiler for a Python-like language
- [**Compiler Explorer**](https://godbolt.org), a web-based tool that allows users to write, compile, and visualize the assembly output of code in various programming languages; the library is readily available and accessible with the directive `#include <nlohmann/json.hpp>`.
- [**GitHub CodeQL**](https://github.com/github/codeql), a code analysis tool used for identifying security vulnerabilities and bugs in software through semantic queries
- [**Flutter**](https://github.com/flutter/flutter/blob/master/engine/src/flutter/impeller/compiler/reflector.cc), a UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase
- [**Fraunhofer VVenC**](https://github.com/fraunhoferhhi/vvenc), a fast and efficient encoder for the Versatile Video Coding (H.266/VVC) standard
- [**GitHub CodeQL**](https://github.com/github/codeql/blob/main/shared/cpp/Diagnostics.h), a code analysis tool used for identifying security vulnerabilities and bugs in software through semantic queries
- [**GoPro ngfx**](https://github.com/gopro/ngfx), a low-level graphics abstraction and profiling framework developed by GoPro
- [**gRPC**](https://github.com/grpc/grpc/blob/master/tools/artifact_gen/utils.h), a high-performance universal remote procedure call framework
- [**Hex-Rays**](https://docs.hex-rays.com/user-guide/user-interface/licenses), a reverse engineering toolset for analyzing and decompiling binaries, primarily used for security research and vulnerability analysis
- [**ImHex**](https://github.com/WerWolv/ImHex), a hex editor designed for reverse engineering, providing advanced features for data analysis and manipulation
- [**Intel GITS**](https://github.com/intel/gits), a tool for capturing and replaying graphics API calls for debugging and performance analysis
- [**Intel GPA Framework**](https://intel.github.io/gpasdk-doc/src/licenses.html), a suite of cross-platform tools for capturing, analyzing, and optimizing graphics applications across different APIs
- [**Intopix**](https://www.intopix.com/software-licensing), a provider of advanced image processing and compression solutions used in software development and AV workflows
- [**Java SE**](https://www.oracle.com/a/tech/docs/jdk8-lium.pdf), the core Java platform that provides the libraries and runtime needed to build and run general-purpose Java applications
- [**MKVToolNix**](https://mkvtoolnix.download/doc/README.md), a set of tools for creating, editing, and inspecting MKV (Matroska) multimedia container files
- [**Meta Yoga**](https://github.com/facebook/yoga), a layout engine that facilitates flexible and efficient user interface design across multiple platforms
- [**NVIDIA Nsight Compute**](https://docs.nvidia.com/nsight-compute/2022.2/pdf/CopyrightAndLicenses.pdf), a performance analysis tool for CUDA applications that provides detailed insights into GPU performance metrics
- [**MKVToolNix**](https://mkvtoolnix.download/doc/README.md), a set of tools for creating, editing, and inspecting MKV (Matroska) multimedia container files
- [**MRTech IFF SDK**](https://mr-technologies.com/pub/iff-sdk-manual-2-0-1/iff-sdk-manual-2-0-1.pdf), an image processing SDK for machine vision applications with GPU-accelerated pipelines
- [**Nix**](https://github.com/NixOS/nix/blob/master/src/nix/build.cc), a purely functional package manager
- [**Notepad++**](https://github.com/notepad-plus-plus/notepad-plus-plus), a free source code editor that supports various programming languages
- [**NVIDIA Nsight Compute**](https://docs.nvidia.com/nsight-compute/2022.2/pdf/CopyrightAndLicenses.pdf), a performance analysis tool for CUDA applications that provides detailed insights into GPU performance metrics
- [**openFrameworks**](https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/utils/ofJson.h), a community-developed C++ toolkit for creative coding
- [**OpenRGB**](https://gitlab.com/CalcProgrammer1/OpenRGB), an open source RGB lighting control that doesn't depend on manufacturer software
- [**OpenTelemetry C++**](https://github.com/open-telemetry/opentelemetry-cpp), a library for collecting and exporting observability data in C++, enabling developers to implement distributed tracing and metrics in their application
- [**Oracle GraalVM**](https://docs.oracle.com/en/graalvm/jdk/21/docs/licensing-information/), a high-performance JDK distribution with ahead-of-time compilation and polyglot runtime support
- [**Philips amp-cucumber-cpp-runner**](https://github.com/philips-software/amp-cucumber-cpp-runner), a behaviour-driven development test runner for embedded C++ software developed at Philips
- [**Qt Creator**](https://doc.qt.io/qtcreator/qtcreator-attribution-json-nlohmann.html), an IDE for developing applications using the Qt application framework
- [**Qt for MCUs**](https://doc.qt.io/QtForMCUs/quickultralite-attribution-nlohmann-json.html), a graphics framework for building fluid user interfaces on microcontrollers
- [**React Native**](https://github.com/react/react-native/blob/main/packages/react-native/ReactCxxPlatform/react/devsupport/PackagerConnection.cpp), a framework for building native mobile applications using React
- [**Scanbot SDK**](https://docs.scanbot.io/barcode-scanner-sdk/web/third-party-libraries/), a software development kit (SDK) that provides tools for integrating advanced document scanning and barcode scanning capabilities into applications
- [**STMicroelectronics TouchGFX**](https://www.st.com/resource/en/additional_license_terms/additional-license-terms-x-cube-touchgfx.html), a graphical user interface framework shipped with STM32 microcontrollers for building embedded HMIs
- [**swagger-codegen**](https://github.com/swagger-api/swagger-codegen/blob/master/samples/server/petstore/pistache-server/model/Pet.h), a template-driven engine that generates API clients and server stubs from an OpenAPI specification
- [**Swoole**](https://github.com/swoole/swoole-src/blob/master/ext-src/swoole_admin_server.cc), a coroutine-based concurrency engine for PHP
- [**Tracy Profiler**](https://github.com/wolfpld/tracy/blob/master/profiler/src/profiler/TracyLlm.hpp), a real-time frame profiler for games and other applications
- [**WasmEdge**](https://github.com/WasmEdge/WasmEdge/blob/master/plugins/wasi_nn/GGML/tts/tts_core.cpp), a lightweight WebAssembly runtime for edge and cloud workloads
- [**x64dbg**](https://github.com/x64dbg/x64dbg/blob/development/src/cross/remote_table/TableRpcData.h), an open source user mode debugger for Windows, aimed at reverse engineering and malware analysis
## Machine Learning and AI
- [**Alibaba MNN**](https://github.com/alibaba/MNN), a lightweight deep learning inference engine for mobile and embedded devices
- [**AMD Gaia**](https://github.com/amd/gaia), an open-source framework for running generative AI applications locally on AMD hardware
- [**AMD Vitis AI (VAIP)**](https://github.com/amd/vaip), the execution provider stack that runs AI models on AMD Ryzen AI and adaptive computing devices
- [**Apple Core ML Tools**](https://github.com/apple/coremltools), a set of tools for converting and configuring machine learning models for deployment in Apple's Core ML framework
- [**Avular Mobile Robotics**](https://www.avular.com/licenses/nlohmann-json-3.9.1.txt), a platform for developing and deploying mobile robotics solutions
- [**FunASR**](https://github.com/modelscope/FunASR/blob/main/runtime/http/bin/asr_sessions.h), a speech recognition toolkit for training and deploying end-to-end models
- [**Google gemma.cpp**](https://github.com/google/gemma.cpp), a lightweight C++ inference engine designed for running AI models from the Gemma family
- [**Google Magenta The Infinite Crate**](https://github.com/magenta/the-infinite-crate), an open-source generative AI plugin for digital audio workstations from Google's Magenta research team
- [**GPT4All**](https://github.com/nomic-ai/gpt4all/blob/main/gpt4all-chat/src/tool.h), a desktop application for running local large language models on consumer hardware
- [**Huawei MindSpore**](https://github.com/mindspore-ai/mindspore/blob/master/Third_Party_Open_Source_Software_Notice), a deep learning framework for training and inference across device, edge, and cloud
- [**KTransformers**](https://github.com/kvcache-ai/ktransformers/blob/main/archive/csrc/balance_serve/sched/model_config.h), a framework for heterogeneous large language model inference
- [**llama.cpp**](https://github.com/ggerganov/llama.cpp), a C++ library designed for efficient inference of large language models (LLMs), enabling streamlined integration into applications
- [**LocalAI**](https://github.com/mudler/LocalAI/blob/master/backend/cpp/ds4/dsml_renderer.cpp), a self-hosted inference engine that exposes local models through an OpenAI-compatible API
- [**MLX**](https://github.com/ml-explore/mlx), an array framework for machine learning on Apple Silicon
- [**Mozilla llamafile**](https://github.com/Mozilla-Ocho/llamafile), a tool designed for distributing and executing large language models (LLMs) efficiently using a single file format
- [**NVIDIA ACE**](https://docs.nvidia.com/ace/latest/index.html), a suite of real-time AI solutions designed for the development of interactive avatars and digital human applications, enabling scalable and sophisticated user interactions
- [**NVIDIA Instant NGP**](https://github.com/NVlabs/instant-ngp/blob/master/src/nerf_loader.cu), an implementation of instant neural graphics primitives for rapid scene reconstruction
- [**NVIDIA TensorRT**](https://github.com/NVIDIA/TensorRT), an SDK for high-performance deep learning inference, including its TensorRT-LLM extension for large language models
- [**NVIDIA TensorRT-LLM**](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/tensorrt_llm/common/safetensors.cpp), a toolkit for optimizing and serving large language model inference on GPUs
- [**ONNX Runtime**](https://github.com/microsoft/onnxruntime), a cross-platform inference and training accelerator for machine learning models
- [**OpenVINO**](https://github.com/openvinotoolkit/openvino), Intel's toolkit for optimizing and deploying deep learning inference across CPUs, GPUs, and NPUs
- [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR/blob/main/deploy/cpp_infer/src/modules/text_detection/result.cc), an optical character recognition toolkit that turns documents and images into structured data
- [**PaddlePaddle**](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/ap/src/axpr/anf_expr.cc), a deep learning framework for distributed training and inference
- [**Peer**](https://support.peer.inc/hc/en-us/articles/17261335054235-Licenses), a platform offering personalized AI assistants for interactive learning and creative collaboration
- [**PyTorch**](https://github.com/pytorch/pytorch), a machine learning framework for building and training neural networks, widely used in research and production
- [**Qualcomm AI Engine Direct**](https://github.com/qualcomm/qai-appbuilder), a toolchain for building and running generative AI applications on Snapdragon devices
- [**sherpa-onnx**](https://github.com/k2-fsa/sherpa-onnx/blob/master/sherpa-onnx/csrc/sentence-piece-tokenizer.cc), a speech toolkit for on-device recognition, synthesis and speaker diarization
- [**stable-diffusion.cpp**](https://github.com/leejet/stable-diffusion.cpp), a C++ implementation of the Stable Diffusion image generation model
- [**TanvasTouch**](https://tanvas.co/tanvastouch-sdk-third-party-acknowledgments), a software development kit (SDK) that enables developers to create tactile experiences on touchscreens, allowing users to feel textures and physical sensations in a digital environment
- [**TensorFlow**](https://github.com/tensorflow/tensorflow), a machine learning framework that facilitates the development and training of models, supporting data serialization and efficient data exchange between components
- [**whisper.cpp**](https://github.com/ggml-org/whisper.cpp), a C++ implementation of OpenAI's Whisper automatic speech recognition model
## Scientific Research and Analysis
- [**BLACK**](https://www.black-sat.org/en/stable/installation/linux.html), a bounded linear temporal logic (LTL) satisfiability checker
- [**CERN ALICE O2**](https://github.com/AliceO2Group/AliceO2), the online-offline computing framework of the ALICE heavy-ion experiment at the Large Hadron Collider
- [**CERN Atlas Athena**](https://gitlab.cern.ch/atlas/athena/-/blob/main/Control/PerformanceMonitoring/PerfMonComps/src/PerfMonMTSvc.h), a software framework used in the ATLAS experiment at the Large Hadron Collider (LHC) for performance monitoring
- [**CERN CMSSW**](https://github.com/cms-sw/cmssw), the offline software framework of the CMS experiment at the Large Hadron Collider
- [**CERN Gaudi**](https://gitlab.cern.ch/gaudi/Gaudi), the event-processing framework used by the LHCb and ATLAS experiments at the Large Hadron Collider
- [**ICU**](https://github.com/unicode-org/icu), the International Components for Unicode, a mature library for software globalization and multilingual support
- [**KAMERA**](https://github.com/Kitware/kamera), a platform for synchronized data collection and real-time deep learning to map marine species like polar bears and seals, aiding Arctic ecosystem research
- [**KiCad**](https://gitlab.com/kicad/code/kicad/-/tree/master/thirdparty/nlohmann_json), a free and open-source software suite for electronic design automation
- [**LLNL ROSE**](https://github.com/llnl/rose), a compiler infrastructure from Lawrence Livermore National Laboratory for building source-to-source program analysis and transformation tools
- [**Maple**](https://www.maplesoft.com/support/help/Maple/view.aspx?path=copyright), a symbolic and numeric computing environment for advanced mathematical modeling and analysis
- [**MeVisLab**](https://mevislabdownloads.mevis.de/docs/current/MeVis/ThirdParty/Documentation/Publish/ThirdPartyReference/index.html), a software framework for medical image processing and visualization.
- [**MITK**](https://github.com/MITK/MITK), the Medical Imaging Interaction Toolkit, a framework for developing interactive medical image processing software
- [**OpenPMD API**](https://openpmd-api.readthedocs.io/en/0.8.0-alpha/backends/json.html), a versatile programming interface for accessing and managing scientific data, designed to facilitate the efficient storage, retrieval, and sharing of simulation data across various applications and platforms
- [**ORNL DataFed**](https://github.com/ORNL/DataFed), a federated scientific data management system developed at Oak Ridge National Laboratory
- [**ParaView**](https://github.com/Kitware/ParaView), an open-source tool for large-scale data visualization and analysis across various scientific domains
- [**QGIS**](https://gitlab.b-data.ch/qgis/qgis/-/blob/backport-57658-to-release-3_34/external/nlohmann/json.hpp), a free and open-source geographic information system (GIS) application that allows users to create, edit, visualize, and analyze geospatial data across a variety of formats
- [**VTK**](https://github.com/Kitware/VTK), a software library for 3D computer graphics, image processing, and visualization
- [**Sandia InterSpec**](https://github.com/sandialabs/InterSpec), spectral radiation analysis software from Sandia National Laboratories for identifying radioactive isotopes
- [**VolView**](https://github.com/Kitware/VolView), a lightweight application for interactive visualization and analysis of 3D medical imaging data.
- [**VTK**](https://github.com/Kitware/VTK), a software library for 3D computer graphics, image processing, and visualization
## Business and Productivity Software
- [**ArcGIS PRO**](https://www.esri.com/content/dam/esrisites/en-us/media/legal/open-source-acknowledgements/arcgis-pro-2-8-attribution-report.html), a desktop geographic information system (GIS) application developed by Esri for mapping and spatial analysis
- [**Autodesk Desktop**](https://damassets.autodesk.net/content/dam/autodesk/www/Company/legal-notices-trademarks/autodesk-desktop-platform-components/internal-autodesk-components-web-page-2023.pdf), a software platform developed by Autodesk for creating and managing desktop applications and services
- [**Check Point**](https://www.checkpoint.com/about-us/copyright-and-trademarks/), a cybersecurity company specializing in threat prevention and network security solutions, offering a range of products designed to protect enterprises from cyber threats and ensure data integrity
- [**EasyEffects**](https://github.com/wwmm/easyeffects/blob/master/src/presets_manager.hpp), an audio effects processor for PipeWire offering limiting, compression and equalization
- [**espanso**](https://github.com/espanso/espanso/blob/dev/espanso-ui/src/win32/native.cpp), a cross-platform text expander
- [**Karabiner-Elements**](https://github.com/pqrs-org/Karabiner-Elements/blob/main/src/share/app_icon.hpp), a keyboard customizer for macOS
- [**MacType**](https://github.com/snowie2000/mactype/blob/directwrite/settings.h), a font rendering engine for Windows
- [**magicplan**](https://help.magicplan.app/acknowledgments), a mobile application for creating floor plans and interior designs using augmented reality
- [**Microsoft Office for Mac**](https://officecdnmac.microsoft.com/pr/legal/mac/OfficeforMacAttributions.html), a suite of productivity applications developed by Microsoft for macOS, including tools for word processing, spreadsheets, and presentations
- [**Microsoft Teams**](https://www.microsoft.com/microsoft-teams/), a team collaboration application offering workspace chat and video conferencing, file storage, and integration of proprietary and third-party applications and services
- [**MuseScore**](https://github.com/musescore/MuseScore), a free and open-source music notation and composition application
- [**NanaZip**](https://github.com/M2Team/NanaZip/blob/main/NanaZip.Codecs/NanaZip.Codecs.Archive.ElectronAsar.cpp), a 7-Zip derivative built for modern Windows
- [**Nexthink Infinity**](https://docs.nexthink.com/legal/services-terms/experience-open-source-software-licenses/infinity-2022.8-software-licenses), a digital employee experience management platform for monitoring and improving IT performance
- [**Sophos Connect Client**](https://docs.sophos.com/nsg/licenses/SophosConnect/SophosConnectAttribution.html), a secure VPN client from Sophos that allows remote users to connect to their corporate network, ensuring secure access to resources and data
- [**Stonebranch**](https://stonebranchdocs.atlassian.net/wiki/spaces/UA77/pages/799545647/Licenses+for+Third-Party+Libraries), a cloud-based cybersecurity solution that integrates backup, disaster recovery, and cybersecurity features to protect data and ensure business continuity for organizations
- [**Tablecruncher**](https://tablecruncher.com/), a data analysis tool that allows users to import, analyze, and visualize spreadsheet data, offering interactive features for better insights and decision-making
- [**magicplan**](https://help.magicplan.app/acknowledgments), a mobile application for creating floor plans and interior designs using augmented reality
- [**VNote**](https://github.com/vnotex/vnote/blob/master/src/core/services/notebookcoreservice.cpp), a Markdown-based note-taking application written in C++
## Databases and Big Data
- [**ADIOS2**](https://code.ornl.gov/ecpcitest/adios2/-/tree/pr4285_FFSUpstream/thirdparty/nlohmann_json?ref_type=heads), a data management framework designed for high-performance input and output operations
- [**Apache Doris**](https://github.com/apache/doris/blob/master/be/src/runtime/be_proc_monitor.cpp), a real-time analytical database for high-concurrency queries
- [**Claris FileMaker Server**](https://www.claris.com/company/legal/docs/acknowledgements/filemaker-server-macwin/claris_fms2025_acknowledgements_en.pdf), the server platform hosting FileMaker custom apps and databases, developed by Apple subsidiary Claris
- [**ClickHouse**](https://github.com/ClickHouse/ClickHouse), a column-oriented database management system for real-time analytical queries
- [**Cribl Stream**](https://docs.cribl.io/stream/third-party-current-list/), a real-time data processing platform that enables organizations to collect, route, and transform observability data, enhancing visibility and insights into their systems
- [**DB Browser for SQLite**](https://github.com/sqlitebrowser/sqlitebrowser), a visual open-source tool for creating, designing, and editing SQLite database files
- [**Manticore Search**](https://github.com/manticoresoftware/manticoresearch/blob/main/src/searchdhttpcompat.cpp), a database for search, offering full-text and vector queries
- [**Milvus**](https://github.com/milvus-io/milvus/blob/master/internal/core/src/query/PlanImpl.h), a cloud-native vector database built for embedding similarity search
- [**MongoDB**](https://github.com/mongodb/mongo/blob/master/src/mongo/replay/config_handler.cpp), a general-purpose document database
- [**MySQL Connector/C++**](https://docs.oracle.com/cd/E17952_01/connector-cpp-9.1-license-com-en/license-opentelemetry-cpp-com.html), a C++ library for connecting and interacting with MySQL databases
- [**MySQL NDB Cluster**](https://downloads.mysql.com/docs/licenses/cluster-9.0-com-en.pdf), a distributed database system that provides high availability and scalability for MySQL databases
- [**MySQL Shell**](https://downloads.mysql.com/docs/licenses/mysql-shell-8.0-gpl-en.pdf), an advanced client and code editor for interacting with MySQL servers, supporting SQL, Python, and JavaScript
- [**PrestoDB**](https://github.com/prestodb/presto), a distributed SQL query engine designed for large-scale data analytics, originally developed by Facebook
- [**PrestoDB**](https://github.com/prestodb/presto/blob/master/presto-native-execution/presto_cpp/main/Announcer.cpp), a distributed SQL query engine designed for large-scale data analytics, originally developed by Facebook
- [**ROOT Data Analysis Framework**](https://root.cern/doc/v614/classnlohmann_1_1basic__json.html), an open-source data analysis framework widely used in high-energy physics and other fields for data processing and visualization
- [**Typesense**](https://github.com/typesense/typesense/blob/v31/include/join.h), an open source typo-tolerant search engine
- [**Vearch**](https://github.com/jd-opensource/vearch), a distributed vector database developed at JD.com for similarity search and retrieval-augmented generation
- [**WiredTiger**](https://github.com/wiredtiger/wiredtiger), a high-performance storage engine for databases, offering support for compression, concurrency, and checkpointing
## Simulation and Modeling
- [**Adobe Lagrange**](https://github.com/adobe/lagrange), a geometry processing library developed by Adobe for mesh manipulation and analysis
- [**Arcturus HoloSuite**](https://www.datocms-assets.com/104353/1698904597-holosuite-third-party-software-credits-and-attributions-2.pdf), a software toolset for capturing, editing, and streaming volumetric video, featuring advanced compression technologies for high-quality 3D content creation
- [**azul**](https://pure.tudelft.nl/ws/files/85338589/tgis.12673.pdf), a fast and efficient 3D city model viewer designed for visualizing urban environments and spatial data
- [**Bambu Studio**](https://github.com/bambulab/BambuStudio), a slicing and print management application for Bambu Lab 3D printers
- [**Blender**](https://projects.blender.org/blender/blender/search?q=nlohmann), a free and open-source 3D creation suite for modeling, animation, rendering, and more
- [**cpplot**](https://cpplot.readthedocs.io/en/latest/library_api/function_eigen_8h_1ac080eac0541014c5892a55e41bf785e6.html), a library for creating interactive graphs and charts in C++, which can be viewed in web browsers
- [**Foundry Nuke**](https://learn.foundry.com/nuke/content/misc/studio_third_party_libraries.html), a powerful node-based digital compositing and visual effects application used in film and television post-production
- [**FreeCAD**](https://github.com/FreeCAD/FreeCAD), a free and open-source parametric 3D CAD modeler for product design and engineering
- [**GAMS**](https://www.gams.com/47/docs/THIRDPARTY.html), a high-performance mathematical modeling system for optimization and decision support
- [**Keysight WirelessPro**](https://docs.keysight.com/display/engdocwirelesspro/WirelessPro+2026+Release+Notes), a simulation platform for 5G, 5G-Advanced, and 6G cellular network research
- [**Kitware SMTK**](https://github.com/Kitware/SMTK), a software toolkit for managing simulation models and workflows in scientific and engineering applications
- [**M-Star**](https://docs.mstarcfd.com/3_Licensing/thirdparty-licenses.html), a computational fluid dynamics software for simulating and analyzing fluid flow
- [**MapleSim CAD Toolbox**](https://www.maplesoft.com/support/help/MapleSim/view.aspx?path=CADToolbox/copyright), a software extension for MapleSim that integrates CAD models, allowing users to import, manipulate, and analyze 3D CAD data within the MapleSim environment for enhanced modeling and simulation
- [**Microsoft AirSim**](https://github.com/microsoft/AirSim/blob/main/AirLib/include/common/Settings.hpp), a simulator for autonomous vehicles and drones built on Unreal Engine
- [**NVIDIA Omniverse**](https://docs.omniverse.nvidia.com/composer/latest/common/product-licenses/usd-explorer/usd-explorer-2023.2.0-licenses-manifest.html), a platform for 3D content creation and collaboration that enables real-time simulations and interactive experiences across various industries
- [**OpenSCAD**](https://github.com/openscad/openscad/blob/master/src/core/AIClient.cc), a script-driven solid 3D CAD modeller
- [**OrcaSlicer**](https://github.com/SoftFever/OrcaSlicer), an open-source slicer supporting a wide range of consumer 3D printers
- [**Pixar Renderman**](https://rmanwiki-26.pixar.com/space/REN26/19662083/Legal+Notice), a photorealistic 3D rendering software developed by Pixar, widely used in the film industry for creating high-quality visual effects and animations
- [**PrusaSlicer**](https://github.com/prusa3d/PrusaSlicer), the slicing software developed by Prusa Research for its 3D printers
- [**ROS - Robot Operating System**](http://docs.ros.org/en/noetic/api/behaviortree_cpp/html/json_8hpp_source.html), a set of software libraries and tools that assist in developing robot applications
- [**UBS**](https://www.ubs.com/), a multinational financial services and banking company
@@ -161,18 +278,31 @@ the result of an internet search. If you know further customers of the library,
- [**Acronis Cyber Protect Cloud**](https://care.acronis.com/s/article/59533-Third-party-software-used-in-Acronis-Cyber-Protect-Cloud?language=en_US), an all-in-one data protection solution that combines backup, disaster recovery, and cybersecurity to safeguard business data from threats like ransomware
- [**Baereos**](https://gitlab.tiger-computing.co.uk/packages/bareos/-/blob/tiger/bullseye/third-party/CLI11/examples/json.cpp), a backup solution that provides data protection and recovery options for various environments, including physical and virtual systems
- [**Bitdefender Home Scanner**](https://www.bitdefender.de/site/Main/view/home-scanner-open-source.html), a tool from Bitdefender that scans devices for malware and security threats, providing a safeguard against potential online dangers
- [**Cisco MLS++**](https://github.com/cisco/mlspp), an implementation of the Messaging Layer Security protocol for end-to-end encrypted group messaging
- [**Citrix Provisioning**](https://docs.citrix.com/en-us/provisioning/2203-ltsr/downloads/pvs-third-party-notices-2203.pdf), a solution that streamlines the delivery of virtual desktops and applications by allowing administrators to manage and provision resources efficiently across multiple environments
- [**Citrix Virtual Apps and Desktops**](https://docs.citrix.com/en-us/citrix-virtual-apps-desktops/2305/downloads/third-party-notices-apps-and-desktops.pdf), a solution from Citrix that delivers virtual apps and desktops
- [**Cyberarc**](https://docs.cyberark.com/Downloads/Legal/Privileged%20Session%20Manager%20for%20SSH%20Third-Party%20Notices.pdf), a security solution that specializes in privileged access management, enabling organizations to control and monitor access to critical systems and data, thereby enhancing overall cybersecurity posture
- [**Deutsche Telekom sysrepo-plugins**](https://github.com/telekom/sysrepo-plugins), a collection of YANG datastore plugins used to manage network devices
- [**Egnyte Desktop**](https://helpdesk.egnyte.com/hc/en-us/articles/360007071732-Third-Party-Software-Acknowledgements), a secure cloud storage solution designed for businesses, enabling file sharing, collaboration, and data management across teams while ensuring compliance and data protection
- [**Elster**](https://www.secunet.com/en/about-us/press/article/elstersecure-bietet-komfortablen-login-ohne-passwort-dank-secunet-protect4use), a digital platform developed by German tax authorities for secure and efficient electronic tax filing and management using secunet protect4use
- [**Envoy**](https://github.com/envoyproxy/envoy), a cloud-native edge and service proxy that forms the data plane of many service meshes
- [**Ethereum Solidity**](https://github.com/ethereum/solidity), a high-level, object-oriented programming language designed for implementing smart contracts on the Ethereum platform
- [**gVisor**](https://github.com/google/gvisor), an application kernel that provides a secure sandbox for running untrusted containers
- [**IBM Storage Virtualize**](https://public.dhe.ibm.com/systems/support/warranty/pdfs/stgoilc/SV_for_FS_7300_v8_7_0_Base_OILC.pdf), the software powering IBM FlashSystem enterprise storage arrays
- [**Inciga**](https://fossies.org/linux/icinga2/third-party/nlohmann_json/json.hpp), a monitoring tool for IT infrastructure, designed to provide insights into system performance and availability through customizable dashboards and alerts
- [**Intel Accelerator Management Daemon for VMware ESXi**](https://downloadmirror.intel.com/772507/THIRD-PARTY.txt), a management tool designed for monitoring and controlling Intel hardware accelerators within VMware ESXi environments, optimizing performance and resource allocation
- [**Juniper Identity Management Service**](https://www.juniper.net/documentation/us/en/software/jims/jims-guide/jims-guide.pdf)
- [**Meta FBOSS**](https://github.com/facebook/fboss), the software stack that controls the network switches in Meta's data centers
- [**Microsoft Azure IoT SDK**](https://library.e.abb.com/public/2779c5f85f30484192eb3cb3f666a201/IP%20Gateway%20Open%20License%20Declaration_9AKK108467A4095_Rev_C.pdf), a collection of tools and libraries to help developers connect, build, and deploy Internet of Things (IoT) solutions on the Azure cloud platform
- [**Microsoft Confidential Consortium Framework**](https://github.com/microsoft/CCF), a framework for building secure, highly available applications on trusted execution environments
- [**Microsoft WinGet**](https://github.com/microsoft/winget-cli), a command-line utility included in the Windows Package Manager
- [**Mitsubishi Electric SECS/GEM**](https://dl.mitsubishielectric.com/dl/fa/document/manual/plc/sh082483eng/sh082483engi.pdf), the semiconductor equipment communication software running on Mitsubishi Electric C Controller and C intelligent function modules
- [**Moxa**](https://www.moxa.com/getmedia/fbe2a0c7-8dda-4b5b-a501-15e45adebb1f/moxa-foss-statement-for-da-720-series-win-10-ltsc-21h2-declaration-v1.0.pdf), a provider of industrial networking, computing, and automation infrastructure
- [**plexusAV**](https://www.sisme.com/media/10994/manual_plexusav-p-avn-4-form8244-c.pdf), a high-performance AV-over-IP transceiver device capable of video encoding and decoding using the IPMX standard
- [**Pointr**](https://docs-dev.pointr.tech/docs/8.x/Developer%20Portal/Open%20Source%20Licenses/), a platform for indoor positioning and navigation solutions, offering tools and SDKs for developers to create location-based applications
- [**secunet protect4use**](https://www.secunet.com/en/about-us/press/article/elstersecure-bietet-komfortablen-login-ohne-passwort-dank-secunet-protect4use), a secure, passwordless multifactor authentication solution that transforms smartphones into digital keyrings, ensuring high security for online services and digital identities
- [**Sencore MRD 7000**](https://www.foccusdigital.com/wp-content/uploads/2025/03/MRD-7000-Manual-8175V.pdf), a professional multi-channel receiver and decoder supporting UHD and HD stream decoding
- [**Siemens SINEC**](https://cache.industry.siemens.com/dl/files/917/109974917/att_1298783/v2/OSS_SINEC-NMS_99.pdf), a family of network management and infrastructure services for industrial networks
- [**Toshiba Industrial Servers**](https://www.global.toshiba/content/dam/toshiba/jp/products-solutions/industrial/computer/product/server/fs20000r/pdf/FS20000R_OSS_License_6E8C5817_rev0.pdf), the FS20000R series of industrial servers for factory automation and control systems
- [**Wazuh**](https://github.com/wazuh/wazuh/blob/main/src/data_provider/src/sysInfo.cpp), a security platform for threat detection, integrity monitoring and incident response
- [**ZeroTier**](https://github.com/zerotier/ZeroTierOne/blob/dev/osdep/OSUtils.hpp), a software-defined networking service that creates virtual Ethernet networks
+9
View File
@@ -868,6 +868,12 @@ The size of an array or object in a [binary format](../features/binary_formats/i
the size following `#` for [UBJSON](../features/binary_formats/ubjson.md)/[BJData](../features/binary_formats/bjdata.md),
or the encoded length for [CBOR](../features/binary_formats/cbor.md).
The exception is also thrown for a [UBJSON](../features/binary_formats/ubjson.md) array of a type that is encoded by its
marker alone (`Z`, `T` or `F`) whose declared count exceeds 1,048,576. Such an array has no payload, so its count alone
decides how much memory is allocated, and a handful of bytes would otherwise describe billions of values.
[`to_ubjson`](../api/basic_json/to_ubjson.md) writes longer arrays of these types without the size and type annotation,
so any value it produces can still be read back.
!!! failure "Example messages"
```
@@ -879,6 +885,9 @@ or the encoded length for [CBOR](../features/binary_formats/cbor.md).
```
[json.exception.out_of_range.408] syntax error while parsing CBOR size: excessive map size
```
```
[json.exception.out_of_range.408] syntax error while parsing UBJSON size: excessive array size
```
### json.exception.out_of_range.409
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1001 KiB

File diff suppressed because it is too large Load Diff
@@ -826,7 +826,17 @@ class binary_writer
std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
// an optimized array of a valueless type carries no payload, so a
// reader has nothing but the declared count to bound the allocation
// by and refuses an excessive one. Write the unoptimized form for
// those, at one byte per element, so the result can be read back.
// Objects are not affected: every element is preceded by its key.
const bool valueless_type = (first_prefix == 'Z' || first_prefix == 'T' || first_prefix == 'F');
const bool excessive_valueless = valueless_type
&& j.m_data.m_value.array->size() > detail::max_valueless_container_size;
if (same_prefix && !excessive_valueless
&& !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
{
prefix_required = false;
oa->write_character(to_char_type('$'));
@@ -1668,6 +1678,15 @@ class binary_writer
CharType dtype = it->second;
key = "_ArraySize_";
// the dimensions are written verbatim as the header length below, so a
// value that is not an array cannot produce a valid one: null emits 'Z'
// and an object emits '{', neither of which a reader accepts after '#'.
// Such an object is not a valid ndarray and falls back to a plain object.
if (!value.at(key).is_array())
{
return true;
}
std::size_t len = (value.at(key).empty() ? 0 : 1);
for (const auto& el : value.at(key))
{
+70 -28
View File
@@ -4473,8 +4473,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -4490,8 +4493,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
template<typename T>
@@ -4516,8 +4522,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in MessagePack format
@@ -4531,8 +4540,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -4547,8 +4559,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
template<typename T>
@@ -4571,8 +4586,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in UBJSON format
@@ -4586,8 +4604,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -4602,8 +4623,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
template<typename T>
@@ -4626,8 +4650,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in BJData format
@@ -4641,8 +4668,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -4657,8 +4687,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in BSON format
@@ -4672,8 +4705,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -4688,8 +4724,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
template<typename T>
@@ -4712,8 +4751,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
}
/// @}
File diff suppressed because it is too large Load Diff
+43 -3
View File
@@ -2751,6 +2751,31 @@ TEST_CASE("BJData")
CHECK(json::to_bjdata(j_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 3, ']', 1, 2, 3, 4, 5, 6}));
CHECK(json::from_bjdata(json::to_bjdata(j_ok), true, true) == j_ok);
}
SECTION("ndarray whose _ArraySize_ is not an array stays as object")
{
// the shape is written verbatim as the header length, so a
// value that is not an array cannot produce a valid one: null
// would emit 'Z' and an object '{', neither of which a reader
// accepts after '#'. Both have to stay plain objects.
json const j_null = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", nullptr}, {"_ArrayData_", json::array()}});
const auto out_null = json::to_bjdata(j_null);
CHECK(out_null.at(0) == '{');
CHECK(json::from_bjdata(out_null) == j_null);
// an object shape passes the per-entry check by iterating its
// values rather than dimensions, so it needs rejecting too
json const j_obj = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {{"a", 1}}}, {"_ArrayData_", {1}}});
const auto out_obj = json::to_bjdata(j_obj);
CHECK(out_obj.at(0) == '{');
CHECK(json::from_bjdata(out_obj) == j_obj);
// a scalar shape is not a dimension list either
json const j_num = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", 1}, {"_ArrayData_", {1}}});
const auto out_num = json::to_bjdata(j_num);
CHECK(out_num.at(0) == '{');
CHECK(json::from_bjdata(out_num) == j_num);
}
}
}
@@ -3263,8 +3288,10 @@ TEST_CASE("BJData")
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR1), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
CHECK(json::from_bjdata(vR1, true, false).is_discarded());
// a dimension vector that opens another one is rejected where the
// nested '[' is read, rather than after it has been descended into
std::vector<uint8_t> const vR2 = {'[', '$', 'i', '#', '[', '#', '[', 'i', 1, ']', ']', 1};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR2), "[json.exception.parse_error.113] parse error at byte 11: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x5D", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR2), "[json.exception.parse_error.113] parse error at byte 7: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
CHECK(json::from_bjdata(vR2, true, false).is_discarded());
std::vector<uint8_t> const vR3 = {'[', '#', '[', 'i', '2', 'i', 2, ']'};
@@ -3272,7 +3299,7 @@ TEST_CASE("BJData")
CHECK(json::from_bjdata(vR3, true, false).is_discarded());
std::vector<uint8_t> const vR4 = {'[', '$', 'i', '#', '[', '$', 'i', '#', '[', 'i', 1, ']', 1};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR4), "[json.exception.parse_error.110] parse error at byte 14: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR4), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
CHECK(json::from_bjdata(vR4, true, false).is_discarded());
std::vector<uint8_t> const vR5 = {'[', '$', 'i', '#', '[', '[', '[', ']', ']', ']'};
@@ -3280,12 +3307,25 @@ TEST_CASE("BJData")
CHECK(json::from_bjdata(vR5, true, false).is_discarded());
std::vector<uint8_t> const vR6 = {'[', '$', 'i', '#', '[', '$', 'i', '#', '[', 'i', '2', 'i', 2, ']'};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR6), "[json.exception.parse_error.112] parse error at byte 14: syntax error while parsing BJData size: ndarray can not be recursive", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR6), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
CHECK(json::from_bjdata(vR6, true, false).is_discarded());
std::vector<uint8_t> const vH = {'[', 'H', '[', '#', '[', '$', 'i', '#', '[', 'i', '2', 'i', 2, ']'};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vH), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
CHECK(json::from_bjdata(vH, true, false).is_discarded());
// Every "#[" of this chain used to open another dimension vector
// and cost several stack frames before anything was rejected, so a
// long enough chain crashed the process (see #5104). The nested
// vector is refused where it is read, so the length is irrelevant.
std::vector<uint8_t> vRdeep = {'['};
for (std::size_t i = 0; i < 100000; ++i)
{
vRdeep.push_back('#');
vRdeep.push_back('[');
}
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vRdeep), "[json.exception.parse_error.113] parse error at byte 5: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
CHECK(json::from_bjdata(vRdeep, true, false).is_discarded());
}
SECTION("objects")
+85
View File
@@ -1011,6 +1011,91 @@ TEST_CASE("BSON document size mismatch")
}
}
TEST_CASE("BSON nesting does not consume the call stack")
{
// An embedded document or array used to be read by calling back into the
// document reader, so the native call stack grew with the nesting depth of
// the input (#5104). The open documents are kept on a heap stack now.
//
// Deeply nested values must not be compared, copied or dumped here: those
// operations are still recursive and would reintroduce the crash.
// A document nested deeply enough to have crashed. The bytes are built
// here rather than with to_bson(), because the writer still recurses once
// per level and would overflow the stack before the reader is ever
// reached. Every level is
// <int32 size> 0x03 'a' 0x00 <inner document> 0x00
// so a level is eight bytes larger than the one it holds, and the sizes
// can be filled in from the outside in.
const std::size_t depth = 30000;
std::vector<uint8_t> input;
input.reserve(5 + (8 * depth));
for (std::size_t i = 0; i < depth; ++i)
{
const std::uint32_t size = static_cast<std::uint32_t>(5 + (8 * (depth - i)));
input.push_back(static_cast<uint8_t>(size & 0xFF));
input.push_back(static_cast<uint8_t>((size >> 8) & 0xFF));
input.push_back(static_cast<uint8_t>((size >> 16) & 0xFF));
input.push_back(static_cast<uint8_t>((size >> 24) & 0xFF));
input.push_back(0x03); // embedded document
input.push_back('a');
input.push_back(0x00);
}
// the innermost document is empty, then one terminator closes each level
input.insert(input.end(), {0x05, 0x00, 0x00, 0x00, 0x00});
input.insert(input.end(), depth, 0x00);
SECTION("a well-formed deep document is read through the SAX interface")
{
SaxCountdown accept_all(1000000);
CHECK(json::sax_parse(input, &accept_all, json::input_format_t::bson));
}
SECTION("a well-formed deep document is read into a value")
{
json j = json::from_bson(input);
// walked rather than compared: comparing, copying or dumping a value
// this deep is still recursive
std::size_t measured = 0;
const json* q = &j;
while (q->is_object() && !q->empty())
{
q = &q->begin().value();
++measured;
}
CHECK(measured == depth);
}
SECTION("embedded documents and arrays are still read the same way")
{
const json values = {{"a", {{"b", {{"c", 1}}}}}};
CHECK(json::from_bson(json::to_bson(values)) == values);
const json array = {{"a", {1, 2, 3}}};
CHECK(json::from_bson(json::to_bson(array)) == array);
const json mixed = {{"a", {json{{"x", 1}}, json{{"y", 2}}}}};
CHECK(json::from_bson(json::to_bson(mixed)) == mixed);
CHECK(json::from_bson(json::to_bson(json::object())) == json::object());
}
SECTION("a size that does not match is still reported per document")
{
// the embedded document claims one byte too many
std::vector<uint8_t> const bad =
{
0x15, 0x00, 0x00, 0x00, 0x03, 'a', 0x00,
0x0D, 0x00, 0x00, 0x00, 0x08, 'b', 0x00, 0x01, 0x00,
0x00
};
json _;
CHECK_THROWS_AS(_ = json::from_bson(bad), json::parse_error&);
CHECK(json::from_bson(bad, true, false).is_discarded());
}
}
TEST_CASE("BSON numerical data")
{
SECTION("number")
+139
View File
@@ -2035,6 +2035,145 @@ TEST_CASE("CBOR definite length equal to the indefinite-length sentinel")
}
}
TEST_CASE("CBOR nesting does not consume the call stack")
{
// Containers used to be read by calling back into the value reader once
// per element, and a tag by calling it for the tagged value, so the native
// call stack grew with the nesting depth of the input. Each of the three
// costs a single byte to encode -- 0x9F, 0x81 and 0xC2 -- so a payload of
// repeated bytes crashed the process (#5104). The containers are kept on a
// heap stack now, and a tag is read in a loop.
//
// Deeply nested values must not be compared, copied or dumped here: those
// operations are still recursive and would reintroduce the crash.
json _;
SECTION("indefinite-length containers")
{
const std::vector<uint8_t> input(500000, 0x9F);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 500001: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&);
CHECK(json::from_cbor(input, true, false).is_discarded());
}
SECTION("definite-length containers")
{
const std::vector<uint8_t> input(500000, 0x81);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 500001: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&);
CHECK(json::from_cbor(input, true, false).is_discarded());
}
SECTION("tags")
{
// a tag is not a value of its own, so a chain of them used to recurse
const std::vector<uint8_t> input(500000, 0xC2);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input, true, true, json::cbor_tag_handler_t::ignore), "[json.exception.parse_error.110] parse error at byte 500001: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&);
CHECK(json::from_cbor(input, true, false, json::cbor_tag_handler_t::ignore).is_discarded());
}
SECTION("a well-formed deep value is read through the SAX interface")
{
std::vector<uint8_t> input(200000, 0x9F);
input.insert(input.end(), 200000, 0xFF);
SaxCountdown accept_all(1000000);
CHECK(json::sax_parse(input, &accept_all, json::input_format_t::cbor));
}
SECTION("a well-formed deep value is read into a value")
{
const std::size_t depth = 10000;
std::vector<uint8_t> input(depth, 0x81);
input.push_back(0x00);
json j = json::from_cbor(input);
std::size_t measured = 0;
const json* p = &j;
while (p->is_array() && !p->empty())
{
p = &p->front();
++measured;
}
CHECK(measured == depth);
CHECK(p->is_number());
}
SECTION("containers are still read the same way")
{
CHECK(json::from_cbor(std::vector<uint8_t>({0x80})) == json::array());
CHECK(json::from_cbor(std::vector<uint8_t>({0xA0})) == json::object());
CHECK(json::from_cbor(std::vector<uint8_t>({0x9F, 0xFF})) == json::array());
CHECK(json::from_cbor(std::vector<uint8_t>({0xBF, 0xFF})) == json::object());
CHECK(json::from_cbor(std::vector<uint8_t>({0x9F, 0x01, 0x02, 0xFF})) == json({1, 2}));
CHECK(json::from_cbor(std::vector<uint8_t>({0xBF, 0x61, 'a', 0x01, 0xFF})) == json({{"a", 1}}));
// definite and indefinite forms nested inside each other
CHECK(json::from_cbor(std::vector<uint8_t>({0x9F, 0x82, 0x01, 0x02, 0xA1, 0x61, 'k', 0xBF, 0xFF, 0xFF})) == json({{1, 2}, {{"k", json::object()}}}));
}
SECTION("tagged values are still read the same way")
{
const auto ignore = json::cbor_tag_handler_t::ignore;
CHECK(json::from_cbor(std::vector<uint8_t>({0xC2, 0x01}), true, true, ignore) == json(1));
// a chain of tags resolves to the value that follows it
CHECK(json::from_cbor(std::vector<uint8_t>({0xC2, 0xC2, 0xC2, 0x01}), true, true, ignore) == json(1));
// a tag inside a container, and one in front of a container
CHECK(json::from_cbor(std::vector<uint8_t>({0x82, 0xC2, 0x01, 0x02}), true, true, ignore) == json({1, 2}));
CHECK(json::from_cbor(std::vector<uint8_t>({0xC2, 0x82, 0x01, 0x02}), true, true, ignore) == json({1, 2}));
}
}
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
{
// Reading an indefinite-length string or byte array used to call itself
// once per chunk, so a payload of repeated 0x7F (or 0x5F) bytes exhausted
// the call stack before any of the input was rejected. The open levels are
// counted now, and the levels below prove the reader still reads the same
// values and reports the same errors at the same byte offsets.
json _;
SECTION("many open levels are reported, not crashed on")
{
const std::vector<uint8_t> input(200000, 0x7F);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 200001: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
CHECK(json::from_cbor(input, true, false).is_discarded());
}
SECTION("many open levels are reported, not crashed on (binary)")
{
const std::vector<uint8_t> input(200000, 0x5F);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 200001: syntax error while parsing CBOR binary: unexpected end of input", json::parse_error&);
CHECK(json::from_cbor(input, true, false).is_discarded());
}
SECTION("chunks are still concatenated")
{
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0xFF})) == json(""));
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x61, 0x61, 0xFF})) == json("a"));
// nested indefinite-length strings are concatenated across levels
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x61, 0x61, 0xFF, 0x61, 0x62, 0xFF})) == json("ab"));
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x7F, 0x61, 0x7A, 0xFF, 0xFF, 0xFF})) == json("z"));
CHECK(json::from_cbor(std::vector<uint8_t>({0xA1, 0x7F, 0x61, 0x61, 0xFF, 0x01})) == json({{"a", 1}}));
}
SECTION("chunks are still concatenated (binary)")
{
CHECK(json::from_cbor(std::vector<uint8_t>({0x5F, 0x41, 0x61, 0xFF})) == json::binary({0x61}));
CHECK(json::from_cbor(std::vector<uint8_t>({0x5F, 0x5F, 0x41, 0x61, 0xFF, 0x41, 0x62, 0xFF})) == json::binary({0x61, 0x62}));
}
SECTION("a chunk that is not a string is still rejected")
{
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x00})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x00", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x5F, 0x5F, 0x00})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR binary: expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x00", json::parse_error&);
}
SECTION("a break marker outside an indefinite-length string is not a string")
{
// 0xFF only closes a string that was opened; on its own it is not one
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&);
}
}
TEST_CASE("CBOR roundtrips" * doctest::skip())
{
SECTION("input from flynn")
+61
View File
@@ -1598,6 +1598,67 @@ TEST_CASE("MessagePack")
}
// use this testcase outside [hide] to run it with Valgrind
TEST_CASE("MessagePack nesting does not consume the call stack")
{
// Reading a container used to call back into the value reader once per
// element, so the native call stack grew with the nesting depth of the
// input: one frame per byte for repeated 0x91 (a one-element array), which
// crashes the process long before the input is exhausted (#5104). The
// containers are kept on a heap stack now.
//
// Note that deeply nested values must not be compared, copied or dumped
// here: those operations are still recursive, and would reintroduce the
// very crash this checks for. Depth is measured by descending instead.
SECTION("an unterminated chain is reported, not crashed on")
{
json _;
const std::vector<uint8_t> input(300000, 0x91);
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(input), "[json.exception.parse_error.110] parse error at byte 300001: syntax error while parsing MessagePack value: unexpected end of input", json::parse_error&);
CHECK(json::from_msgpack(input, true, false).is_discarded());
}
SECTION("a well-formed deep value is read through the SAX interface")
{
std::vector<uint8_t> input(300000, 0x91);
input.push_back(0x01); // innermost value
SaxCountdown accept_all(600001);
CHECK(json::sax_parse(input, &accept_all, json::input_format_t::msgpack));
}
SECTION("a well-formed deep value is read into a value")
{
const std::size_t depth = 10000;
std::vector<uint8_t> input(depth, 0x91);
input.push_back(0x01);
json j = json::from_msgpack(input);
std::size_t measured = 0;
const json* p = &j;
while (p->is_array() && !p->empty())
{
p = &p->front();
++measured;
}
CHECK(measured == depth);
CHECK(p->is_number());
}
SECTION("containers are still read the same way")
{
CHECK(json::from_msgpack(std::vector<uint8_t>({0x90})) == json::array());
CHECK(json::from_msgpack(std::vector<uint8_t>({0x80})) == json::object());
CHECK(json::from_msgpack(std::vector<uint8_t>({0x92, 0x90, 0x80})) == json({json::array(), json::object()}));
CHECK(json::from_msgpack(std::vector<uint8_t>({0x91, 0x91, 0x91, 0x90})) == json({{{json::array()}}}));
CHECK(json::from_msgpack(std::vector<uint8_t>({0x81, 0xA1, 'a', 0x81, 0xA1, 'b', 0x92, 0x01, 0x02})) == json({{"a", {{"b", {1, 2}}}}}));
// array 16 and map 32, i.e. the counted forms
CHECK(json::from_msgpack(std::vector<uint8_t>({0xDC, 0x00, 0x02, 0x01, 0x02})) == json({1, 2}));
CHECK(json::from_msgpack(std::vector<uint8_t>({0xDF, 0x00, 0x00, 0x00, 0x01, 0xA1, 'k', 0xC3})) == json({{"k", true}}));
}
}
TEST_CASE("single MessagePack roundtrip")
{
SECTION("sample.json")
-808
View File
@@ -239,209 +239,6 @@ class my_allocator : public std::allocator<T>
};
};
/////////////////////////////////////////////////////////////////////
// for #3077
/////////////////////////////////////////////////////////////////////
class FooAlloc
{};
class Foo
{
public:
explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {}
bool value = false;
};
class FooBar
{
public:
Foo foo{}; // NOLINT(readability-redundant-member-init)
};
inline void from_json(const nlohmann::json& j, FooBar& fb) // NOLINT(misc-use-internal-linkage)
{
j.at("value").get_to(fb.foo.value);
}
/////////////////////////////////////////////////////////////////////
// for #3171
/////////////////////////////////////////////////////////////////////
struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions)
{
for_3171_base(const std::string& /*unused*/ = {}) {}
virtual ~for_3171_base();
for_3171_base(const for_3171_base& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default)
: str(other.str)
{}
for_3171_base& operator=(const for_3171_base& other)
{
if (this != &other)
{
str = other.str;
}
return *this;
}
for_3171_base(for_3171_base&& other) noexcept
: str(std::move(other.str))
{}
for_3171_base& operator=(for_3171_base&& other) noexcept
{
if (this != &other)
{
str = std::move(other.str);
}
return *this;
}
virtual void _from_json(const json& j)
{
j.at("str").get_to(str);
}
std::string str{}; // NOLINT(readability-redundant-member-init)
};
for_3171_base::~for_3171_base() = default;
struct for_3171_derived : public for_3171_base
{
for_3171_derived() = default;
~for_3171_derived() override;
explicit for_3171_derived(const std::string& /*unused*/) { }
for_3171_derived(const for_3171_derived& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default)
: for_3171_base(other)
{}
for_3171_derived& operator=(const for_3171_derived& other)
{
if (this != &other)
{
for_3171_base::operator=(other); // Call base class assignment operator
}
return *this;
}
for_3171_derived(for_3171_derived&& other) noexcept
: for_3171_base(std::move(other))
{}
for_3171_derived& operator=(for_3171_derived&& other) noexcept
{
if (this != &other)
{
for_3171_base::operator=(std::move(other)); // Call base class move assignment operator
}
return *this;
}
};
for_3171_derived::~for_3171_derived() = default;
inline void from_json(const json& j, for_3171_base& tb) // NOLINT(misc-use-internal-linkage)
{
tb._from_json(j);
}
/////////////////////////////////////////////////////////////////////
// for #3312
/////////////////////////////////////////////////////////////////////
#ifdef JSON_HAS_CPP_20
struct for_3312
{
std::string name;
};
inline void from_json(const json& j, for_3312& obj) // NOLINT(misc-use-internal-linkage)
{
j.at("name").get_to(obj.name);
}
#endif
/////////////////////////////////////////////////////////////////////
// for #3204
/////////////////////////////////////////////////////////////////////
struct for_3204_foo
{
for_3204_foo() = default;
explicit for_3204_foo(std::string /*unused*/) {} // NOLINT(performance-unnecessary-value-param)
};
struct for_3204_bar
{
enum constructed_from_t // NOLINT(cppcoreguidelines-use-enum-class)
{
constructed_from_none = 0,
constructed_from_foo = 1,
constructed_from_json = 2
};
explicit for_3204_bar(std::function<void(for_3204_foo)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
: constructed_from(constructed_from_foo) {}
explicit for_3204_bar(std::function<void(json)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
: constructed_from(constructed_from_json) {}
constructed_from_t constructed_from = constructed_from_none;
};
/////////////////////////////////////////////////////////////////////
// for #3333
/////////////////////////////////////////////////////////////////////
struct for_3333 final
{
for_3333(int x_ = 0, int y_ = 0) : x(x_), y(y_) {}
template <class T>
for_3333(const T& /*unused*/)
{
CHECK(false);
}
int x = 0;
int y = 0;
};
template <>
inline for_3333::for_3333(const json& j)
: for_3333(j.value("x", 0), j.value("y", 0))
{}
/////////////////////////////////////////////////////////////////////
// for #3810
/////////////////////////////////////////////////////////////////////
struct Example_3810
{
int bla{};
Example_3810() = default;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example_3810, bla) // NOLINT(misc-use-internal-linkage)
/////////////////////////////////////////////////////////////////////
// for #4740
/////////////////////////////////////////////////////////////////////
#ifdef JSON_HAS_CPP_17
struct Example_4740
{
std::optional<std::string> host = std::nullopt;
std::optional<int> port = std::nullopt;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_4740, host, port)
};
#endif
TEST_CASE("regression tests 2")
{
SECTION("issue #1001 - Fix memory leak during parser callback")
@@ -959,611 +756,6 @@ TEST_CASE("regression tests 2")
CHECK(j == k);
}
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
// JSON_HAS_CPP_17 (do not remove; see note at top of file)
SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
{
nlohmann::detail::std_fs::path text_path("/tmp/text.txt");
const json j(text_path);
const auto j_path = j.get<nlohmann::detail::std_fs::path>();
CHECK(j_path == text_path);
#if DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(8, 4, 0)
// only known to work on Clang and GCC >=8.4
CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
#endif
}
#endif
SECTION("issue #3077 - explicit constructor with default does not compile")
{
json j;
j[0]["value"] = true;
std::vector<FooBar> foo;
j.get_to(foo);
}
SECTION("issue #3108 - ordered_json doesn't support range based erase")
{
ordered_json j = {1, 2, 2, 4};
auto last = std::unique(j.begin(), j.end());
j.erase(last, j.end());
CHECK(j.dump() == "[1,2,4]");
j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val)
{
return val == 2;
}), j.end());
CHECK(j.dump() == "[1,4]");
}
SECTION("issue #3343 - json and ordered_json are not interchangeable")
{
json::object_t jobj({ { "product", "one" } });
ordered_json::object_t ojobj({{"product", "one"}});
auto jit = jobj.begin();
auto ojit = ojobj.begin();
CHECK(jit->first == ojit->first);
CHECK(jit->second.get<std::string>() == ojit->second.get<std::string>());
}
SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed")
{
const json j{{ "str", "value"}};
// failed with: error: no match for operator= (operand types are for_3171_derived and const nlohmann::basic_json<>::string_t
// {aka const std::__cxx11::basic_string<char>})
// s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
auto td = j.get<for_3171_derived>();
CHECK(td.str == "value");
}
#ifdef JSON_HAS_CPP_20
SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20")
{
// see test for #3171
const ordered_json j = {{"name", "class"}};
for_3312 obj{};
j.get_to(obj);
CHECK(obj.name == "class");
}
#endif
#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any")
{
const json j;
const std::any a1 = j;
std::any&& a2 = j;
CHECK(a1.type() == typeid(j));
CHECK(a2.type() == typeid(j));
}
#endif
SECTION("issue #3204 - ambiguous regression")
{
const for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param)
const for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param)
CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo);
CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json);
}
SECTION("issue #3333 - Ambiguous conversion from nlohmann::basic_json<> to custom class")
{
const json j
{
{"x", 1},
{"y", 2}
};
const for_3333 p = j;
CHECK(p.x == 1);
CHECK(p.y == 2);
}
SECTION("issue #3810 - ordered_json doesn't support construction from C array of custom type")
{
Example_3810 states[45]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
// fix "not used" warning
states[0].bla = 1;
const auto* const expected = R"([{"bla":1},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0}])";
// This works:
nlohmann::json j;
j["test"] = states;
CHECK(j["test"].dump() == expected);
// This doesn't compile:
nlohmann::ordered_json oj;
oj["test"] = states;
CHECK(oj["test"].dump() == expected);
}
#ifdef JSON_HAS_CPP_17
SECTION("issue #4740 - build issue with std::optional")
{
const auto t1 = Example_4740();
const auto j1 = nlohmann::json(t1);
CHECK(j1.dump() == "{\"host\":null,\"port\":null}");
const auto t2 = j1.get<Example_4740>();
CHECK(!t2.host.has_value());
CHECK(!t2.port.has_value());
// improve coverage
auto t3 = Example_4740();
t3.port = 80;
t3.host = "example.com";
const auto j2 = nlohmann::json(t3);
CHECK(j2.dump() == "{\"host\":\"example.com\",\"port\":80}");
const auto t4 = j2.get<Example_4740>();
CHECK(t4.host.has_value());
CHECK(t4.port.has_value());
}
#endif
#if !defined(_MSVC_LANG)
// MSVC returns garbage on invalid enum values, so this test is excluded
// there.
SECTION("issue #4762 - json exception 302 with unhelpful explanation : type must be number, but is number")
{
// In #4762, the main issue was that a json object with an invalid type
// returned "number" as type_name(), because this was the default case.
// This test makes sure we now return "invalid" instead.
json j;
j.m_data.m_type = static_cast<json::value_t>(100); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange)
CHECK(j.type_name() == "invalid");
}
#endif
#ifdef JSON_HAS_CPP_17
SECTION("issue #4804: from_cbor incompatible with std::vector<std::byte> as binary_t")
{
const std::vector<std::uint8_t> data = {0x80};
const auto decoded = json_4804::from_cbor(data);
CHECK((decoded == json_4804::array()));
}
SECTION("discussion #4209 - custom BinaryType direct assignment and round-tripping")
{
// Test that assigning a custom BinaryType directly creates a binary value, not an array
const std::vector<std::byte> original{std::byte{1}, std::byte{2}, std::byte{3}};
const json_4804 j = original;
CHECK(j.is_binary());
CHECK(!j.is_array());
// Test round-tripping: extracting the binary value back as the custom container type
const auto extracted = j.get<std::vector<std::byte>>();
CHECK(extracted == original);
// Test that the default json alias behavior is unchanged: std::vector<uint8_t> -> array
const json default_json = std::vector<std::uint8_t> {1, 2, 3};
CHECK(default_json.is_array());
CHECK(!default_json.is_binary());
}
SECTION("discussion #4209 - custom BinaryType extraction from parsed array")
{
// Test that extracting a custom BinaryType from a parsed JSON array still works
// (not just from a binary-typed node)
const auto j = json_4804::parse("[1,2,3]");
CHECK(j.is_array());
CHECK(!j.is_binary());
// Extracting as custom BinaryType should work from arrays
const auto extracted = j.get<std::vector<std::byte>>();
CHECK(extracted.size() == 3);
CHECK(extracted[0] == std::byte{1});
CHECK(extracted[1] == std::byte{2});
CHECK(extracted[2] == std::byte{3});
}
SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit")
{
const json jval{};
auto GetValue = [](const json & valRoot) -> std::optional<json>
{
if (valRoot.contains("default"))
{
return valRoot.at("default");
}
return std::nullopt;
};
auto result = GetValue(jval);
CHECK(!result.has_value());
}
#endif
#if JSON_HAS_RANGES == 1
SECTION("issue #4440 - assert when using std::views::filter and GCC 10")
{
auto noOpFilter = std::views::filter([](auto&&) noexcept
{
return true;
});
json j = {1, 2, 3};
auto filtered = j | noOpFilter;
CHECK(*filtered.begin() == 1);
}
#endif
#if JSON_HAS_RANGES && !defined(__MINGW32__)
SECTION("issue #4916 - constructing array from C++20 ranges view does not work")
{
std::vector<int> nums{1, 2, 37, 42, 21};
auto filteredNums = nums | std::views::filter([](int i)
{
return i > 10;
});
json const j(filteredNums);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({37, 42, 21}));
}
#endif
// owning_view is not available in libstdc++ < 12
#if JSON_HAS_RANGES && !defined(__MINGW32__) && !(defined(__GLIBCXX__) && _GLIBCXX_RELEASE < 12)
SECTION("issue #4916 - constructing array from prvalue C++20 ranges view (owning_view)")
{
json const j(std::vector<int> {1, 2, 37, 42, 21} | std::views::filter([](int i)
{
return i > 10;
}));
CHECK(j.type() == json::value_t::array);
CHECK(j == json({37, 42, 21}));
}
#endif
#if JSON_HAS_RANGES && !defined(__MINGW32__)
SECTION("issue #4916 - constructing array from C++20 transform view (prvalue elements)")
{
std::vector<int> nums{1, 2, 3};
auto t = nums | std::views::transform([](int i) noexcept
{
return i * 2;
});
json const j(t);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({2, 4, 6}));
}
#endif
}
TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
{
// With issue #4798, we encode NaN, infinity, and -infinity as float instead
// of double to allow for smaller encodings.
const json jx = std::numeric_limits<T>::quiet_NaN();
const json jy = std::numeric_limits<T>::infinity();
const json jz = -std::numeric_limits<T>::infinity();
/////////////////////////////////////////////////////////////////////////
// MessagePack
/////////////////////////////////////////////////////////////////////////
// expected MessagePack values
const std::vector<std::uint8_t> msgpack_x = {{0xCA, 0x7F, 0xC0, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_y = {{0xCA, 0x7F, 0x80, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_z = {{0xCA, 0xFF, 0x80, 0x00, 0x00}};
CHECK(json::to_msgpack(jx) == msgpack_x);
CHECK(json::to_msgpack(jy) == msgpack_y);
CHECK(json::to_msgpack(jz) == msgpack_z);
CHECK(std::isnan(json::from_msgpack(msgpack_x).get<T>()));
CHECK(json::from_msgpack(msgpack_y).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_msgpack(msgpack_z).get<T>() == -std::numeric_limits<T>::infinity());
// Make sure the other MessagePakc encodings for NaN, infinity, and
// -infinity are still supported.
const std::vector<std::uint8_t> msgpack_x_2 = {{0xCB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_y_2 = {{0xCB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_z_2 = {{0xCB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CHECK(std::isnan(json::from_msgpack(msgpack_x_2).get<T>()));
CHECK(json::from_msgpack(msgpack_y_2).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_msgpack(msgpack_z_2).get<T>() == -std::numeric_limits<T>::infinity());
/////////////////////////////////////////////////////////////////////////
// CBOR
/////////////////////////////////////////////////////////////////////////
// expected CBOR values
const std::vector<std::uint8_t> cbor_x = {{0xF9, 0x7E, 0x00}};
const std::vector<std::uint8_t> cbor_y = {{0xF9, 0x7C, 0x00}};
const std::vector<std::uint8_t> cbor_z = {{0xF9, 0xfC, 0x00}};
CHECK(json::to_cbor(jx) == cbor_x);
CHECK(json::to_cbor(jy) == cbor_y);
CHECK(json::to_cbor(jz) == cbor_z);
CHECK(std::isnan(json::from_cbor(cbor_x).get<T>()));
CHECK(json::from_cbor(cbor_y).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_cbor(cbor_z).get<T>() == -std::numeric_limits<T>::infinity());
// Make sure the other CBOR encodings for NaN, infinity, and -infinity are
// still supported.
const std::vector<std::uint8_t> cbor_x_2 = {{0xFA, 0x7F, 0xC0, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_y_2 = {{0xFA, 0x7F, 0x80, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_z_2 = {{0xFA, 0xFF, 0x80, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_x_3 = {{0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_y_3 = {{0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_z_3 = {{0xFB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CHECK(std::isnan(json::from_cbor(cbor_x_2).get<T>()));
CHECK(json::from_cbor(cbor_y_2).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_cbor(cbor_z_2).get<T>() == -std::numeric_limits<T>::infinity());
CHECK(std::isnan(json::from_cbor(cbor_x_3).get<T>()));
CHECK(json::from_cbor(cbor_y_3).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_cbor(cbor_z_3).get<T>() == -std::numeric_limits<T>::infinity());
}
TEST_CASE("regression test #5074 - portable workaround for single-element brace init")
{
json const j_obj = {{"key", "value"}};
json const j = json::array({j_obj});
CHECK(j.is_array());
CHECK(j.size() == 1);
CHECK(j[0] == j_obj);
}
#if defined(JSON_BRACE_INIT_COPY_SEMANTICS) && (JSON_BRACE_INIT_COPY_SEMANTICS == 1)
TEST_CASE("regression test #5074 - single-element brace init with JSON_BRACE_INIT_COPY_SEMANTICS")
{
// with JSON_BRACE_INIT_COPY_SEMANTICS: single-element brace init copies/moves
json const j_obj = {{"key", "value"}, {"num", 42}};
json const j_arr = {1, 2, 3};
// object: brace init copies instead of wrapping
json const j1{j_obj};
CHECK(j1.is_object());
CHECK(j1 == j_obj);
// array: brace init copies instead of wrapping
json const j2{j_arr};
CHECK(j2.is_array());
CHECK(j2.size() == 3);
CHECK(j2 == j_arr);
// primitives still work as initializer lists
json const j3{true};
CHECK(j3.is_boolean());
json const j4{42};
CHECK(j4.is_number_integer());
}
#endif
struct Example_5122
{
float b = 2;
nlohmann::ordered_map<std::string, std::string> c{}; // NOLINT(readability-redundant-member-init): needed for GCC -Weffc++
int a = 1;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_5122, b, c, a)
};
TEST_CASE("regression test #5122 - from_json into types holding nlohmann::ordered_map")
{
Example_5122 src;
src.c.emplace("first", "1");
src.c.emplace("second", "2");
ordered_json const j = src;
Example_5122 const dst = j.get<Example_5122>();
CHECK(dst.b == src.b);
CHECK(dst.a == src.a);
REQUIRE(dst.c.size() == src.c.size());
auto src_it = src.c.begin();
auto dst_it = dst.c.begin();
for (; src_it != src.c.end(); ++src_it, ++dst_it)
{
CHECK(dst_it->first == src_it->first);
CHECK(dst_it->second == src_it->second);
}
}
// -Wself-assign-overloaded was introduced in Clang 7. Gate the pragma on
// __has_warning so older Clang versions do not error with "unknown warning
// group". The __has_warning check has to stay inside the __clang__ branch
// because GCC does not provide it and would tokenize-error on the argument.
#if defined(__clang__) && defined(__has_warning)
#if __has_warning("-Wself-assign-overloaded")
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wself-assign-overloaded")
#endif
#endif
TEST_CASE("regression test #5122 - nlohmann::ordered_map copy-assignment is self-assignment safe")
{
nlohmann::ordered_map<std::string, std::string> m;
m.emplace("first", "1");
m.emplace("second", "2");
// Insertion order is preserved by ordered_map, so we can check it directly.
m = m;
REQUIRE(m.size() == 2);
auto it = m.begin();
CHECK(it->first == "first");
CHECK(it->second == "1");
++it;
CHECK(it->first == "second");
CHECK(it->second == "2");
}
#if defined(__clang__) && defined(__has_warning)
#if __has_warning("-Wself-assign-overloaded")
DOCTEST_CLANG_SUPPRESS_WARNING_POP
#endif
#endif
TEST_CASE("regression test #5122 - nlohmann::ordered_map move-assignment transfers contents")
{
nlohmann::ordered_map<std::string, std::string> src;
src.emplace("first", "1");
src.emplace("second", "2");
nlohmann::ordered_map<std::string, std::string> dst;
dst.emplace("stale", "x");
dst = std::move(src);
REQUIRE(dst.size() == 2);
auto it = dst.begin();
CHECK(it->first == "first");
CHECK(it->second == "1");
++it;
CHECK(it->first == "second");
CHECK(it->second == "2");
// Re-assigning into the moved-from object must leave it in a usable state.
src = nlohmann::ordered_map<std::string, std::string> {};
src.emplace("after-move", "3");
REQUIRE(src.size() == 1);
CHECK(src.begin()->first == "after-move");
}
// Stand-in for a third-party library (e.g., Eigen as of 3.4, which added
// STL-compatible begin()/end() to its vector types), living in its own
// namespace with its own to_json overload for its vector type.
namespace issue_4320_eigen
{
// "array-compatible" from the library's point of view (it has begin()/end()),
// but for which this (fake) third-party namespace provides its own to_json.
struct vector3
{
double v[3]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-use-default-member-init,modernize-use-default-member-init)
vector3(double x, double y, double z) : v{x, y, z} {} // NOLINT(hicpp-member-init,cppcoreguidelines-pro-type-member-init)
double x() const
{
return v[0];
}
double y() const
{
return v[1];
}
double z() const
{
return v[2];
}
double* begin()
{
return v;
}
double* end()
{
return v + 3;
}
const double* begin() const
{
return v;
}
const double* end() const
{
return v + 3;
}
};
inline void to_json(json& j, const vector3& v) // NOLINT(misc-use-internal-linkage)
{
j = {{"x", v.x()}, {"y", v.y()}, {"z", v.z()}};
}
} // namespace issue_4320_eigen
// The user's own namespace, using the (fake) Eigen type as an implementation
// detail behind a payload type that has nothing to do with vectors/arrays.
namespace issue_4320
{
// Publicly derives from issue_4320_eigen::vector3 but does *not* define its
// own to_json - it is only ever used as a temporary to reach the base
// class's to_json via ADL.
struct vector3_wrapper : issue_4320_eigen::vector3
{
using issue_4320_eigen::vector3::vector3;
};
struct payload
{
double x, y, z;
};
inline vector3_wrapper to_eigen(const payload& p) // NOLINT(misc-use-internal-linkage)
{
return {p.x, p.y, p.z};
}
inline void to_json(json& j, const payload& p) // NOLINT(misc-use-internal-linkage)
{
// Unqualified call, passing a *derived* vector3_wrapper: relies on ADL
// finding issue_4320_eigen::to_json(json&, const vector3&) through the
// vector3 base class, via a derived-to-base conversion. Must NOT resolve
// to the library's own generic array-compatible to_json (an exact-match
// template for vector3_wrapper, since it also has begin()/end()), which
// would serialize this as [x, y, z] instead of {"x":x, "y":y, "z":z}.
to_json(j, to_eigen(p));
}
} // namespace issue_4320
TEST_CASE("issue #4320 - custom base class must not leak nlohmann::detail into ADL")
{
// Before the fix, basic_json unconditionally derived from a type living in
// nlohmann::detail (json_default_base), which made nlohmann::detail an
// associated namespace of every basic_json for ADL purposes. That leaked
// the library's internal generic-array to_json overload into unqualified
// to_json() calls made from user code, silently bypassing user-defined
// to_json overloads reached via a derived-to-base conversion.
const issue_4320::payload p{1.0, 2.0, 3.0};
json j;
to_json(j, p);
CHECK(j == json({{"x", 1.0}, {"y", 2.0}, {"z", 3.0}}));
}
TEST_CASE("issue #5338 - truncated CBOR tagged binary subtype is rejected")
{
const std::vector<std::vector<std::uint8_t>> truncated_tags =
{
{0xD8},
{0xD9, 0x00},
{0xDA, 0x00, 0x00, 0x00},
{0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
for (const auto& data : truncated_tags)
{
CAPTURE(data);
for (const auto tag_handler :
{
json::cbor_tag_handler_t::ignore, json::cbor_tag_handler_t::store
})
{
CAPTURE(tag_handler);
const auto result = json::from_cbor(data, true, false, tag_handler);
CHECK(result.is_discarded());
}
}
}
TEST_CASE("issue #5402 - update(merge_objects=true) overwrites a primitive with an object")
{
json t = {{"k", 1}};
t.update(json{{"k", {{"x", 2}}}}, true);
CHECK(t == json({{"k", {{"x", 2}}}}));
json mixed = {{"keep", {{"a", 1}}}, {"replace", 1}};
mixed.update(json{{"keep", {{"b", 2}}}, {"replace", {{"x", 2}}}}, true);
CHECK(mixed == json({{"keep", {{"a", 1}, {"b", 2}}}, {"replace", {{"x", 2}}}}));
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
+899
View File
@@ -0,0 +1,899 @@
// __ _____ _____ _____
// __| | __| | | | 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
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using ordered_json = nlohmann::ordered_json;
#ifdef JSON_TEST_NO_GLOBAL_UDLS
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif
#include <cstdio>
#include <list>
#include <type_traits>
#include <utility>
#ifdef JSON_HAS_CPP_17
#include <any>
#include <variant>
#endif
#ifdef JSON_HAS_CPP_17
#if __has_include(<optional>)
#include <optional>
#elif __has_include(<experimental/optional>)
#endif
/////////////////////////////////////////////////////////////////////
// for #4804
/////////////////////////////////////////////////////////////////////
using json_4804 = nlohmann::basic_json<std::map, // ObjectType
std::vector, // ArrayType
std::string, // StringType
bool, // BooleanType
std::int64_t, // NumberIntegerType
std::uint64_t, // NumberUnsignedType
double, // NumberFloatType
std::allocator, // AllocatorType
nlohmann::adl_serializer, // JSONSerializer
std::vector<std::byte>, // BinaryType
void // CustomBaseClass
>;
#endif
#ifdef JSON_HAS_CPP_20
#if __has_include(<span>)
#include <span>
#endif
#endif
/////////////////////////////////////////////////////////////////////
// for #4825 - explicitly instantiating basic_json must compile; this
// forces instantiation of binary_writer::write_bjdata_ndarray, whose
// static_cast<string_t> was ambiguous under explicit instantiation on
// C++17. Merely compiling this translation unit is the regression test.
/////////////////////////////////////////////////////////////////////
template class nlohmann::basic_json<>;
/////////////////////////////////////////////////////////////////////
// for #4440
/////////////////////////////////////////////////////////////////////
#if JSON_HAS_RANGES == 1
#include <ranges>
#endif
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
/////////////////////////////////////////////////////////////////////
// for #3077
/////////////////////////////////////////////////////////////////////
class FooAlloc
{};
class Foo
{
public:
explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {}
bool value = false;
};
class FooBar
{
public:
Foo foo{}; // NOLINT(readability-redundant-member-init)
};
inline void from_json(const nlohmann::json& j, FooBar& fb) // NOLINT(misc-use-internal-linkage)
{
j.at("value").get_to(fb.foo.value);
}
/////////////////////////////////////////////////////////////////////
// for #3171
/////////////////////////////////////////////////////////////////////
struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions)
{
for_3171_base(const std::string& /*unused*/ = {}) {}
virtual ~for_3171_base();
for_3171_base(const for_3171_base& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default)
: str(other.str)
{}
for_3171_base& operator=(const for_3171_base& other)
{
if (this != &other)
{
str = other.str;
}
return *this;
}
for_3171_base(for_3171_base&& other) noexcept
: str(std::move(other.str))
{}
for_3171_base& operator=(for_3171_base&& other) noexcept
{
if (this != &other)
{
str = std::move(other.str);
}
return *this;
}
virtual void _from_json(const json& j)
{
j.at("str").get_to(str);
}
std::string str{}; // NOLINT(readability-redundant-member-init)
};
for_3171_base::~for_3171_base() = default;
struct for_3171_derived : public for_3171_base
{
for_3171_derived() = default;
~for_3171_derived() override;
explicit for_3171_derived(const std::string& /*unused*/) { }
for_3171_derived(const for_3171_derived& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default)
: for_3171_base(other)
{}
for_3171_derived& operator=(const for_3171_derived& other)
{
if (this != &other)
{
for_3171_base::operator=(other); // Call base class assignment operator
}
return *this;
}
for_3171_derived(for_3171_derived&& other) noexcept
: for_3171_base(std::move(other))
{}
for_3171_derived& operator=(for_3171_derived&& other) noexcept
{
if (this != &other)
{
for_3171_base::operator=(std::move(other)); // Call base class move assignment operator
}
return *this;
}
};
for_3171_derived::~for_3171_derived() = default;
inline void from_json(const json& j, for_3171_base& tb) // NOLINT(misc-use-internal-linkage)
{
tb._from_json(j);
}
/////////////////////////////////////////////////////////////////////
// for #3312
/////////////////////////////////////////////////////////////////////
#ifdef JSON_HAS_CPP_20
struct for_3312
{
std::string name;
};
inline void from_json(const json& j, for_3312& obj) // NOLINT(misc-use-internal-linkage)
{
j.at("name").get_to(obj.name);
}
#endif
/////////////////////////////////////////////////////////////////////
// for #3204
/////////////////////////////////////////////////////////////////////
struct for_3204_foo
{
for_3204_foo() = default;
explicit for_3204_foo(std::string /*unused*/) {} // NOLINT(performance-unnecessary-value-param)
};
struct for_3204_bar
{
enum constructed_from_t // NOLINT(cppcoreguidelines-use-enum-class)
{
constructed_from_none = 0,
constructed_from_foo = 1,
constructed_from_json = 2
};
explicit for_3204_bar(std::function<void(for_3204_foo)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
: constructed_from(constructed_from_foo) {}
explicit for_3204_bar(std::function<void(json)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
: constructed_from(constructed_from_json) {}
constructed_from_t constructed_from = constructed_from_none;
};
/////////////////////////////////////////////////////////////////////
// for #3333
/////////////////////////////////////////////////////////////////////
struct for_3333 final
{
for_3333(int x_ = 0, int y_ = 0) : x(x_), y(y_) {}
template <class T>
for_3333(const T& /*unused*/)
{
CHECK(false);
}
int x = 0;
int y = 0;
};
template <>
inline for_3333::for_3333(const json& j)
: for_3333(j.value("x", 0), j.value("y", 0))
{}
/////////////////////////////////////////////////////////////////////
// for #3810
/////////////////////////////////////////////////////////////////////
struct Example_3810
{
int bla{};
Example_3810() = default;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example_3810, bla) // NOLINT(misc-use-internal-linkage)
/////////////////////////////////////////////////////////////////////
// for #4740
/////////////////////////////////////////////////////////////////////
#ifdef JSON_HAS_CPP_17
struct Example_4740
{
std::optional<std::string> host = std::nullopt;
std::optional<int> port = std::nullopt;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_4740, host, port)
};
#endif
TEST_CASE("regression tests 3")
{
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
// JSON_HAS_CPP_17 (do not remove; see note at top of file)
SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
{
nlohmann::detail::std_fs::path text_path("/tmp/text.txt");
const json j(text_path);
const auto j_path = j.get<nlohmann::detail::std_fs::path>();
CHECK(j_path == text_path);
#if DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(8, 4, 0)
// only known to work on Clang and GCC >=8.4
CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
#endif
}
#endif
SECTION("issue #3077 - explicit constructor with default does not compile")
{
json j;
j[0]["value"] = true;
std::vector<FooBar> foo;
j.get_to(foo);
}
SECTION("issue #3108 - ordered_json doesn't support range based erase")
{
ordered_json j = {1, 2, 2, 4};
auto last = std::unique(j.begin(), j.end());
j.erase(last, j.end());
CHECK(j.dump() == "[1,2,4]");
j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val)
{
return val == 2;
}), j.end());
CHECK(j.dump() == "[1,4]");
}
SECTION("issue #3343 - json and ordered_json are not interchangeable")
{
json::object_t jobj({ { "product", "one" } });
ordered_json::object_t ojobj({{"product", "one"}});
auto jit = jobj.begin();
auto ojit = ojobj.begin();
CHECK(jit->first == ojit->first);
CHECK(jit->second.get<std::string>() == ojit->second.get<std::string>());
}
SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed")
{
const json j{{ "str", "value"}};
// failed with: error: no match for operator= (operand types are for_3171_derived and const nlohmann::basic_json<>::string_t
// {aka const std::__cxx11::basic_string<char>})
// s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
auto td = j.get<for_3171_derived>();
CHECK(td.str == "value");
}
#ifdef JSON_HAS_CPP_20
SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20")
{
// see test for #3171
const ordered_json j = {{"name", "class"}};
for_3312 obj{};
j.get_to(obj);
CHECK(obj.name == "class");
}
#endif
#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any")
{
const json j;
const std::any a1 = j;
std::any&& a2 = j;
CHECK(a1.type() == typeid(j));
CHECK(a2.type() == typeid(j));
}
#endif
SECTION("issue #3204 - ambiguous regression")
{
const for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param)
const for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param)
CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo);
CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json);
}
SECTION("issue #3333 - Ambiguous conversion from nlohmann::basic_json<> to custom class")
{
const json j
{
{"x", 1},
{"y", 2}
};
const for_3333 p = j;
CHECK(p.x == 1);
CHECK(p.y == 2);
}
SECTION("issue #3810 - ordered_json doesn't support construction from C array of custom type")
{
Example_3810 states[45]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
// fix "not used" warning
states[0].bla = 1;
const auto* const expected = R"([{"bla":1},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0}])";
// This works:
nlohmann::json j;
j["test"] = states;
CHECK(j["test"].dump() == expected);
// This doesn't compile:
nlohmann::ordered_json oj;
oj["test"] = states;
CHECK(oj["test"].dump() == expected);
}
#ifdef JSON_HAS_CPP_17
SECTION("issue #4740 - build issue with std::optional")
{
const auto t1 = Example_4740();
const auto j1 = nlohmann::json(t1);
CHECK(j1.dump() == "{\"host\":null,\"port\":null}");
const auto t2 = j1.get<Example_4740>();
CHECK(!t2.host.has_value());
CHECK(!t2.port.has_value());
// improve coverage
auto t3 = Example_4740();
t3.port = 80;
t3.host = "example.com";
const auto j2 = nlohmann::json(t3);
CHECK(j2.dump() == "{\"host\":\"example.com\",\"port\":80}");
const auto t4 = j2.get<Example_4740>();
CHECK(t4.host.has_value());
CHECK(t4.port.has_value());
}
#endif
#if !defined(_MSVC_LANG)
// MSVC returns garbage on invalid enum values, so this test is excluded
// there.
SECTION("issue #4762 - json exception 302 with unhelpful explanation : type must be number, but is number")
{
// In #4762, the main issue was that a json object with an invalid type
// returned "number" as type_name(), because this was the default case.
// This test makes sure we now return "invalid" instead.
json j;
j.m_data.m_type = static_cast<json::value_t>(100); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange)
CHECK(j.type_name() == "invalid");
}
#endif
#ifdef JSON_HAS_CPP_17
SECTION("issue #4804: from_cbor incompatible with std::vector<std::byte> as binary_t")
{
const std::vector<std::uint8_t> data = {0x80};
const auto decoded = json_4804::from_cbor(data);
CHECK((decoded == json_4804::array()));
}
SECTION("discussion #4209 - custom BinaryType direct assignment and round-tripping")
{
// Test that assigning a custom BinaryType directly creates a binary value, not an array
const std::vector<std::byte> original{std::byte{1}, std::byte{2}, std::byte{3}};
const json_4804 j = original;
CHECK(j.is_binary());
CHECK(!j.is_array());
// Test round-tripping: extracting the binary value back as the custom container type
const auto extracted = j.get<std::vector<std::byte>>();
CHECK(extracted == original);
// Test that the default json alias behavior is unchanged: std::vector<uint8_t> -> array
const json default_json = std::vector<std::uint8_t> {1, 2, 3};
CHECK(default_json.is_array());
CHECK(!default_json.is_binary());
}
SECTION("discussion #4209 - custom BinaryType extraction from parsed array")
{
// Test that extracting a custom BinaryType from a parsed JSON array still works
// (not just from a binary-typed node)
const auto j = json_4804::parse("[1,2,3]");
CHECK(j.is_array());
CHECK(!j.is_binary());
// Extracting as custom BinaryType should work from arrays
const auto extracted = j.get<std::vector<std::byte>>();
CHECK(extracted.size() == 3);
CHECK(extracted[0] == std::byte{1});
CHECK(extracted[1] == std::byte{2});
CHECK(extracted[2] == std::byte{3});
}
SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit")
{
const json jval{};
auto GetValue = [](const json & valRoot) -> std::optional<json>
{
if (valRoot.contains("default"))
{
return valRoot.at("default");
}
return std::nullopt;
};
auto result = GetValue(jval);
CHECK(!result.has_value());
}
#endif
#if JSON_HAS_RANGES == 1
SECTION("issue #4440 - assert when using std::views::filter and GCC 10")
{
auto noOpFilter = std::views::filter([](auto&&) noexcept
{
return true;
});
json j = {1, 2, 3};
auto filtered = j | noOpFilter;
CHECK(*filtered.begin() == 1);
}
#endif
#if JSON_HAS_RANGES && !defined(__MINGW32__)
SECTION("issue #4916 - constructing array from C++20 ranges view does not work")
{
std::vector<int> nums{1, 2, 37, 42, 21};
auto filteredNums = nums | std::views::filter([](int i)
{
return i > 10;
});
json const j(filteredNums);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({37, 42, 21}));
}
#endif
// owning_view is not available in libstdc++ < 12
#if JSON_HAS_RANGES && !defined(__MINGW32__) && !(defined(__GLIBCXX__) && _GLIBCXX_RELEASE < 12)
SECTION("issue #4916 - constructing array from prvalue C++20 ranges view (owning_view)")
{
json const j(std::vector<int> {1, 2, 37, 42, 21} | std::views::filter([](int i)
{
return i > 10;
}));
CHECK(j.type() == json::value_t::array);
CHECK(j == json({37, 42, 21}));
}
#endif
#if JSON_HAS_RANGES && !defined(__MINGW32__)
SECTION("issue #4916 - constructing array from C++20 transform view (prvalue elements)")
{
std::vector<int> nums{1, 2, 3};
auto t = nums | std::views::transform([](int i) noexcept
{
return i * 2;
});
json const j(t);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({2, 4, 6}));
}
#endif
}
TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
{
// With issue #4798, we encode NaN, infinity, and -infinity as float instead
// of double to allow for smaller encodings.
const json jx = std::numeric_limits<T>::quiet_NaN();
const json jy = std::numeric_limits<T>::infinity();
const json jz = -std::numeric_limits<T>::infinity();
/////////////////////////////////////////////////////////////////////////
// MessagePack
/////////////////////////////////////////////////////////////////////////
// expected MessagePack values
const std::vector<std::uint8_t> msgpack_x = {{0xCA, 0x7F, 0xC0, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_y = {{0xCA, 0x7F, 0x80, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_z = {{0xCA, 0xFF, 0x80, 0x00, 0x00}};
CHECK(json::to_msgpack(jx) == msgpack_x);
CHECK(json::to_msgpack(jy) == msgpack_y);
CHECK(json::to_msgpack(jz) == msgpack_z);
CHECK(std::isnan(json::from_msgpack(msgpack_x).get<T>()));
CHECK(json::from_msgpack(msgpack_y).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_msgpack(msgpack_z).get<T>() == -std::numeric_limits<T>::infinity());
// Make sure the other MessagePakc encodings for NaN, infinity, and
// -infinity are still supported.
const std::vector<std::uint8_t> msgpack_x_2 = {{0xCB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_y_2 = {{0xCB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> msgpack_z_2 = {{0xCB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CHECK(std::isnan(json::from_msgpack(msgpack_x_2).get<T>()));
CHECK(json::from_msgpack(msgpack_y_2).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_msgpack(msgpack_z_2).get<T>() == -std::numeric_limits<T>::infinity());
/////////////////////////////////////////////////////////////////////////
// CBOR
/////////////////////////////////////////////////////////////////////////
// expected CBOR values
const std::vector<std::uint8_t> cbor_x = {{0xF9, 0x7E, 0x00}};
const std::vector<std::uint8_t> cbor_y = {{0xF9, 0x7C, 0x00}};
const std::vector<std::uint8_t> cbor_z = {{0xF9, 0xfC, 0x00}};
CHECK(json::to_cbor(jx) == cbor_x);
CHECK(json::to_cbor(jy) == cbor_y);
CHECK(json::to_cbor(jz) == cbor_z);
CHECK(std::isnan(json::from_cbor(cbor_x).get<T>()));
CHECK(json::from_cbor(cbor_y).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_cbor(cbor_z).get<T>() == -std::numeric_limits<T>::infinity());
// Make sure the other CBOR encodings for NaN, infinity, and -infinity are
// still supported.
const std::vector<std::uint8_t> cbor_x_2 = {{0xFA, 0x7F, 0xC0, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_y_2 = {{0xFA, 0x7F, 0x80, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_z_2 = {{0xFA, 0xFF, 0x80, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_x_3 = {{0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_y_3 = {{0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
const std::vector<std::uint8_t> cbor_z_3 = {{0xFB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CHECK(std::isnan(json::from_cbor(cbor_x_2).get<T>()));
CHECK(json::from_cbor(cbor_y_2).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_cbor(cbor_z_2).get<T>() == -std::numeric_limits<T>::infinity());
CHECK(std::isnan(json::from_cbor(cbor_x_3).get<T>()));
CHECK(json::from_cbor(cbor_y_3).get<T>() == std::numeric_limits<T>::infinity());
CHECK(json::from_cbor(cbor_z_3).get<T>() == -std::numeric_limits<T>::infinity());
}
TEST_CASE("regression test #5074 - portable workaround for single-element brace init")
{
json const j_obj = {{"key", "value"}};
json const j = json::array({j_obj});
CHECK(j.is_array());
CHECK(j.size() == 1);
CHECK(j[0] == j_obj);
}
#if defined(JSON_BRACE_INIT_COPY_SEMANTICS) && (JSON_BRACE_INIT_COPY_SEMANTICS == 1)
TEST_CASE("regression test #5074 - single-element brace init with JSON_BRACE_INIT_COPY_SEMANTICS")
{
// with JSON_BRACE_INIT_COPY_SEMANTICS: single-element brace init copies/moves
json const j_obj = {{"key", "value"}, {"num", 42}};
json const j_arr = {1, 2, 3};
// object: brace init copies instead of wrapping
json const j1{j_obj};
CHECK(j1.is_object());
CHECK(j1 == j_obj);
// array: brace init copies instead of wrapping
json const j2{j_arr};
CHECK(j2.is_array());
CHECK(j2.size() == 3);
CHECK(j2 == j_arr);
// primitives still work as initializer lists
json const j3{true};
CHECK(j3.is_boolean());
json const j4{42};
CHECK(j4.is_number_integer());
}
#endif
struct Example_5122
{
float b = 2;
nlohmann::ordered_map<std::string, std::string> c{}; // NOLINT(readability-redundant-member-init): needed for GCC -Weffc++
int a = 1;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_5122, b, c, a)
};
TEST_CASE("regression test #5122 - from_json into types holding nlohmann::ordered_map")
{
Example_5122 src;
src.c.emplace("first", "1");
src.c.emplace("second", "2");
ordered_json const j = src;
Example_5122 const dst = j.get<Example_5122>();
CHECK(dst.b == src.b);
CHECK(dst.a == src.a);
REQUIRE(dst.c.size() == src.c.size());
auto src_it = src.c.begin();
auto dst_it = dst.c.begin();
for (; src_it != src.c.end(); ++src_it, ++dst_it)
{
CHECK(dst_it->first == src_it->first);
CHECK(dst_it->second == src_it->second);
}
}
// -Wself-assign-overloaded was introduced in Clang 7. Gate the pragma on
// __has_warning so older Clang versions do not error with "unknown warning
// group". The __has_warning check has to stay inside the __clang__ branch
// because GCC does not provide it and would tokenize-error on the argument.
#if defined(__clang__) && defined(__has_warning)
#if __has_warning("-Wself-assign-overloaded")
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wself-assign-overloaded")
#endif
#endif
TEST_CASE("regression test #5122 - nlohmann::ordered_map copy-assignment is self-assignment safe")
{
nlohmann::ordered_map<std::string, std::string> m;
m.emplace("first", "1");
m.emplace("second", "2");
// Insertion order is preserved by ordered_map, so we can check it directly.
m = m;
REQUIRE(m.size() == 2);
auto it = m.begin();
CHECK(it->first == "first");
CHECK(it->second == "1");
++it;
CHECK(it->first == "second");
CHECK(it->second == "2");
}
#if defined(__clang__) && defined(__has_warning)
#if __has_warning("-Wself-assign-overloaded")
DOCTEST_CLANG_SUPPRESS_WARNING_POP
#endif
#endif
TEST_CASE("regression test #5122 - nlohmann::ordered_map move-assignment transfers contents")
{
nlohmann::ordered_map<std::string, std::string> src;
src.emplace("first", "1");
src.emplace("second", "2");
nlohmann::ordered_map<std::string, std::string> dst;
dst.emplace("stale", "x");
dst = std::move(src);
REQUIRE(dst.size() == 2);
auto it = dst.begin();
CHECK(it->first == "first");
CHECK(it->second == "1");
++it;
CHECK(it->first == "second");
CHECK(it->second == "2");
// Re-assigning into the moved-from object must leave it in a usable state.
src = nlohmann::ordered_map<std::string, std::string> {};
src.emplace("after-move", "3");
REQUIRE(src.size() == 1);
CHECK(src.begin()->first == "after-move");
}
// Stand-in for a third-party library (e.g., Eigen as of 3.4, which added
// STL-compatible begin()/end() to its vector types), living in its own
// namespace with its own to_json overload for its vector type.
namespace issue_4320_eigen
{
// "array-compatible" from the library's point of view (it has begin()/end()),
// but for which this (fake) third-party namespace provides its own to_json.
struct vector3
{
double v[3]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-use-default-member-init,modernize-use-default-member-init)
vector3(double x, double y, double z) : v{x, y, z} {} // NOLINT(hicpp-member-init,cppcoreguidelines-pro-type-member-init)
double x() const
{
return v[0];
}
double y() const
{
return v[1];
}
double z() const
{
return v[2];
}
double* begin()
{
return v;
}
double* end()
{
return v + 3;
}
const double* begin() const
{
return v;
}
const double* end() const
{
return v + 3;
}
};
inline void to_json(json& j, const vector3& v) // NOLINT(misc-use-internal-linkage)
{
j = {{"x", v.x()}, {"y", v.y()}, {"z", v.z()}};
}
} // namespace issue_4320_eigen
// The user's own namespace, using the (fake) Eigen type as an implementation
// detail behind a payload type that has nothing to do with vectors/arrays.
namespace issue_4320
{
// Publicly derives from issue_4320_eigen::vector3 but does *not* define its
// own to_json - it is only ever used as a temporary to reach the base
// class's to_json via ADL.
struct vector3_wrapper : issue_4320_eigen::vector3
{
using issue_4320_eigen::vector3::vector3;
};
struct payload
{
double x, y, z;
};
inline vector3_wrapper to_eigen(const payload& p) // NOLINT(misc-use-internal-linkage)
{
return {p.x, p.y, p.z};
}
inline void to_json(json& j, const payload& p) // NOLINT(misc-use-internal-linkage)
{
// Unqualified call, passing a *derived* vector3_wrapper: relies on ADL
// finding issue_4320_eigen::to_json(json&, const vector3&) through the
// vector3 base class, via a derived-to-base conversion. Must NOT resolve
// to the library's own generic array-compatible to_json (an exact-match
// template for vector3_wrapper, since it also has begin()/end()), which
// would serialize this as [x, y, z] instead of {"x":x, "y":y, "z":z}.
to_json(j, to_eigen(p));
}
} // namespace issue_4320
TEST_CASE("issue #4320 - custom base class must not leak nlohmann::detail into ADL")
{
// Before the fix, basic_json unconditionally derived from a type living in
// nlohmann::detail (json_default_base), which made nlohmann::detail an
// associated namespace of every basic_json for ADL purposes. That leaked
// the library's internal generic-array to_json overload into unqualified
// to_json() calls made from user code, silently bypassing user-defined
// to_json overloads reached via a derived-to-base conversion.
const issue_4320::payload p{1.0, 2.0, 3.0};
json j;
to_json(j, p);
CHECK(j == json({{"x", 1.0}, {"y", 2.0}, {"z", 3.0}}));
}
TEST_CASE("issue #5338 - truncated CBOR tagged binary subtype is rejected")
{
const std::vector<std::vector<std::uint8_t>> truncated_tags =
{
{0xD8},
{0xD9, 0x00},
{0xDA, 0x00, 0x00, 0x00},
{0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
for (const auto& data : truncated_tags)
{
CAPTURE(data);
for (const auto tag_handler :
{
json::cbor_tag_handler_t::ignore, json::cbor_tag_handler_t::store
})
{
CAPTURE(tag_handler);
const auto result = json::from_cbor(data, true, false, tag_handler);
CHECK(result.is_discarded());
}
}
}
TEST_CASE("issue #5402 - update(merge_objects=true) overwrites a primitive with an object")
{
json t = {{"k", 1}};
t.update(json{{"k", {{"x", 2}}}}, true);
CHECK(t == json({{"k", {{"x", 2}}}}));
json mixed = {{"keep", {{"a", 1}}}, {"replace", 1}};
mixed.update(json{{"keep", {{"b", 2}}}, {"replace", {{"x", 2}}}}, true);
CHECK(mixed == json({{"keep", {{"a", 1}, {"b", 2}}}, {"replace", {{"x", 2}}}}));
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
+166
View File
@@ -2149,6 +2149,172 @@ TEST_CASE("UBJSON")
}
}
TEST_CASE("UBJSON nesting does not consume the call stack")
{
// Containers used to be read by calling back into the value reader once
// per element, so the native call stack grew with the nesting depth of the
// input. '[' alone opens a container, so a payload of repeated '[' crashed
// the process (#5104), as did the optimized forms, which reach the same
// path through a type or size annotation. The containers are kept on a
// heap stack now.
//
// Deeply nested values must not be compared, copied or dumped here: those
// operations are still recursive and would reintroduce the crash.
json _;
SECTION("containers that end at a marker")
{
const std::vector<uint8_t> input(500000, '[');
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(input), "[json.exception.parse_error.110] parse error at byte 500001: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&);
CHECK(json::from_ubjson(input, true, false).is_discarded());
}
SECTION("containers with a size")
{
std::vector<uint8_t> input;
for (std::size_t i = 0; i < 100000; ++i)
{
input.push_back('[');
input.push_back('#');
input.push_back('i');
input.push_back(1);
}
CHECK_THROWS_AS(_ = json::from_ubjson(input), json::parse_error&);
CHECK(json::from_ubjson(input, true, false).is_discarded());
}
SECTION("containers with a type and a size")
{
// '[' is a permitted optimized type in UBJSON, so each element of such
// a container is itself a container, read without a marker of its own
std::vector<uint8_t> input;
for (std::size_t i = 0; i < 100000; ++i)
{
const std::vector<uint8_t> level = {'[', '$', '[', '#', 'i', 1};
input.insert(input.end(), level.begin(), level.end());
}
CHECK_THROWS_AS(_ = json::from_ubjson(input), json::parse_error&);
CHECK(json::from_ubjson(input, true, false).is_discarded());
}
SECTION("a well-formed deep value is read through the SAX interface")
{
std::vector<uint8_t> input(100000, '[');
input.insert(input.end(), 100000, ']');
SaxCountdown accept_all(1000000);
CHECK(json::sax_parse(input, &accept_all, json::input_format_t::ubjson));
}
SECTION("a well-formed deep value is read into a value")
{
const std::size_t depth = 10000;
std::vector<uint8_t> input(depth, '[');
input.insert(input.end(), depth, ']');
json j = json::from_ubjson(input);
std::size_t measured = 0;
const json* p = &j;
while (p->is_array() && !p->empty())
{
p = &p->front();
++measured;
}
// the innermost array is empty, so the descent stops one level short
CHECK(measured == depth - 1);
}
SECTION("containers are still read the same way")
{
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', ']'})) == json::array());
CHECK(json::from_ubjson(std::vector<uint8_t>({'{', '}'})) == json::object());
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '#', 'i', 0})) == json::array());
CHECK(json::from_ubjson(std::vector<uint8_t>({'{', '#', 'i', 0})) == json::object());
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'i', '#', 'i', 2, 1, 2})) == json({1, 2}));
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '#', 'i', 2, 'i', 1, 'i', 2})) == json({1, 2}));
CHECK(json::from_ubjson(std::vector<uint8_t>({'{', '$', 'i', '#', 'i', 1, 'i', 1, 'a', 1})) == json({{"a", 1}}));
// a no-op is not a value, so a container of them holds none
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'N', '#', 'i', 2})) == json::array());
// sized and unsized forms nested inside one another
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '[', '#', 'i', 2, 'i', 1, 'i', 2, ']'})) == json({{1, 2}}));
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '#', 'i', 1, '[', 'i', 1, ']'})) == json({{1}}));
// an optimized container of containers
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', '[', '#', 'i', 2, 'i', 1, ']', 'i', 2, ']'})) == json({{1}, {2}}));
}
SECTION("BJData containers are still read the same way")
{
// the ND-array wrapper and the binary shortcut are complete values,
// not containers the reader descends into
CHECK(json::from_bjdata(std::vector<uint8_t>({'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 1, 2, 3, 4, 5, 6})) ==
json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 3}}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}}));
CHECK(json::from_bjdata(std::vector<uint8_t>({'[', '$', 'i', '#', 'i', 2, 1, 2})) == json({1, 2}));
CHECK(json::from_bjdata(std::vector<uint8_t>({'[', '[', 'i', 1, ']', ']'})) == json({{1}}));
}
}
TEST_CASE("UBJSON optimized arrays of a valueless type are bounded")
{
// An element of type 'Z', 'T' or 'F' is encoded by its marker alone, so an
// optimized array of one of those has no payload and the declared count is
// the only thing deciding how much is allocated. Ten bytes used to produce
// billions of values (#2793); every other type costs at least one byte per
// element and is bounded by the end of the input.
json _;
SECTION("an excessive count is rejected")
{
// 'l' is a big-endian int32: 0x7FFFFFFF elements, about 34 GB of value
for (const auto marker :
{'Z', 'T', 'F'
})
{
const std::vector<uint8_t> input = {'[', '$', static_cast<uint8_t>(marker), '#', 'l', 0x7F, 0xFF, 0xFF, 0xFF};
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(input), "[json.exception.out_of_range.408] syntax error while parsing UBJSON size: excessive array size", json::out_of_range&);
CHECK(json::from_ubjson(input, true, false).is_discarded());
}
}
SECTION("ordinary counts are unaffected")
{
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'Z', '#', 'i', 3})) == json({nullptr, nullptr, nullptr}));
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'T', '#', 'i', 2})) == json({true, true}));
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'F', '#', 'i', 2})) == json({false, false}));
// 'N' is a no-op rather than a value, and still yields an empty array
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'N', '#', 'i', 2})) == json::array());
}
SECTION("a type with a payload is unaffected")
{
// A count past the limit is not rejected for 'U', which costs a byte
// per element and is bounded by the end of the input instead. The
// count is kept just past the limit rather than made huge, because a
// count that also exceeds the array's max_size() is reported as
// out_of_range before the input runs out, and max_size() depends on
// the width of std::size_t.
const std::vector<uint8_t> input = {'[', '$', 'U', '#', 'l', 0x00, 0x10, 0x00, 0x01};
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(input), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&);
CHECK(json::from_ubjson(input, true, false).is_discarded());
}
SECTION("the writer stays within what the reader accepts")
{
// below the limit the optimized form is used and is tiny; above it the
// writer falls back so that the result can still be read back
json const at_limit(1048576, nullptr);
const auto v_at_limit = json::to_ubjson(at_limit, true, true);
CHECK(v_at_limit.size() == 9);
CHECK(v_at_limit.at(1) == '$');
CHECK(json::from_ubjson(v_at_limit) == at_limit);
json const above_limit(1048577, nullptr);
const auto v_above_limit = json::to_ubjson(above_limit, true, true);
CHECK(v_above_limit.at(1) != '$');
CHECK(json::from_ubjson(v_above_limit) == above_limit);
}
}
TEST_CASE("Universal Binary JSON Specification Examples 1")
{
SECTION("Null Value")