Compare commits

...
Author SHA1 Message Date
Niels Lohmann 247b03b2de Regenerate the amalgamated headers
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:29:57 +02:00
Niels Lohmann 56ff716d88 Mark the unsigned long of the MSVC bit scans for cpplint
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:29:56 +02:00
Niels Lohmann 9bbd0dafb0 Fix clang-tidy 22 findings in unit-class_lexer.cpp
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:29:55 +02:00
Niels Lohmann 1c8437a724 Reword float conversion notes for the string type and number_float_t docs
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:29:54 +02:00
Niels Lohmann 934f6644d7 Load string scan words with memcpy and use MSVC bit-scan intrinsics
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:29:53 +02:00
Niels Lohmann 1795f59f10 Fix CI warnings in own float parser
- pow5_table.hpp: pow5_128_largest_power was unused in this branch's
  own code (GCC -Werror=unused-const-variable); tie it to the table
  size with a static_assert instead of removing it, since a later
  branch in the stack (json-view/23-zmij) uses it.
- number_parse.hpp: rename the local variable `copy` to `buffer` to
  satisfy cpplint's build/include_what_you_use check.
- unit-class_lexer.cpp: extend the NOLINT list on the seeded mt19937
  with bugprone-random-generator-seed, and parenthesize
  `8 * sizeof(Bits) - 1` for clang-tidy.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:29:53 +02:00
Niels Lohmann 2434cb6e72 Speed up the lexer: own float parser, string scan, and \u table
Give the library its own correctly rounded float converter for
binary32 and binary64 (IEEE 754), and speed up the lexer's string
and escape scanning.

The converter splits a number token into sign, significand, and
decimal exponent, then tries Clinger's fast path, then a templated
Eisel-Lemire step, and falls back to an exact big-integer digit
comparison for tokens with more than 19 significant digits whose two
candidate values round differently. This replaces std::from_chars
and strtod/strtof for both formats, so parsed values no longer
depend on the C/C++ library or the current locale. The strtold
fallback kept for other long double formats (x87, binary128) now
also copies a multi-byte decimal point correctly, fixing #5660.
eisel_lemire() and decimal_to_float() are always inlined so callers
keep the whole conversion in their hot loop.

The string-scanning kernels in string_scan.hpp find a stop byte with
the trailing-zero count of the SWAR mask instead of a byte loop, and
scalar_string_bulk_run() validates a run of multi-byte UTF-8
sequences one after another instead of re-searching after each one.

get_codepoint() decodes a contiguous \uXXXX escape with one table
lookup per byte instead of four range-checked get() calls; the
streaming path and all error positions are unchanged.

Adds 508 generated hard float-parsing cases with expected binary32
and binary64 bits, and kernel-comparison tests for the string scans
and the escape table against byte-by-byte references.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:29:52 +02:00
Niels Lohmann e8b747e4bc Re-amalgamate single_include after #5795 and #5806 (#5808)
#5795 and #5806 changed binary_writer.hpp but were merged without
updated amalgamated files, so the check_amalgamation run on develop
(c59ad9525) fails. This is the output of 'make amalgamate'; no source
files change.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 11:26:11 +02:00
Niels Lohmann c59ad95252 Expect type_error.321 for deep discarded values in the BON8 writer test (#5807)
#5806 added the test while #5802 made to_bon8 reject discarded values; both
merged, so the test failed on develop.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 10:27:16 +02:00
Niels LohmannandClaude Opus 5.5 b38a2c9177 Stop the BON8 writer overflowing the stack on deep values (#5806)
* Stop the BON8 writer overflowing the stack on deep values

to_bon8() recursed once per nesting level, so a value the iterative
BON8 reader accepts (e.g. ~24k nested one-element arrays) crashed on
the way back out. #5781 bounded the CBOR, MessagePack, and UBJSON/BJData
writers, but BON8 was merged before it and was not covered.

Apply the same scheme: recurse for the first recursion_depth_limit()
levels, then finish the value with write_bon8_iterative, which keeps
the open containers on a heap stack (reusing binary_container_frame)
and writes the 0xFE closer when it leaves a container with more than
four elements. The output is byte-for-byte unchanged.

Fixes https://issues.oss-fuzz.com/issues/572238015

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

* Merge the array and object branches of the BON8 iterative writer

write_bon8_iterative and write_bon8_value_or_push handled arrays and
objects in separate branches that repeated the end-of-container check,
the 0xFE closer and the marker computation. Share those parts and branch
only where arrays and objects really differ. The output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hz7VJi1FTKr6gpseLErbbS
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 08:24:35 +02:00
Niels Lohmann eb8899a29c Restore v3.12.0 support for custom object key types (#5795)
* Restore v3.12.0 support for custom object key types

Custom object_t types whose key_type is not string_t compiled with
v3.12.0 for several APIs that unreleased changes broke:

- to_bson failed for every custom key type (#5553 kept a const string_t*
  to the key); the nested entry's header is now written where the entry
  is found.
- Copying deep values (and parse, merge_patch, update, insert) required
  operator== on keys (#5389); keys without one are now paired via find().
- to_cbor/to_msgpack required an implicit conversion to string_t (#5746,
  #5328); keys without one go through a temporary basic_json again.
- at() required a conversion to string_t for its error message (#5727);
  other keys are passed to concat() unchanged again.

The new unit-custom-object-key-type.cpp covers five key types with
different capabilities.

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

* Use the with_object_t alias for the custom object key test types

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

* Avoid floating-point equality in custom key type test

GCC with -Werror=float-equal rejects comparing the double value with ==.

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

* Write the head of nested BSON elements in one helper

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

* Suppress bugprone-return-const-ref-from-parameter in key_for_message

The reference is only passed to concat() within the full-expression that
holds the key, like the similar helpers in binary_writer.hpp.

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

* Make the value of the custom test key types private

clang-tidy (cppcoreguidelines-non-private-member-variables-in-classes)
rejects the protected member; the derived key types use a protected
accessor instead.

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

* Pass keys with data() and size() unchanged into the at() miss message

key_for_message() converted every key that string_t can be constructed
from, so a miss on a string_t or string_view key copied it before
concat() copied it again. Keys that concat() can append through data()
and size() are now passed through; only other keys (string literals,
key types that just convert to string_t) are converted.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 08:24:31 +02:00
Niels Lohmann 11ef352511 Point users to update(…, true), JSON Pointer defaults, and built-in std::optional (#5804)
A code search of client code found many hand-written versions of
functionality the library already has: recursive object merges
(update(j, true) since 3.10.5), dotted-path getters (value() and
contains() with a JSON Pointer), get_or helpers (value() throws for a
member that is present but null), and adl_serializer specializations
for std::optional (supported since 3.12.0).

- modifying_values.md: describe both modes of update(), add a
  defaults + user settings recipe, and stop recommending Merge Patch
  for recursive merges.
- merge_patch.md: note that null deletes keys, so a merge patch is not
  a general deep merge; link update().
- default_value.md: add "Nested values" (value/contains with a JSON
  Pointer, building a pointer from a dotted path with operator/=) and
  a warning that null and mistyped members are not missing.
- value.md: note that a null member is converted, not replaced by the
  default.
- json_pointer.md: link value/contains/at with JSON Pointers.
- arbitrary_types.md: note that std::optional needs no serializer.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 08:22:10 +02:00
Niels Lohmann 74a03dec84 Throw type_error.321 for discarded values in to_bon8 (#5802)
write_bon8_value silently skipped discarded values, but the array/object
count marker still counted them, so [1, discarded] produced 82 91: a marker
announcing two elements followed by one. Throw type_error.321 like the other
binary writers do, at any nesting level.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 08:22:00 +02:00
Niels Lohmann 6197341a21 Clean up non-code files (#5801)
* Clean up README, contribution guide, and repository metadata

- REUSE.toml: fix the Hedley path and SPDX id (CC0-1.0), mark the docset
  icons as the public-domain JSON logo
- CITATION.cff: v3.12.0 was released on 2025-04-11
- FILES.md: list all workflows, describe the Meson option defaults
- README: ctest -LE, table of contents, typos, stale Android/MinGW advice,
  moved links, merge duplicate Thanks entries, list the analysis tools
  used in CI
- CONTRIBUTING: iterative parser, json_literals.hpp is generated, links
- .gitignore: backups and release outputs; .gitattributes: mark
  generated files
- labeler: label other build systems and .github documentation
- MODULE.bazel: add the module version

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

* Clean up CI, CMake, Makefile, and Bazel files

- CMakeLists.txt: avoid VERSION_GREATER_EQUAL, which CMake < 3.7 lacks
- ci.cmake: test JSON_DisableTupleReferenceConversion in ci_cmake_flags,
  remove unused variables and unreachable per-compiler targets, look up
  Clang tools consistently, forward CMAKE_CXX_FLAGS to ci_module_cpp20,
  format json_literals.hpp and remove backups in ci_test_amalgamation
- BUILD.bazel: add json_literals.hpp to the single-header target
- workflows: format json_literals.hpp before copying it, drop the obsolete
  natvis --version plumbing, name natvis and macro_builder in failure
  messages, drop the duplicate amalgamation job, install Valgrind only
  where needed, republish docs on version bumps, fix stale names
- Makefile: complete .PHONY and help, check-amalgamation always restores
  the checked-in files, natvis uses its own venv, macro_builder_check
  installs astyle, clean removes the fuzzer binaries
- remove tools/amalgamate/config_json_view.json (json_view.hpp is not on
  develop yet)

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

* Fix inaccuracies in the documentation

- version history: json_base_class_t (3.11.3), JSON_HAS_CPP_11 (3.10.0),
  JSON_HAS_RANGES exclusions, define-type macros, ABI tags
- releases: 3.12.0 raised the minimum CMake version
- from_*: the (ptr, len) overloads are deleted, not removed, in 4.0.0
- contains/count/find: document the deleted integral overloads
- add JSON_HAS_RANGE_VIEW_CONVERSION and list the JSON_HAS_* macros in
  the macro overview
- mention BON8 and error_handler where binary formats are listed
- broken links, outdated URLs, warning count, Hunter v0.26.12
- copy_markdown_source hook: expand snippets in the Markdown copies

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

* Pass /EHsc to the Windows C++20 module build

ci_module_cpp20 now forwards CMAKE_CXX_FLAGS to the module build. The
Windows workflow sets CMAKE_CXX_FLAGS, which replaces CMake's MSVC
defaults including /EHsc, so <chrono> failed with C4530 under /WX.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 08:21:51 +02:00
Niels Lohmann 2cca04ae6f Report one-character non-numeric array indices like longer ones (#5800)
* Report one-character non-numeric array indices like longer ones

A JSON pointer reference token that is not a number but has only one
character (e.g. "/a/x") was reported as out_of_range.404 ("unresolved
reference token"), because the "is not a number" check only ran for
tokens longer than one character; "/a/xy" got parse_error.109. Both now
throw parse_error.109. "-" and the empty token are still reported as
out_of_range.404. As a consequence, value(json_pointer, default) on an
array now throws for "/x" as it already did for "/xy".

Also document why ordered_map::erase's destroy/placement-new loop on
pair<const Key, T> is kept despite [basic.life]/8 before C++20.

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

* Document the one-character array index change

Add 3.13.0 version-history entries to at, operator[], value, patch,
patch_inplace, and unflatten, and describe in exceptions.md which array
indices throw parse_error.109 and which out_of_range.404.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 08:21:41 +02:00
Niels Lohmann 1649d9eda4 Use and extend the detail helpers to remove duplicated code (#5783)
* Use and extend the detail helpers to remove duplicated code

Library:
- binary_reader: format bytes with hex_byte() instead of snprintf
- add throw_type_must_be() for the 20 copies of type_error.302
- binary_reader: add last_byte_error()/unexpected_byte() for the 32
  "parse error at the last read byte" sites (replaces bon8_error)
- json_sax: add check_container_size() for out_of_range.408 and
  diagnostic_positions::set_container_start/_end()
- json_pointer: add throw_no_parent() (405) and throw_unresolved() (404)

Tests:
- unit-class_parser uses the shared utils::SaxCountdown
- move SaxEventLogger (and its ExitAfter* variants) from unit-class_parser
  and unit-deserialization into the new tests/src/test_sax.hpp

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

* Factor out more repeated error paths and test boilerplate

Library:
- add throw_cannot_use_with() for the 34 copies of type_error.304-312
  "cannot use X with Y"
- iter_impl: add throw_cannot_get_value() (invalid_iterator.214)
- parser: add syntax_error() for the 12 parse_error.101 sites
- ordered_map: share the four at() bodies via at_impl()
- json_sax_dom_callback_parser: add pop_container() for end_object()
  and end_array()
- binary_reader: build the two UBJSON/BJData length-type messages with
  concat() and last_byte_error()

Tests:
- move same_value(), the NDEBUG guard, and step 0 (parse without
  exceptions) of the seven fuzzer drivers into tests/src/fuzzer_common.hpp

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

* Name the exception ids and address review comments

Add detail::exception_id, a scoped enum with one named enumerator per
documented exception id, and use it for every id in the library. The
create() functions get an overload for it; the int overloads stay for
user code.

Following the review of #5783: add binary_reader::invalid_byte() and
length_type_error(), basic_json::throw_subscript_wrong_type(), move the
fuzzer includes and the using-declaration into fuzzer_common.hpp, and
rename test_sax.hpp to sax_event_loggers.hpp.

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

* Silence -Wweak-vtables for the SAX event loggers

The loggers moved from anonymous namespaces in the test files into
sax_event_loggers.hpp, so clang now warns that their vtables are
emitted in every translation unit.

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

* Silence MSVC 2015 C4100 in parser::syntax_error for static SAX::parse_error

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

* Inline invalid_byte() into its call sites

The wrapper only fixed the message string of unexpected_byte(), which is
the same kind of per-argument helper that was declined for
throw_type_must_be(). Call unexpected_byte("invalid byte", ...) directly.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 08:21:13 +02:00
Niels Lohmann f8b47ff6f6 Check the URL scheme before downloading in generate_docset.py (#5803)
Codacy flagged two Bandit findings in the docset generator added in
#5799: B310 (urlopen with an unchecked scheme) and B506 (yaml.load).
download() now rejects anything but http(s) URLs before opening them,
and the yaml.load call is marked, since its Loader derives from
yaml.SafeLoader. The SHA-1 used to name downloaded files is marked as
not used for security.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-10 16:21:53 +02:00
Niels Lohmann 07ec1a4e31 Generate the docset index and pages with Python (#5799)
Replace the hand-maintained docs/docset/docSet.sql and the bash/sed
Makefile recipe with docs/docset/generate_docset.py:

- The search index is generated from the mkdocs.yml nav and each page's
  H1 and declaration. Pages documenting several entities (e.g.
  JSON_HAS_CPP_11..26) get one entry per name; all non-API pages become
  guides. New API pages no longer need a manual entry.
- Page titles are set from the index names.
- The docset is self-contained: the mermaid loader no longer points to
  https://json.nlohmann.me/assets/..., the repository widget no longer
  queries api.github.com, remaining remote images are downloaded, and
  the build fails if any remote resource load remains.
- The CSS that hides the site navigation now uses Material's classes;
  the element selectors lost against them, so the header was shown.

check_structure.py's docset check is replaced by running the generator
in `make style_check`.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-10 12:10:11 +02:00
128 changed files with 7110 additions and 3417 deletions
+3
View File
@@ -0,0 +1,3 @@
# generated files are collapsed in diffs and excluded from language statistics
single_include/** linguist-generated
nlohmann_json.natvis linguist-generated
+1 -1
View File
@@ -1,5 +1,5 @@
# JSON for Modern C++ was originally written by Niels Lohmann. # JSON for Modern C++ was originally written by Niels Lohmann.
# Since 2013, over 250 contributors have helped to improve the library. # Since 2013, over 400 contributors have helped to improve the library.
# This CODEOWNERS file is only to make sure that @nlohmann is requested # This CODEOWNERS file is only to make sure that @nlohmann is requested
# for a code review in case of a pull request. # for a code review in case of a pull request.
+16 -14
View File
@@ -98,7 +98,7 @@ $ ctest --test-dir build -j 10
The test suite should report: The test suite should report:
``` ```
100% tests passed, 0 tests failed out of 98 100% tests passed, 0 tests failed out of <number of tests>
``` ```
#### Add tests #### Add tests
@@ -149,14 +149,17 @@ make build -C docs/mkdocs # strict build: fails on broken links, anchor
make check_mermaid -C docs/mkdocs # checks the Mermaid diagrams (requires Node.js) make check_mermaid -C docs/mkdocs # checks the Mermaid diagrams (requires Node.js)
``` ```
A new API page also needs an entry in [`docs/docset/docSet.sql`](https://github.com/nlohmann/json/blob/develop/docs/docset/docSet.sql), The search index of the docset is generated from [`mkdocs.yml`](https://github.com/nlohmann/json/blob/develop/docs/mkdocs/mkdocs.yml)
the search index of the docset; `make build` reports missing entries. and each page's title (H1) and declaration by
[`docs/docset/generate_docset.py`](https://github.com/nlohmann/json/blob/develop/docs/docset/generate_docset.py);
`make build` reports API pages that cannot be classified.
### Amalgamate the source code ### Amalgamate the source code
The single-header files The single-header files
[`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) and [`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp),
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp) [`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp), and
[`single_include/nlohmann/json_literals.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_literals.hpp)
are **generated** from the source files in the are **generated** from the source files in the
[`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). **Do not** edit the [`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). **Do not** edit the
files directly; instead, modify the include/nlohmann sources and regenerate the files by executing: files directly; instead, modify the include/nlohmann sources and regenerate the files by executing:
@@ -179,7 +182,7 @@ The amalgamation check in CI fails if any of these generated files is out of dat
## Recommended documentation ## Recommended documentation
- The library’s [README file](https://github.com/nlohmann/json/blob/master/README.md) is an excellent starting point to - The library’s [README file](https://github.com/nlohmann/json/blob/develop/README.md) is an excellent starting point to
understand its functionality. understand its functionality.
- The [documentation page](https://json.nlohmann.me) is the reference documentation of the library. - The [documentation page](https://json.nlohmann.me) is the reference documentation of the library.
- [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259) is the reference for the JavaScript Object Notation (JSON) - [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259) is the reference for the JavaScript Object Notation (JSON)
@@ -218,9 +221,9 @@ feature macros then allows users to test their code against the library in the n
### Break C++11 language conformance ### Break C++11 language conformance
This library is designed to work with C++11 and later. This means that any This library is designed to work with C++11 and later. This means that any
[supported C++11 compiler](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers) should compile [supported C++11 compiler](https://github.com/nlohmann/json/blob/develop/README.md#supported-compilers) should compile
the library without problems. Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual the library without problems. Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual
Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. C++ 2013 and earlier are known not to work due to missing or incomplete C++11 support.
Please do not add features that do not work with the mentioned supported compilers. Please guard features from C++14 and Please do not add features that do not work with the mentioned supported compilers. Please guard features from C++14 and
later against the respective [`JSON_HAS_CPP_14`](https://json.nlohmann.me/api/macros/json_has_cpp_11/) macros. later against the respective [`JSON_HAS_CPP_14`](https://json.nlohmann.me/api/macros/json_has_cpp_11/) macros.
@@ -236,12 +239,11 @@ extension.
The following areas really need contribution and are always welcomed: The following areas really need contribution and are always welcomed:
- Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the - Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the
bleeding-edge versions Clang. bleeding-edge versions of Clang.
- Improving the efficiency of the **JSON parser**. The current parser is implemented as a naive recursive descent parser - Improving the efficiency of the **JSON parser**. The current parser is a hand-written iterative parser that keeps an
with hand-coded string handling. More sophisticated approaches like LALR parsers would be really appreciated. That explicit stack instead of recursing, with hand-coded string handling. Speeding up the lexer or the value construction
said, parser generators like Bison or ANTLR do not play nice with single-header files -- I really would like to keep would be appreciated. Parser generators like Bison or ANTLR do not play nice with single-header files -- I really
the parser inside the `json.hpp` header, and I am not aware of approaches similar to [`re2c`](http://re2c.org) for would like to keep the parser inside the `json.hpp` header.
parsing.
- Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency - Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency
is not everything, speed and memory consumption are very important characteristics for C++ developers, so having is not everything, speed and memory consumption are very important characteristics for C++ developers, so having
proper comparisons would be interesting. proper comparisons would be interesting.
+2 -2
View File
@@ -52,7 +52,7 @@ body:
If possible, provide a small and self-contained example that triggers If possible, provide a small and self-contained example that triggers
the bug. Please understand that we cannot analyze and debug large code the bug. Please understand that we cannot analyze and debug large code
bases. Please do not paste screenshots here. bases. Please do not paste screenshots here.
render: Shell render: cpp
- type: textarea - type: textarea
id: output id: output
attributes: attributes:
@@ -79,7 +79,7 @@ body:
description: > description: >
Which version of the library did you use? If it is a released version, Which version of the library did you use? If it is a released version,
please enter the version number (e.g., 3.12.0). Otherwise, please enter please enter the version number (e.g., 3.12.0). Otherwise, please enter
the commit hash. If you got the library from another source as the the commit hash. If you got the library from another source than the
GitHub repository (e.g., via a package manager), please also state GitHub repository (e.g., via a package manager), please also state
this. this.
validations: validations:
+11
View File
@@ -21,6 +21,17 @@ labels:
files: files:
- "cmake/.*" - "cmake/.*"
# other build systems
- label: "CMake"
files:
- "(meson\\.build|meson_options\\.txt|BUILD\\.bazel|MODULE\\.bazel|Package\\.swift)"
- label: "documentation"
files:
- ".github/ISSUE_TEMPLATE/.*"
- ".github/config\\.yml"
- ".github/(CONTRIBUTING|SECURITY|CODE_OF_CONDUCT|PULL_REQUEST_TEMPLATE)\\.md"
- label: "CI" - label: "CI"
files: files:
- ".github/workflows/.*" - ".github/workflows/.*"
+7 -13
View File
@@ -83,8 +83,7 @@ jobs:
# byte for byte), so there is nothing to gain from checking out a # byte for byte), so there is nothing to gain from checking out a
# separate copy, and doing so would make this step fail on a PR # separate copy, and doing so would make this step fail on a PR
# that adds support for a new dispatch table until that PR itself # that adds support for a new dispatch table until that PR itself
# merges to develop, the same way generate_natvis.py's --version # merges to develop.
# requirement briefly did.
TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX) TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX)
c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder" c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder"
"$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp" "$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp"
@@ -117,7 +116,6 @@ jobs:
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s . python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s .
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s . python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s .
cp include/nlohmann/json_literals.hpp $INCLUDE_DIR/json_literals.hpp
# the header list of the Bazel "json" target must match the files in include/ # the header list of the Bazel "json" target must match the files in include/
cmake -P cmake/scripts/gen_bazel_build_file.cmake cmake -P cmake/scripts/gen_bazel_build_file.cmake
@@ -138,17 +136,13 @@ jobs:
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \ ${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
$(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort) $(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort)
# json_literals.hpp is copied verbatim from the (now formatted) include/
# directory, like `make amalgamate` does
cp include/nlohmann/json_literals.hpp $INCLUDE_DIR/json_literals.hpp
- name: Regenerate nlohmann_json.natvis - name: Regenerate nlohmann_json.natvis
run: | run: |
cd $MAIN_DIR cd $MAIN_DIR
# Pass --version explicitly so this step also works with the tool
# copy from develop before this repository's own generate_natvis.py
# learns to derive the version itself: the older script requires
# --version, and the newer one accepts it as an explicit override.
ABI_MACROS=include/nlohmann/detail/abi_macros.hpp
VERSION_MAJOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MAJOR' $ABI_MACROS | grep -o '[0-9]\+')
VERSION_MINOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MINOR' $ABI_MACROS | grep -o '[0-9]\+')
VERSION_PATCH=$(grep -m1 'define NLOHMANN_JSON_VERSION_PATCH' $ABI_MACROS | grep -o '[0-9]\+')
# The template is what this check verifies the committed natvis # The template is what this check verifies the committed natvis
# file against, so take it from this PR rather than from the # file against, so take it from this PR rather than from the
# develop checkout: generate_natvis.py loads the template from its # develop checkout: generate_natvis.py loads the template from its
@@ -156,7 +150,7 @@ jobs:
# template could never pass. Like the macro_builder step above, # template could never pass. Like the macro_builder step above,
# this only renders PR content in a job without credentials. # this only renders PR content in a job without credentials.
cp tools/generate_natvis/nlohmann_json.natvis.j2 $NATVIS_TOOL_DIR/ cp tools/generate_natvis/nlohmann_json.natvis.j2 $NATVIS_TOOL_DIR/
python3 $NATVIS_TOOL_DIR/generate_natvis.py --version "$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" $MAIN_DIR python3 $NATVIS_TOOL_DIR/generate_natvis.py $MAIN_DIR
- name: Build patch and check for differences - name: Build patch and check for differences
id: diff id: diff
@@ -165,7 +159,7 @@ jobs:
mkdir -p ${{ github.workspace }}/patch mkdir -p ${{ github.workspace }}/patch
git diff --patch --no-color > ${{ github.workspace }}/patch/amalgamation.patch git diff --patch --no-color > ${{ github.workspace }}/patch/amalgamation.patch
if [ -s ${{ github.workspace }}/patch/amalgamation.patch ]; then if [ -s ${{ github.workspace }}/patch/amalgamation.patch ]; then
echo "The source code has not been amalgamated/formatted correctly or BUILD.bazel is out of date. Diff:" echo "The source code has not been amalgamated/formatted correctly, or BUILD.bazel, nlohmann_json.natvis, or the macro_builder tables in macro_scope.hpp are out of date ('make amalgamate BUILD.bazel natvis' fixes all but the tables, see tools/macro_builder/README.md). Diff:"
cat ${{ github.workspace }}/patch/amalgamation.patch cat ${{ github.workspace }}/patch/amalgamation.patch
echo "has_diff=true" >> "$GITHUB_OUTPUT" echo "has_diff=true" >> "$GITHUB_OUTPUT"
else else
@@ -96,7 +96,7 @@ jobs:
issue_number: issue_number, issue_number: issue_number,
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: '## 🔴 Amalgamation check failed! 🔴\nThe source code has not been amalgamated and/or formatted correctly, or `BUILD.bazel` is out of date.' body: '## 🔴 Amalgamation check failed! 🔴\nThe source code has not been amalgamated and/or formatted correctly, or `BUILD.bazel`, `nlohmann_json.natvis`, or the macro_builder tables in `macro_scope.hpp` are out of date. `make amalgamate BUILD.bazel natvis` fixes all but the tables (see `tools/macro_builder/README.md`).'
+ (hasPatch ? '\n\n📎 A ready-to-apply patch is attached to the [failed workflow run](' + runUrl + ') as the `amalgamation-patch` artifact.' + (hasPatch ? '\n\n📎 A ready-to-apply patch is attached to the [failed workflow run](' + runUrl + ') as the `amalgamation-patch` artifact.'
+ ' Download it, then apply it locally from the repository root with:' + ' Download it, then apply it locally from the repository root with:'
+ '\n\n```shell\ngit apply amalgamation.patch\n```\n\n' + '\n\n```shell\ngit apply amalgamation.patch\n```\n\n'
@@ -15,6 +15,7 @@ on:
- .github/SECURITY.md - .github/SECURITY.md
- cmake/clang_flags.cmake - cmake/clang_flags.cmake
- cmake/gcc_flags.cmake - cmake/gcc_flags.cmake
- include/nlohmann/detail/abi_macros.hpp
- tests/fmt_formatter/project/main.cpp - tests/fmt_formatter/project/main.cpp
- tools/astyle/.astylerc - tools/astyle/.astylerc
workflow_dispatch: workflow_dispatch:
+3 -2
View File
@@ -109,7 +109,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
target: [ci_test_amalgamation, ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind] target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind]
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
@@ -117,6 +117,7 @@ jobs:
egress-policy: audit egress-policy: audit
- name: Install Valgrind - name: Install Valgrind
if: matrix.target == 'ci_test_valgrind'
run: sudo apt-get update ; sudo apt-get install -y valgrind run: sudo apt-get update ; sudo apt-get install -y valgrind
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: with:
@@ -279,7 +280,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Get latest CMake and ninja - name: Get latest CMake and ninja
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2 uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
- name: Set env FORCE_STDCPPFS_FLAG for clang 7 / 8 / 9 / 10 - name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV" run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }} if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
- name: Run CMake - name: Run CMake
+4 -2
View File
@@ -187,7 +187,7 @@ jobs:
- name: Test - name: Test
run: cd build ; ctest -j 10 -C Debug --output-on-failure run: cd build ; ctest -j 10 -C Debug --output-on-failure
clang-cl-12: clang-cl:
runs-on: windows-2022 runs-on: windows-2022
strategy: strategy:
matrix: matrix:
@@ -217,8 +217,10 @@ jobs:
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2 uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
with: with:
cmakeVersion: 4.3.4 cmakeVersion: 4.3.4
# CMAKE_CXX_FLAGS replaces CMake's MSVC defaults (which include /EHsc), and
# ci_module_cpp20 forwards it to the module build, so list /EHsc explicitly
- name: Run CMake (Debug) - name: Run CMake (Debug)
run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_CI=ON -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_CI=ON -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /EHsc /W4 /WX"
- name: Build - name: Build
run: cmake --build build --config Debug --target ci_module_cpp20 run: cmake --build build --config Debug --target ci_module_cpp20
+14 -1
View File
@@ -27,7 +27,6 @@
/docs/docset/JSON_for_Modern_C++.tgz /docs/docset/JSON_for_Modern_C++.tgz
/docs/docset/docSet.dsidx /docs/docset/docSet.dsidx
/docs/mkdocs/.cache/ /docs/mkdocs/.cache/
/docs/mkdocs/docs/__pycache__/
/docs/mkdocs/hooks/__pycache__/ /docs/mkdocs/hooks/__pycache__/
/docs/mkdocs/scripts/mermaid/node_modules/ /docs/mkdocs/scripts/mermaid/node_modules/
/docs/mkdocs/site/ /docs/mkdocs/site/
@@ -47,3 +46,17 @@ nlohmann_json.spdx
# Bazel-related # Bazel-related
MODULE.bazel.lock MODULE.bazel.lock
# backups left by `make check-amalgamation` or the ci_test_amalgamation target if they are interrupted
/BUILD.bazel~
/nlohmann_json.natvis~
/single_include/nlohmann/*.hpp~
/venv_astyle/
# release outputs of `make release`
/include.zip
/include.zip.asc
/json.tar.xz
/json.tar.xz.asc
/release_files/
/single_include/nlohmann/*.asc
+1
View File
@@ -81,6 +81,7 @@ cc_library(
hdrs = [ hdrs = [
"single_include/nlohmann/json.hpp", "single_include/nlohmann/json.hpp",
"single_include/nlohmann/json_fwd.hpp", "single_include/nlohmann/json_fwd.hpp",
"single_include/nlohmann/json_literals.hpp",
], ],
includes = ["single_include"], includes = ["single_include"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
+1 -1
View File
@@ -8,7 +8,7 @@ authors:
website: https://nlohmann.me website: https://nlohmann.me
title: "JSON for Modern C++" title: "JSON for Modern C++"
version: 3.12.0 version: 3.12.0
date-released: 2025-04-07 date-released: 2025-04-11
license: MIT license: MIT
repository-code: "https://github.com/nlohmann/json" repository-code: "https://github.com/nlohmann/json"
url: https://json.nlohmann.me url: https://json.nlohmann.me
+1 -1
View File
@@ -30,7 +30,7 @@ endif ()
option(NLOHMANN_JSON_BUILD_MODULES "Build C++ modules support" OFF) option(NLOHMANN_JSON_BUILD_MODULES "Build C++ modules support" OFF)
if(NLOHMANN_JSON_BUILD_MODULES) if(NLOHMANN_JSON_BUILD_MODULES)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) if(NOT CMAKE_VERSION VERSION_LESS 3.28)
message(STATUS "Building nlohmann.json C++ module") message(STATUS "Building nlohmann.json C++ module")
add_subdirectory(src/modules) add_subdirectory(src/modules)
else() else()
+7 -4
View File
@@ -14,7 +14,7 @@ This file describes the source for supporting files; that is, files that are not
The [GitHub Actions](https://docs.github.com/en/actions) workflows that build, test, and analyze the library. Each file in this folder defines one workflow: The [GitHub Actions](https://docs.github.com/en/actions) workflows that build, test, and analyze the library. Each file in this folder defines one workflow:
- `ubuntu.yml`, `macos.yml`, `windows.yml` — build and run the test suite on Linux, macOS, and Windows. - `ubuntu.yml`, `macos.yml`, `windows.yml` — build and run the test suite on Linux, macOS, and Windows.
- `check_amalgamation.yml` — verify that the single-header amalgamation in `single_include` is up to date on pull requests. - `check_amalgamation.yml` — verify on pull requests and pushes to `develop` that the generated files (the single-header amalgamation in `single_include`, `BUILD.bazel`, and `nlohmann_json.natvis`) are up to date.
- `comment_check_amalgamation.yml` — comment on a pull request when the amalgamation check failed. - `comment_check_amalgamation.yml` — comment on a pull request when the amalgamation check failed.
- `cifuzz.yml` — run short fuzzing sessions via [OSS-Fuzz CIFuzz](https://google.github.io/oss-fuzz/getting-started/continuous-integration/) on pull requests. - `cifuzz.yml` — run short fuzzing sessions via [OSS-Fuzz CIFuzz](https://google.github.io/oss-fuzz/getting-started/continuous-integration/) on pull requests.
- `codeql-analysis.yml` — run [CodeQL](https://codeql.github.com) code scanning. - `codeql-analysis.yml` — run [CodeQL](https://codeql.github.com) code scanning.
@@ -24,7 +24,9 @@ The [GitHub Actions](https://docs.github.com/en/actions) workflows that build, t
- `dependency-review.yml` — scan dependency changes in pull requests for known vulnerabilities. - `dependency-review.yml` — scan dependency changes in pull requests for known vulnerabilities.
- `labeler.yml` — the "Pull Request Labeler" workflow (see `.github/labeler.yml`). - `labeler.yml` — the "Pull Request Labeler" workflow (see `.github/labeler.yml`).
- `stale.yml` — comment on and close stale issues and pull requests. - `stale.yml` — comment on and close stale issues and pull requests.
- `publish_documentation.yml` — build and publish the documentation on every merge to the `develop` branch. - `publish_documentation.yml` — build and publish the documentation on pushes to the `develop` branch that touch the documentation sources (or the files it embeds).
- `check_docs_links.yml` — check the links of the documentation weekly, as external links can break without any change in this repository.
- `cancel_closed_pr_runs.yml` — cancel unfinished workflow runs of a pull request once it is closed or merged.
Further documentation: Further documentation:
@@ -266,8 +268,9 @@ meson setup builddir
ninja -C builddir ninja -C builddir
``` ```
`meson_options.txt` defines the options, which mirror the CMake options that change the library's target (for example, `meson_options.txt` defines the options, which mirror the names of the CMake options that change the library's target (for example,
`-DDiagnostics=true`). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options` `-DDiagnostics=true`); their defaults may differ (for example, `MultipleHeaders` defaults to `false` in Meson, while
`JSON_MultipleHeaders` is `ON` in CMake). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options`
([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files ([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files
stay in sync with the CMake options. stay in sync with the CMake options.
+1
View File
@@ -1,5 +1,6 @@
module( module(
name = "nlohmann_json", name = "nlohmann_json",
version = "3.12.0",
compatibility_level = 1, compatibility_level = 1,
) )
+37 -26
View File
@@ -1,4 +1,4 @@
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check check_build_options .PHONY: all amalgamate check-amalgamation check_build_options pretty install_astyle clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check run_benchmarks pvs_studio serve_header reuse spdx
########################################################################## ##########################################################################
# configuration # configuration
@@ -41,12 +41,18 @@ all:
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources" @echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
@echo "ChangeLog.md - generate ChangeLog file" @echo "ChangeLog.md - generate ChangeLog file"
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date" @echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
@echo "check_build_options - check that the Meson build and the pkg-config files offer the options of the CMake target"
@echo "clean - remove built files" @echo "clean - remove built files"
@echo "fuzzing - see tests/fuzzing.md for how to build and run the fuzzers" @echo "(fuzzers) - see tests/fuzzing.md, or run 'make -C tests fuzzers' to build the AFL fuzzers"
@echo "macro_builder_check - check that macro_scope.hpp matches tools/macro_builder's output" @echo "macro_builder_check - check that macro_scope.hpp matches tools/macro_builder's output"
@echo "natvis - regenerate nlohmann_json.natvis from the current ABI tags and version" @echo "natvis - regenerate nlohmann_json.natvis from the current ABI tags and version"
@echo "pretty - beautify code with Artistic Style" @echo "pretty - beautify code with Artistic Style"
@echo "pvs_studio - run the PVS-Studio static analyzer and open the report"
@echo "release - create the release files (include.zip, json.tar.xz, single headers, signatures, hashes)"
@echo "reuse - annotate the source files with REUSE license headers and lint them"
@echo "run_benchmarks - build and run benchmarks" @echo "run_benchmarks - build and run benchmarks"
@echo "serve_header - serve the single header, re-amalgamated on demand (see tools/serve_header)"
@echo "spdx - generate the SPDX software bill of materials nlohmann_json.spdx"
@echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp" @echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp"
@echo "update_hedley_undef - rebuild hedley_undef.hpp from the JSON_HEDLEY_* #define names in hedley.hpp" @echo "update_hedley_undef - rebuild hedley_undef.hpp from the JSON_HEDLEY_* #define names in hedley.hpp"
@@ -108,13 +114,18 @@ $(AMALGAMATED_FWD_FILE): $(SRCS)
$(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp
cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE) cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE)
NATVIS_VENV=tools/generate_natvis/venv
# regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp # regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp
# (the dependencies are installed into a virtual environment first)
natvis: natvis:
python3 tools/generate_natvis/generate_natvis.py . @test -d $(NATVIS_VENV) || python3 -mvenv $(NATVIS_VENV)
@$(NATVIS_VENV)/bin/python3 -c 'import jinja2' 2>/dev/null || $(NATVIS_VENV)/bin/pip3 install --quiet -r tools/generate_natvis/requirements.txt
$(NATVIS_VENV)/bin/python3 tools/generate_natvis/generate_natvis.py .
# regenerate the two tools/macro_builder blocks of $(MACRO_SCOPE_HPP) (see its README.md) and diff against the # regenerate the two tools/macro_builder blocks of $(MACRO_SCOPE_HPP) (see its README.md) and diff against the
# checked-in header; phony, because it never writes $(MACRO_SCOPE_HPP) itself # checked-in header; phony, because it never writes $(MACRO_SCOPE_HPP) itself
macro_builder_check: macro_builder_check: install_astyle
@set -e; \ @set -e; \
TMPDIR=$$(mktemp -d ./macro_builder_check.XXXXXX); \ TMPDIR=$$(mktemp -d ./macro_builder_check.XXXXXX); \
trap 'rm -rf "$$TMPDIR"' EXIT; \ trap 'rm -rf "$$TMPDIR"' EXIT; \
@@ -131,27 +142,27 @@ macro_builder_check:
check_build_options: check_build_options:
python3 tools/check_build_options/check_build_options.py . python3 tools/check_build_options/check_build_options.py .
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources # check if the single headers have been amalgamated from the nlohmann sources and if BUILD.bazel and
# nlohmann_json.natvis are up to date; all checks are run, and the checked-in files are always restored
check-amalgamation: check-amalgamation:
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ @status=0; \
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ banner() { printf '===================================================================\n %s\n===================================================================\n' "$$1"; }; \
@mv $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~ for FILE in $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel nlohmann_json.natvis; do \
@$(MAKE) amalgamate mv "$$FILE" "$$FILE~" || exit 1; \
@diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false) done; \
@diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false) $(MAKE) amalgamate || status=1; \
@diff $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE) ; false) $(MAKE) BUILD.bazel || status=1; \
@mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) $(MAKE) natvis || status=1; \
@mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) for FILE in $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE); do \
@mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE) diff "$$FILE" "$$FILE~" || { banner "$$FILE is out of date! Amalgamation required! Please read the contribution guidelines in file .github/CONTRIBUTING.md."; status=1; }; \
@mv BUILD.bazel BUILD.bazel~ done; \
@$(MAKE) BUILD.bazel diff BUILD.bazel BUILD.bazel~ || { banner "BUILD.bazel is out of date! Please run 'make BUILD.bazel'."; status=1; }; \
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false) diff nlohmann_json.natvis nlohmann_json.natvis~ || { banner "nlohmann_json.natvis is out of date! Please run 'make natvis'."; status=1; }; \
@mv BUILD.bazel~ BUILD.bazel for FILE in $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel nlohmann_json.natvis; do \
@mv nlohmann_json.natvis nlohmann_json.natvis~ mv "$$FILE~" "$$FILE"; \
@$(MAKE) natvis done; \
@diff nlohmann_json.natvis nlohmann_json.natvis~ || (echo "===================================================================\n nlohmann_json.natvis is out of date! Please run 'make natvis'.\n===================================================================" ; mv nlohmann_json.natvis~ nlohmann_json.natvis ; false) $(MAKE) macro_builder_check || status=1; \
@mv nlohmann_json.natvis~ nlohmann_json.natvis exit $$status
@$(MAKE) macro_builder_check
# generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild # generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild
BUILD.bazel: BUILD.bazel:
@@ -212,8 +223,8 @@ release: include.zip json.tar.xz
# clean up # clean up
clean: clean:
rm -fr fuzz fuzz-testing *.dSYM tests/*.dSYM rm -fr *.dSYM tests/*.dSYM tests/parse_*_fuzzer
rm -fr cmake-build-benchmarks fuzz-testing cmake-build-pvs-studio release_files rm -fr cmake-build-benchmarks cmake-build-pvs-studio release_files
$(MAKE) clean -Cdocs $(MAKE) clean -Cdocs
+425 -447
View File
@@ -48,6 +48,7 @@
- [Pkg-config](#pkg-config) - [Pkg-config](#pkg-config)
- [License](#license) - [License](#license)
- [Contact](#contact) - [Contact](#contact)
- [Security](#security)
- [Thanks](#thanks) - [Thanks](#thanks)
- [Used third-party tools](#used-third-party-tools) - [Used third-party tools](#used-third-party-tools)
- [Notes](#notes) - [Notes](#notes)
@@ -112,9 +113,9 @@ There is also a [**docset**](https://github.com/Kapeli/Dash-User-Contributions/t
## Quick reference ## Quick reference
- **Constructors** [basic_json](https://json.nlohmann.me/api/basic_json/basic_json), [array](https://json.nlohmann.me/api/basic_json/array), [binary](https://json.nlohmann.me/api/basic_json/binary), [object](https://json.nlohmann.me/api/basic_json/object) - **Constructors**: [basic_json](https://json.nlohmann.me/api/basic_json/basic_json), [array](https://json.nlohmann.me/api/basic_json/array), [binary](https://json.nlohmann.me/api/basic_json/binary), [object](https://json.nlohmann.me/api/basic_json/object)
- **Object inspection**: [type](https://json.nlohmann.me/api/basic_json/type), [operator value_t](https://json.nlohmann.me/api/basic_json/operator_value_t), [type_name](https://json.nlohmann.me/api/basic_json/type_name), [is_primitive](https://json.nlohmann.me/api/basic_json/is_primitive), [is_structured](https://json.nlohmann.me/api/basic_json/is_structured), [is_null](https://json.nlohmann.me/api/basic_json/is_null), [is_boolean](https://json.nlohmann.me/api/basic_json/is_boolean), [is_number](https://json.nlohmann.me/api/basic_json/is_number), [is_number_integer](https://json.nlohmann.me/api/basic_json/is_number_integer), [is_number_unsigned](https://json.nlohmann.me/api/basic_json/is_number_unsigned), [is_number_float](https://json.nlohmann.me/api/basic_json/is_number_float), [is_object](https://json.nlohmann.me/api/basic_json/is_object), [is_array](https://json.nlohmann.me/api/basic_json/is_array), [is_string](https://json.nlohmann.me/api/basic_json/is_string), [is_binary](https://json.nlohmann.me/api/basic_json/is_binary), [is_discarded](https://json.nlohmann.me/api/basic_json/is_discarded) - **Object inspection**: [type](https://json.nlohmann.me/api/basic_json/type), [operator value_t](https://json.nlohmann.me/api/basic_json/operator_value_t), [type_name](https://json.nlohmann.me/api/basic_json/type_name), [is_primitive](https://json.nlohmann.me/api/basic_json/is_primitive), [is_structured](https://json.nlohmann.me/api/basic_json/is_structured), [is_null](https://json.nlohmann.me/api/basic_json/is_null), [is_boolean](https://json.nlohmann.me/api/basic_json/is_boolean), [is_number](https://json.nlohmann.me/api/basic_json/is_number), [is_number_integer](https://json.nlohmann.me/api/basic_json/is_number_integer), [is_number_unsigned](https://json.nlohmann.me/api/basic_json/is_number_unsigned), [is_number_float](https://json.nlohmann.me/api/basic_json/is_number_float), [is_object](https://json.nlohmann.me/api/basic_json/is_object), [is_array](https://json.nlohmann.me/api/basic_json/is_array), [is_string](https://json.nlohmann.me/api/basic_json/is_string), [is_binary](https://json.nlohmann.me/api/basic_json/is_binary), [is_discarded](https://json.nlohmann.me/api/basic_json/is_discarded)
- **Value access**; [get](https://json.nlohmann.me/api/basic_json/get), [get_to](https://json.nlohmann.me/api/basic_json/get_to), [get_ptr](https://json.nlohmann.me/api/basic_json/get_ptr), [get_ref](https://json.nlohmann.me/api/basic_json/get_ref), [operator ValueType](https://json.nlohmann.me/api/basic_json/operator_ValueType), [get_binary](https://json.nlohmann.me/api/basic_json/get_binary) - **Value access**: [get](https://json.nlohmann.me/api/basic_json/get), [get_to](https://json.nlohmann.me/api/basic_json/get_to), [get_ptr](https://json.nlohmann.me/api/basic_json/get_ptr), [get_ref](https://json.nlohmann.me/api/basic_json/get_ref), [operator ValueType](https://json.nlohmann.me/api/basic_json/operator_ValueType), [get_binary](https://json.nlohmann.me/api/basic_json/get_binary)
- **Element access**: [at](https://json.nlohmann.me/api/basic_json/at), [operator[]](https://json.nlohmann.me/api/basic_json/operator[]), [value](https://json.nlohmann.me/api/basic_json/value), [front](https://json.nlohmann.me/api/basic_json/front), [back](https://json.nlohmann.me/api/basic_json/back) - **Element access**: [at](https://json.nlohmann.me/api/basic_json/at), [operator[]](https://json.nlohmann.me/api/basic_json/operator[]), [value](https://json.nlohmann.me/api/basic_json/value), [front](https://json.nlohmann.me/api/basic_json/front), [back](https://json.nlohmann.me/api/basic_json/back)
- **Lookup**: [find](https://json.nlohmann.me/api/basic_json/find), [count](https://json.nlohmann.me/api/basic_json/count), [contains](https://json.nlohmann.me/api/basic_json/contains) - **Lookup**: [find](https://json.nlohmann.me/api/basic_json/find), [count](https://json.nlohmann.me/api/basic_json/count), [contains](https://json.nlohmann.me/api/basic_json/contains)
- **Iterators**: [begin](https://json.nlohmann.me/api/basic_json/begin), [cbegin](https://json.nlohmann.me/api/basic_json/cbegin), [end](https://json.nlohmann.me/api/basic_json/end), [cend](https://json.nlohmann.me/api/basic_json/cend), [rbegin](https://json.nlohmann.me/api/basic_json/rbegin), [rend](https://json.nlohmann.me/api/basic_json/rend), [crbegin](https://json.nlohmann.me/api/basic_json/crbegin), [crend](https://json.nlohmann.me/api/basic_json/crend), [items](https://json.nlohmann.me/api/basic_json/items) - **Iterators**: [begin](https://json.nlohmann.me/api/basic_json/begin), [cbegin](https://json.nlohmann.me/api/basic_json/cbegin), [end](https://json.nlohmann.me/api/basic_json/end), [cend](https://json.nlohmann.me/api/basic_json/cend), [rbegin](https://json.nlohmann.me/api/basic_json/rbegin), [rend](https://json.nlohmann.me/api/basic_json/rend), [crbegin](https://json.nlohmann.me/api/basic_json/crbegin), [crend](https://json.nlohmann.me/api/basic_json/crend), [items](https://json.nlohmann.me/api/basic_json/items)
@@ -1184,7 +1185,7 @@ binary[1]; // 0xFE
binary.set_subtype(0x10); binary.set_subtype(0x10);
// serialize to MessagePack // serialize to MessagePack
auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE auto msgpack = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE
``` ```
## Customers ## Customers
@@ -1220,17 +1221,7 @@ I would be happy to learn about other compilers/versions.
Please note: Please note:
- GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler. - GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler.
- Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default. - For GCC running on MinGW or Android, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`) may occur. Note this is not an issue with the code, but rather with the compiler or C++ standard library itself: use a recent toolchain (for Android, a current [NDK](https://developer.android.com/ndk) with the LLVM C++ library `APP_STL := c++_shared`, and for MinGW, a distribution with a complete C++11 standard library). See [this discussion](https://github.com/nlohmann/json/issues/136) for MinGW and [this discussion](https://github.com/nlohmann/json/issues/219) for the old Android NDK using `APP_STL := gnustl_static`.
```makefile
APP_STL := c++_shared
NDK_TOOLCHAIN_VERSION := clang3.6
APP_CPPFLAGS += -frtti -fexceptions
```
The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10.
- For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`) may occur. Note this is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](https://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219).
- Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case. - Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case.
@@ -1396,7 +1387,7 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright &copy; 2009 [Florian Loitsch](https://florian.loitsch.com/) - The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright &copy; 2009 [Florian Loitsch](https://florian.loitsch.com/)
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). - The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0). - The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
- The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright &copy; 2021 The fast_float authors - The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright &copy; 2021 The fast_float authors
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software"> <img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
@@ -1437,436 +1428,416 @@ I deeply appreciate the help of the following people.
12. [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual Studio. 12. [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual Studio.
13. [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types. 13. [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types.
14. [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling. 14. [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling.
15. [dariomt](https://github.com/dariomt) fixed some typos in the examples. 15. [dariomt](https://github.com/dariomt) fixed some typos in the examples and fixed a subtlety in MSVC type support and implemented the `get_ref()` function to get a reference to stored values.
16. [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation. 16. [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation.
17. [Colin Hirsch](https://github.com/ColinH) took care of a small namespace issue. 17. [Colin Hirsch](https://github.com/ColinH) took care of a small namespace issue.
18. [Huu Nguyen](https://github.com/whoshuu) corrected a variable name in the documentation. 18. [Huu Nguyen](https://github.com/whoshuu) corrected a variable name in the documentation.
19. [Silverweed](https://github.com/silverweed) overloaded `parse()` to accept an rvalue reference. 19. [Silverweed](https://github.com/silverweed) overloaded `parse()` to accept an rvalue reference.
20. [dariomt](https://github.com/dariomt) fixed a subtlety in MSVC type support and implemented the `get_ref()` function to get a reference to stored values. 20. [ZahlGraf](https://github.com/ZahlGraf) added a workaround that allows compilation using Android NDK.
21. [ZahlGraf](https://github.com/ZahlGraf) added a workaround that allows compilation using Android NDK. 21. [whackashoe](https://github.com/whackashoe) replaced a function that was marked as unsafe by Visual Studio.
22. [whackashoe](https://github.com/whackashoe) replaced a function that was marked as unsafe by Visual Studio. 22. [406345](https://github.com/406345) fixed two small warnings.
23. [406345](https://github.com/406345) fixed two small warnings. 23. [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function.
24. [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function. 24. [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines.
25. [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines. 25. [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers.
26. [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers. 26. [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file.
27. [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file. 27. [msm-](https://github.com/msm-) added support for American Fuzzy Lop.
28. [msm-](https://github.com/msm-) added support for American Fuzzy Lop. 28. [Annihil](https://github.com/Annihil) fixed an example in the README file.
29. [Annihil](https://github.com/Annihil) fixed an example in the README file. 29. [Themercee](https://github.com/Themercee) noted a wrong URL in the README file.
30. [Themercee](https://github.com/Themercee) noted a wrong URL in the README file. 30. [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`.
31. [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`. 31. [abc100m](https://github.com/abc100m) analyzed the issues with GCC 4.8 and proposed a [partial solution](https://github.com/nlohmann/json/pull/212).
32. [abc100m](https://github.com/abc100m) analyzed the issues with GCC 4.8 and proposed a [partial solution](https://github.com/nlohmann/json/pull/212). 32. [zewt](https://github.com/zewt) added useful notes to the README file about Android.
33. [zewt](https://github.com/zewt) added useful notes to the README file about Android. 33. [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake.
34. [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake. 34. [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files.
35. [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files. 35. [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal).
36. [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal). 36. [Mário Feroldi](https://github.com/thelostt) fixed a small typo.
37. [Mário Feroldi](https://github.com/thelostt) fixed a small typo. 37. [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
38. [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release. 38. [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
39. [Damien](https://github.com/dtoma) fixed one of the last conversion warnings. 39. [Thomas Braun](https://github.com/t-b) fixed a warning in a test case and adjusted MSVC calls in the CI.
40. [Thomas Braun](https://github.com/t-b) fixed a warning in a test case and adjusted MSVC calls in the CI. 40. [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types and split the single header file into smaller chunks.
41. [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types and split the single header file into smaller chunks. 41. [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
42. [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation. 42. [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
43. [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`. 43. [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
44. [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion. 44. [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix.
45. [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix. 45. [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
46. [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file. 46. [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function.
47. [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function. 47. [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](https://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.
48. [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](https://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing. 48. [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan.
49. [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan. 49. [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning and supported the implementation of user-defined types.
50. [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. 50. [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
51. [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. 51. [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one.
52. [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. 52. [Daniel599](https://github.com/Daniel599) helped to get Travis to execute the tests with Clang's sanitizers and allowed to use more algorithms with the `items()` function.
53. [Daniel599](https://github.com/Daniel599) helped to get Travis to execute the tests with Clang's sanitizers. 53. [Jonathan Lee](https://github.com/vjon) fixed an example in the README file.
54. [Jonathan Lee](https://github.com/vjon) fixed an example in the README file. 54. [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types.
55. [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types. 55. [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio.
56. [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio. 56. [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
57. [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types. 57. [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite.
58. [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example. 58. [ukhegg](https://github.com/ukhegg) proposed an improvement for the examples section.
59. [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite. 59. [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README.
60. [ukhegg](https://github.com/ukhegg) proposed an improvement for the examples section. 60. [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s.
61. [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README. 61. [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation.
62. [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s. 62. [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings.
63. [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation. 63. [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible.
64. [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings. 64. [ftillier](https://github.com/ftillier) fixed a compiler warning.
65. [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible. 65. [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped.
66. [ftillier](https://github.com/ftillier) fixed a compiler warning. 66. [Fytch](https://github.com/Fytch) found a bug in the documentation.
67. [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped. 67. [Jay Sistar](https://github.com/Type1J) implemented a Meson build description.
68. [Fytch](https://github.com/Fytch) found a bug in the documentation. 68. [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation.
69. [Jay Sistar](https://github.com/Type1J) implemented a Meson build description. 69. [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager.
70. [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation. 70. [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`.
71. [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager. 71. [Mike Tzou](https://github.com/Chocobo1) fixed some typos.
72. [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`. 72. [amrcode](https://github.com/amrcode) noted misleading documentation about comparison of floats.
73. [Mike Tzou](https://github.com/Chocobo1) fixed some typos. 73. [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `<iostream>` with `<iosfwd>`.
74. [amrcode](https://github.com/amrcode) noted misleading documentation about comparison of floats. 74. [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library.
75. [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `<iostream>` with `<iosfwd>`. 75. [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists.
76. [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library. 76. [Greg Hurrell](https://github.com/wincent) fixed a typo.
77. [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists. 77. [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo.
78. [Greg Hurrell](https://github.com/wincent) fixed a typo. 78. [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler.
79. [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo. 79. [Markus Werle](https://github.com/daixtrose) fixed a typo.
80. [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler. 80. [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check.
81. [Markus Werle](https://github.com/daixtrose) fixed a typo. 81. [Alex](https://github.com/leha-bot) noted an error in a code sample.
82. [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check. 82. [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped to fix them.
83. [Alex](https://github.com/leha-bot) noted an error in a code sample. 83. [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams.
84. [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped to fix them. 84. [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error, fixed the compilation with MSVC 2015 in debug mode and fixed a warning.
85. [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams. 85. [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings and avoided unnecessary string copies in `find()` and `count()`.
86. [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error. 86. [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file.
87. [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings. 87. [pvleuven](https://github.com/pvleuven) helped to fix a warning in ICC.
88. [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file. 88. [Pavel](https://github.com/crea7or) helped to fix some warnings in MSVC.
89. [pvleuven](https://github.com/pvleuven) helped to fix a warning in ICC. 89. [Mitja](https://github.com/Itja) fixed some typos.
90. [Pavel](https://github.com/crea7or) helped to fix some warnings in MSVC. 90. [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links.
91. [Jamie Seward](https://github.com/jseward) avoided unnecessary string copies in `find()` and `count()`. 91. [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view and removed the dependency from `std::stringstream`.
92. [Mitja](https://github.com/Itja) fixed some typos. 92. [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings.
93. [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links. 93. [Eren Okka](https://github.com/erengy) fixed some MSVC warnings.
94. [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view. 94. [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed.
95. [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings. 95. [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README.
96. [Eren Okka](https://github.com/erengy) fixed some MSVC warnings. 96. [zerodefect](https://github.com/zerodefect) fixed a compiler warning.
97. [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed. 97. [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior.
98. [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README. 98. [mark-99](https://github.com/mark-99) helped fix an ICC error.
99. [zerodefect](https://github.com/zerodefect) fixed a compiler warning. 99. [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file.
100. [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior. 100. [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings.
101. [mark-99](https://github.com/mark-99) helped fix an ICC error. 101. [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager.
102. [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file. 102. [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise.
103. [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings. 103. [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback.
104. [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager. 104. [agrianius](https://github.com/agrianius) allowed dumping JSON to an alternative string type and added code to use alternative string implementations.
105. [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise. 105. [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake.
106. [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback. 106. [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io).
107. [agrianius](https://github.com/agrianius) allowed dumping JSON to an alternative string type. 107. [Carlos O'Ryan](https://github.com/coryan) fixed a typo.
108. [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake. 108. [James Upjohn](https://github.com/jupjohn) fixed a version number in the compilers section.
109. [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io). 109. [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines and provided documentation for the CMake integration.
110. [Carlos O'Ryan](https://github.com/coryan) fixed a typo. 110. [Jan Schöppach](https://github.com/dns13) fixed a typo.
111. [James Upjohn](https://github.com/jupjohn) fixed a version number in the compilers section. 111. [martin-mfg](https://github.com/martin-mfg) fixed a typo.
112. [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines and provided documentation for the CMake integration. 112. [Julius Rakow](https://github.com/juliusrakow) fixed the Meson include directory and fixed the links to [cppreference.com](https://cppreference.com).
113. [Jan Schöppach](https://github.com/dns13) fixed a typo. 113. [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases.
114. [martin-mfg](https://github.com/martin-mfg) fixed a typo. 114. [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library and fixed an issue with a double macro definition.
115. [Matthias Möller](https://github.com/TinyTinni) removed the dependency from `std::stringstream`. 115. [thyu](https://github.com/thyu) fixed a compiler warning.
116. [agrianius](https://github.com/agrianius) added code to use alternative string implementations. 116. [David Guthrie](https://github.com/LEgregius) fixed a subtle compilation error with Clang 3.4.2.
117. [Daniel599](https://github.com/Daniel599) allowed to use more algorithms with the `items()` function. 117. [Dennis Fischer](https://github.com/dennisfischer) allowed to call `find_package` without installing the library.
118. [Julius Rakow](https://github.com/juliusrakow) fixed the Meson include directory and fixed the links to [cppreference.com](https://cppreference.com). 118. [Ben Berman](https://github.com/rivertam) made some error messages more understandable.
119. [Sonu Lohani](https://github.com/sonulohani) fixed the compilation with MSVC 2015 in debug mode. 119. [zakalibit](https://github.com/zakalibit) fixed a compilation problem with the Intel C++ compiler.
120. [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases. 120. [mandreyel](https://github.com/mandreyel) fixed a compilation problem.
121. [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library. 121. [Kostiantyn Ponomarenko](https://github.com/koponomarenko) added version and license information to the Meson build file.
122. [thyu](https://github.com/thyu) fixed a compiler warning. 122. [Henry Schreiner](https://github.com/henryiii) added support for GCC 4.8.
123. [David Guthrie](https://github.com/LEgregius) fixed a subtle compilation error with Clang 3.4.2. 123. [knilch](https://github.com/knilch0r) made sure the test suite does not stall when run in the wrong directory.
124. [Dennis Fischer](https://github.com/dennisfischer) allowed to call `find_package` without installing the library. 124. [Antonio Borondo](https://github.com/antonioborondo) fixed an MSVC 2017 warning.
125. [Hyeon Kim](https://github.com/simnalamburt) fixed an issue with a double macro definition. 125. [Dan Gendreau](https://github.com/dgendreau) implemented the `NLOHMANN_JSON_SERIALIZE_ENUM` macro to quickly define an enum/JSON mapping.
126. [Ben Berman](https://github.com/rivertam) made some error messages more understandable. 126. [efp](https://github.com/efp) added line and column information to parse errors.
127. [zakalibit](https://github.com/zakalibit) fixed a compilation problem with the Intel C++ compiler. 127. [julian-becker](https://github.com/julian-becker) added BSON support.
128. [mandreyel](https://github.com/mandreyel) fixed a compilation problem. 128. [Pratik Chowdhury](https://github.com/pratikpc) added support for structured bindings.
129. [Kostiantyn Ponomarenko](https://github.com/koponomarenko) added version and license information to the Meson build file. 129. [David Avedissian](https://github.com/davedissian) added support for Clang 5.0.1 (PS4 version).
130. [Henry Schreiner](https://github.com/henryiii) added support for GCC 4.8. 130. [Jonathan Dumaresq](https://github.com/dumarjo) implemented an input adapter to read from `FILE*`.
131. [knilch](https://github.com/knilch0r) made sure the test suite does not stall when run in the wrong directory. 131. [kjpus](https://github.com/kjpus) fixed a link in the documentation.
132. [Antonio Borondo](https://github.com/antonioborondo) fixed an MSVC 2017 warning. 132. [Manvendra Singh](https://github.com/manu-chroma) fixed a typo in the documentation.
133. [Dan Gendreau](https://github.com/dgendreau) implemented the `NLOHMANN_JSON_SERIALIZE_ENUM` macro to quickly define an enum/JSON mapping. 133. [ziggurat29](https://github.com/ziggurat29) fixed an MSVC warning.
134. [efp](https://github.com/efp) added line and column information to parse errors. 134. [Sylvain Corlay](https://github.com/SylvainCorlay) added code to avoid an issue with MSVC.
135. [julian-becker](https://github.com/julian-becker) added BSON support. 135. [mefyl](https://github.com/mefyl) fixed a bug when JSON was parsed from an input stream.
136. [Pratik Chowdhury](https://github.com/pratikpc) added support for structured bindings. 136. [Millian Poquet](https://github.com/mpoquet) allowed to install the library via Meson.
137. [David Avedissian](https://github.com/davedissian) added support for Clang 5.0.1 (PS4 version). 137. [Michael Behrns-Miller](https://github.com/moodboom) found an issue with a missing namespace.
138. [Jonathan Dumaresq](https://github.com/dumarjo) implemented an input adapter to read from `FILE*`. 138. [Nasztanovics Ferenc](https://github.com/naszta) fixed a compilation issue with libc 2.12.
139. [kjpus](https://github.com/kjpus) fixed a link in the documentation. 139. [Andreas Schwab](https://github.com/andreas-schwab) fixed the endian conversion.
140. [Manvendra Singh](https://github.com/manu-chroma) fixed a typo in the documentation. 140. [Mark-Dunning](https://github.com/Mark-Dunning) fixed a warning in MSVC.
141. [ziggurat29](https://github.com/ziggurat29) fixed an MSVC warning. 141. [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) added `operator/` for JSON Pointers and fixed a compilation issue with MSVC.
142. [Sylvain Corlay](https://github.com/SylvainCorlay) added code to avoid an issue with MSVC. 142. [John-Mark](https://github.com/johnmarkwayve) noted a missing header.
143. [mefyl](https://github.com/mefyl) fixed a bug when JSON was parsed from an input stream. 143. [Vitaly Zaitsev](https://github.com/xvitaly) fixed compilation with GCC 9.0.
144. [Millian Poquet](https://github.com/mpoquet) allowed to install the library via Meson. 144. [Laurent Stacul](https://github.com/stac47) fixed compilation with GCC 9.0.
145. [Michael Behrns-Miller](https://github.com/moodboom) found an issue with a missing namespace. 145. [Ivor Wanders](https://github.com/iwanders) helped to reduce the CMake requirement to version 3.1 and fixed the examples to catch exception by `const&`.
146. [Nasztanovics Ferenc](https://github.com/naszta) fixed a compilation issue with libc 2.12. 146. [njlr](https://github.com/njlr) updated the Buckaroo instructions.
147. [Andreas Schwab](https://github.com/andreas-schwab) fixed the endian conversion. 147. [Lion](https://github.com/lieff) fixed a compilation issue with GCC 7 on CentOS.
148. [Mark-Dunning](https://github.com/Mark-Dunning) fixed a warning in MSVC. 148. [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function.
149. [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) added `operator/` for JSON Pointers. 149. [past-due](https://github.com/past-due) suppressed an unfixable warning.
150. [John-Mark](https://github.com/johnmarkwayve) noted a missing header. 150. [Elvis Oric](https://github.com/elvisoric) improved Meson support.
151. [Vitaly Zaitsev](https://github.com/xvitaly) fixed compilation with GCC 9.0. 151. [Matěj Plch](https://github.com/Afforix) fixed an example in the README.
152. [Laurent Stacul](https://github.com/stac47) fixed compilation with GCC 9.0. 152. [Mark Beckwith](https://github.com/wythe) fixed two typos.
153. [Ivor Wanders](https://github.com/iwanders) helped to reduce the CMake requirement to version 3.1. 153. [scinart](https://github.com/scinart) fixed a bug in the serializer.
154. [njlr](https://github.com/njlr) updated the Buckaroo instructions. 154. [Patrick Boettcher](https://github.com/pboettch) implemented `push_back()` and `pop_back()` for JSON Pointers.
155. [Lion](https://github.com/lieff) fixed a compilation issue with GCC 7 on CentOS. 155. [Bruno Oliveira](https://github.com/nicoddemus) added support for Conda.
156. [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function. 156. [Michele Caini](https://github.com/skypjack) fixed links in the README.
157. [past-due](https://github.com/past-due) suppressed an unfixable warning. 157. [Hani](https://github.com/hnkb) documented how to install the library with NuGet.
158. [Elvis Oric](https://github.com/elvisoric) improved Meson support. 158. [yann-morin-1998](https://github.com/yann-morin-1998) helped to reduce the CMake requirement to version 3.1.
159. [Matěj Plch](https://github.com/Afforix) fixed an example in the README. 159. [Konstantin Podsvirov](https://github.com/podsvirov) maintains a package for the MSYS2 software distro and fixed the MSYS2 package documentation.
160. [Mark Beckwith](https://github.com/wythe) fixed a typo. 160. [remyabel](https://github.com/remyabel2) added GNUInstallDirs to the CMake files.
161. [scinart](https://github.com/scinart) fixed a bug in the serializer. 161. [Taylor Howard](https://github.com/taylorhoward92) fixed a unit test.
162. [Patrick Boettcher](https://github.com/pboettch) implemented `push_back()` and `pop_back()` for JSON Pointers. 162. [Gabe Ron](https://github.com/Macr0Nerd) implemented the `to_string` method.
163. [Bruno Oliveira](https://github.com/nicoddemus) added support for Conda. 163. [Watal M. Iwasaki](https://github.com/heavywatal) fixed a Clang warning.
164. [Michele Caini](https://github.com/skypjack) fixed links in the README. 164. [Viktor Kirilov](https://github.com/onqtam) switched the unit tests from [Catch](https://github.com/philsquared/Catch) to [doctest](https://github.com/doctest/doctest)
165. [Hani](https://github.com/hnkb) documented how to install the library with NuGet. 165. [Juncheng E](https://github.com/ejcjason) fixed a typo.
166. [Mark Beckwith](https://github.com/wythe) fixed a typo. 166. [tete17](https://github.com/tete17) fixed a bug in the `contains` function.
167. [yann-morin-1998](https://github.com/yann-morin-1998) helped to reduce the CMake requirement to version 3.1. 167. [Xav83](https://github.com/Xav83) fixed some cppcheck warnings.
168. [Konstantin Podsvirov](https://github.com/podsvirov) maintains a package for the MSYS2 software distro. 168. [0xflotus](https://github.com/0xflotus) fixed some typos.
169. [remyabel](https://github.com/remyabel2) added GNUInstallDirs to the CMake files. 169. [Christian Deneke](https://github.com/chris0x44) added a const version of `json_pointer::back`.
170. [Taylor Howard](https://github.com/taylorhoward92) fixed a unit test. 170. [Julien Hamaide](https://github.com/crazyjul) made the `items()` function work with custom string types.
171. [Gabe Ron](https://github.com/Macr0Nerd) implemented the `to_string` method. 171. [Evan Nemerson](https://github.com/nemequ) updated fixed a bug in Hedley and updated this library accordingly.
172. [Watal M. Iwasaki](https://github.com/heavywatal) fixed a Clang warning. 172. [Florian Pigorsch](https://github.com/flopp) fixed a lot of typos.
173. [Viktor Kirilov](https://github.com/onqtam) switched the unit tests from [Catch](https://github.com/philsquared/Catch) to [doctest](https://github.com/onqtam/doctest) 173. [Camille Bégué](https://github.com/cbegue) fixed an issue in the conversion from `std::pair` and `std::tuple` to `json`.
174. [Juncheng E](https://github.com/ejcjason) fixed a typo. 174. [Anthony VH](https://github.com/AnthonyVH) fixed a compile error in an enum deserialization.
175. [tete17](https://github.com/tete17) fixed a bug in the `contains` function. 175. [Yuriy Vountesmery](https://github.com/ua-code-dragon) noted a subtle bug in a preprocessor check.
176. [Xav83](https://github.com/Xav83) fixed some cppcheck warnings. 176. [Chen](https://github.com/dota17) fixed numerous issues in the library.
177. [0xflotus](https://github.com/0xflotus) fixed some typos. 177. [Antony Kellermann](https://github.com/aokellermann) added a CI step for GCC 10.1.
178. [Christian Deneke](https://github.com/chris0x44) added a const version of `json_pointer::back`. 178. [Alex](https://github.com/gistrec) fixed an MSVC warning.
179. [Julien Hamaide](https://github.com/crazyjul) made the `items()` function work with custom string types. 179. [Rainer](https://github.com/rvjr) proposed an improvement in the floating-point serialization in CBOR.
180. [Evan Nemerson](https://github.com/nemequ) updated fixed a bug in Hedley and updated this library accordingly. 180. [Francois Chabot](https://github.com/FrancoisChabot) made performance improvements in the input adapters.
181. [Florian Pigorsch](https://github.com/flopp) fixed a lot of typos. 181. [Arthur Sonzogni](https://github.com/ArthurSonzogni) documented how the library can be included via `FetchContent`.
182. [Camille Bégué](https://github.com/cbegue) fixed an issue in the conversion from `std::pair` and `std::tuple` to `json`. 182. [Rimas Misevičius](https://github.com/rmisev) fixed an error message.
183. [Anthony VH](https://github.com/AnthonyVH) fixed a compile error in an enum deserialization. 183. [Alexander Myasnikov](https://github.com/alexandermyasnikov) fixed some examples and a link in the README.
184. [Yuriy Vountesmery](https://github.com/ua-code-dragon) noted a subtle bug in a preprocessor check. 184. [Hubert Chathi](https://github.com/uhoreg) made CMake's version config file architecture-independent.
185. [Chen](https://github.com/dota17) fixed numerous issues in the library. 185. [OmnipotentEntity](https://github.com/OmnipotentEntity) implemented the binary values for CBOR, MessagePack, BSON, and UBJSON.
186. [Antony Kellermann](https://github.com/aokellermann) added a CI step for GCC 10.1. 186. [ArtemSarmini](https://github.com/ArtemSarmini) fixed a compilation issue with GCC 10 and fixed a leak.
187. [Alex](https://github.com/gistrec) fixed an MSVC warning. 187. [Evgenii Sopov](https://github.com/sea5kg) integrated the library to the wsjcpp package manager.
188. [Rainer](https://github.com/rvjr) proposed an improvement in the floating-point serialization in CBOR. 188. [Sergey Linev](https://github.com/linev) fixed a compiler warning.
189. [Francois Chabot](https://github.com/FrancoisChabot) made performance improvements in the input adapters. 189. [Miguel Magalhães](https://github.com/magamig) fixed the year in the copyright.
190. [Arthur Sonzogni](https://github.com/ArthurSonzogni) documented how the library can be included via `FetchContent`. 190. [Alexander “weej” Jones](https://github.com/alex-weej) fixed an example in the README.
191. [Rimas Misevičius](https://github.com/rmisev) fixed an error message. 191. [Antoine Cœur](https://github.com/Coeur) fixed some typos in the documentation.
192. [Alexander Myasnikov](https://github.com/alexandermyasnikov) fixed some examples and a link in the README. 192. [jothepro](https://github.com/jothepro) updated links to the Hunter package.
193. [Hubert Chathi](https://github.com/uhoreg) made CMake's version config file architecture-independent. 193. [Dave Lee](https://github.com/kastiglione) fixed a link in the README.
194. [OmnipotentEntity](https://github.com/OmnipotentEntity) implemented the binary values for CBOR, MessagePack, BSON, and UBJSON. 194. [Joël Lamotte](https://github.com/Klaim) added instruction for using Build2's package manager.
195. [ArtemSarmini](https://github.com/ArtemSarmini) fixed a compilation issue with GCC 10 and fixed a leak. 195. [Paul Jurczak](https://github.com/pauljurczak) fixed an example in the README.
196. [Evgenii Sopov](https://github.com/sea5kg) integrated the library to the wsjcpp package manager. 196. [Carlos Gomes Martinho](https://github.com/gocarlos) updated the Conan package source.
197. [Sergey Linev](https://github.com/linev) fixed a compiler warning. 197. [Tridacnid](https://github.com/Tridacnid) improved the CMake tests.
198. [Miguel Magalhães](https://github.com/magamig) fixed the year in the copyright. 198. [Michael](https://github.com/MBalszun) fixed MSVC warnings.
199. [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) fixed a compilation issue with MSVC. 199. [Quentin Barbarat](https://github.com/quentin-dev) fixed an example in the documentation.
200. [Alexander “weej” Jones](https://github.com/alex-weej) fixed an example in the README. 200. [XyFreak](https://github.com/XyFreak) fixed a compiler warning.
201. [Antoine Cœur](https://github.com/Coeur) fixed some typos in the documentation. 201. [TotalCaesar659](https://github.com/TotalCaesar659) fixed links in the README and updated the links in the documentation to use HTTPS.
202. [jothepro](https://github.com/jothepro) updated links to the Hunter package. 202. [Tanuj Garg](https://github.com/tanuj208) improved the fuzzer coverage for UBSAN input.
203. [Dave Lee](https://github.com/kastiglione) fixed a link in the README. 203. [AODQ](https://github.com/AODQ) fixed a compiler warning.
204. [Joël Lamotte](https://github.com/Klaim) added instruction for using Build2's package manager. 204. [jwittbrodt](https://github.com/jwittbrodt) made `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` inline.
205. [Paul Jurczak](https://github.com/pauljurczak) fixed an example in the README. 205. [pfeatherstone](https://github.com/pfeatherstone) improved the upper bound of arguments of the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros.
206. [Sonu Lohani](https://github.com/sonulohani) fixed a warning. 206. [Jan Procházka](https://github.com/jprochazk) fixed a bug in the CBOR parser for binary and string values.
207. [Carlos Gomes Martinho](https://github.com/gocarlos) updated the Conan package source. 207. [T0b1-iOS](https://github.com/T0b1-iOS) fixed a bug in the new hash implementation.
208. [Konstantin Podsvirov](https://github.com/podsvirov) fixed the MSYS2 package documentation. 208. [Matthew Bauer](https://github.com/matthewbauer) adjusted the CBOR writer to create tags for binary subtypes.
209. [Tridacnid](https://github.com/Tridacnid) improved the CMake tests. 209. [gatopeich](https://github.com/gatopeich) implemented an ordered map container for `nlohmann::ordered_json`.
210. [Michael](https://github.com/MBalszun) fixed MSVC warnings. 210. [Érico Nogueira Rolim](https://github.com/ericonr) added support for pkg-config.
211. [Quentin Barbarat](https://github.com/quentin-dev) fixed an example in the documentation. 211. [KonanM](https://github.com/KonanM) proposed an implementation for the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros.
212. [XyFreak](https://github.com/XyFreak) fixed a compiler warning. 212. [Guillaume Racicot](https://github.com/gracicot) implemented `string_view` support and allowed C++20 support.
213. [TotalCaesar659](https://github.com/TotalCaesar659) fixed links in the README. 213. [Alex Reinking](https://github.com/alexreinking) improved CMake support for `FetchContent`.
214. [Tanuj Garg](https://github.com/tanuj208) improved the fuzzer coverage for UBSAN input. 214. [Hannes Domani](https://github.com/ssbssa) provided a GDB pretty printer.
215. [AODQ](https://github.com/AODQ) fixed a compiler warning. 215. Lars Wirzenius reviewed the README file.
216. [jwittbrodt](https://github.com/jwittbrodt) made `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` inline. 216. [Jun Jie](https://github.com/ongjunjie) fixed a compiler path in the CMake scripts.
217. [pfeatherstone](https://github.com/pfeatherstone) improved the upper bound of arguments of the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros. 217. [Ronak Buch](https://github.com/rbuch) fixed typos in the documentation.
218. [Jan Procházka](https://github.com/jprochazk) fixed a bug in the CBOR parser for binary and string values. 218. [Alexander Karzhenkov](https://github.com/karzhenkov) fixed a move constructor and the Travis builds.
219. [T0b1-iOS](https://github.com/T0b1-iOS) fixed a bug in the new hash implementation. 219. [Leonardo Lima](https://github.com/leozz37) added CPM.Cmake support.
220. [Matthew Bauer](https://github.com/matthewbauer) adjusted the CBOR writer to create tags for binary subtypes. 220. [Joseph Blackman](https://github.com/jbzdarkid) fixed a warning.
221. [gatopeich](https://github.com/gatopeich) implemented an ordered map container for `nlohmann::ordered_json`. 221. [Yaroslav](https://github.com/YarikTH) updated doctest and implemented unit tests.
222. [Érico Nogueira Rolim](https://github.com/ericonr) added support for pkg-config. 222. [Martin Stump](https://github.com/globberwops) fixed a bug in the CMake files.
223. [KonanM](https://github.com/KonanM) proposed an implementation for the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros. 223. [Jaakko Moisio](https://github.com/jasujm) fixed a bug in the input adapters.
224. [Guillaume Racicot](https://github.com/gracicot) implemented `string_view` support and allowed C++20 support. 224. [bl-ue](https://github.com/bl-ue) fixed some Markdown issues in the README file.
225. [Alex Reinking](https://github.com/alexreinking) improved CMake support for `FetchContent`. 225. [William A. Wieselquist](https://github.com/wawiesel) fixed an example from the README.
226. [Hannes Domani](https://github.com/ssbssa) provided a GDB pretty printer. 226. [abbaswasim](https://github.com/abbaswasim) fixed an example from the README.
227. Lars Wirzenius reviewed the README file. 227. [Remy Jette](https://github.com/remyjette) fixed a warning.
228. [Jun Jie](https://github.com/ongjunjie) fixed a compiler path in the CMake scripts. 228. [Fraser](https://github.com/frasermarlow) fixed the documentation.
229. [Ronak Buch](https://github.com/rbuch) fixed typos in the documentation. 229. [Ben Beasley](https://github.com/musicinmybrain) updated doctest.
230. [Alexander Karzhenkov](https://github.com/karzhenkov) fixed a move constructor and the Travis builds. 230. [Doron Behar](https://github.com/doronbehar) fixed pkg-config.pc.
231. [Leonardo Lima](https://github.com/leozz37) added CPM.Cmake support. 231. [raduteo](https://github.com/raduteo) fixed a warning.
232. [Joseph Blackman](https://github.com/jbzdarkid) fixed a warning. 232. [David Pfahler](https://github.com/theShmoo) added the possibility to compile the library without I/O support.
233. [Yaroslav](https://github.com/YarikTH) updated doctest and implemented unit tests. 233. [Morten Fyhn Amundsen](https://github.com/mortenfyhn) fixed a typo.
234. [Martin Stump](https://github.com/globberwops) fixed a bug in the CMake files. 234. [jpl-mac](https://github.com/jpl-mac) allowed treating the library as a system header in CMake.
235. [Jaakko Moisio](https://github.com/jasujm) fixed a bug in the input adapters. 235. [Jason Dsouza](https://github.com/jasmcaus) fixed the indentation of the CMake file.
236. [bl-ue](https://github.com/bl-ue) fixed some Markdown issues in the README file. 236. [offa](https://github.com/offa) added a link to Conan Center to the documentation.
237. [William A. Wieselquist](https://github.com/wawiesel) fixed an example from the README. 237. [Rafail Giavrimis](https://github.com/grafail) fixed the Google Benchmark default branch.
238. [abbaswasim](https://github.com/abbaswasim) fixed an example from the README. 238. [Louis Dionne](https://github.com/ldionne) fixed a conversion operator.
239. [Remy Jette](https://github.com/remyjette) fixed a warning. 239. [justanotheranonymoususer](https://github.com/justanotheranonymoususer) made the examples in the README more consistent.
240. [Fraser](https://github.com/frasermarlow) fixed the documentation. 240. [Finkman](https://github.com/Finkman) suppressed some `-Wfloat-equal` warnings and fixed the debug pretty-printer.
241. [Ben Beasley](https://github.com/musicinmybrain) updated doctest. 241. [Ferry Huberts](https://github.com/fhuberts) fixed `-Wswitch-enum` warnings and fixed a typo.
242. [Doron Behar](https://github.com/doronbehar) fixed pkg-config.pc. 242. [Arseniy Terekhin](https://github.com/senyai) made the GDB pretty-printer robust against unset variable names.
243. [raduteo](https://github.com/raduteo) fixed a warning. 243. [Amir Masoud Abdol](https://github.com/amirmasoudabdol) updated the Homebrew command as nlohmann/json is now in homebrew-core.
244. [David Pfahler](https://github.com/theShmoo) added the possibility to compile the library without I/O support. 244. [Hallot](https://github.com/Hallot) fixed some `-Wextra-semi-stmt warnings`.
245. [Morten Fyhn Amundsen](https://github.com/mortenfyhn) fixed a typo. 245. [Giovanni Cerretani](https://github.com/gcerretani) fixed `-Wunused` warnings on `JSON_DIAGNOSTICS` and fixed a check for `std::filesystem`.
246. [jpl-mac](https://github.com/jpl-mac) allowed treating the library as a system header in CMake. 246. [Bogdan Popescu](https://github.com/Kapeli) hosts the [docset](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for offline documentation viewers.
247. [Jason Dsouza](https://github.com/jasmcaus) fixed the indentation of the CMake file. 247. [Carl Smedstad](https://github.com/carlsmedstad) fixed an assertion error when using `JSON_DIAGNOSTICS`.
248. [offa](https://github.com/offa) added a link to Conan Center to the documentation. 248. [miikka75](https://github.com/miikka75) provided an important fix to compile C++17 code with Clang 9.
249. [TotalCaesar659](https://github.com/TotalCaesar659) updated the links in the documentation to use HTTPS. 249. [Maarten Becker](https://github.com/kernie) fixed a warning for shadowed variables.
250. [Rafail Giavrimis](https://github.com/grafail) fixed the Google Benchmark default branch. 250. [Cristi Vîjdea](https://github.com/axnsan12) fixed typos in the `operator[]` documentation.
251. [Louis Dionne](https://github.com/ldionne) fixed a conversion operator. 251. [Alex Beregszaszi](https://github.com/axic) fixed spelling mistakes in comments.
252. [justanotheranonymoususer](https://github.com/justanotheranonymoususer) made the examples in the README more consistent. 252. [Dirk Stolle](https://github.com/striezel) fixed typos in the documentation.
253. [Finkman](https://github.com/Finkman) suppressed some `-Wfloat-equal` warnings. 253. [Daniel Albuschat](https://github.com/daniel-kun) corrected the parameter name in the `parse` documentation.
254. [Ferry Huberts](https://github.com/fhuberts) fixed `-Wswitch-enum` warnings. 254. [Prince Mendiratta](https://github.com/Prince-Mendiratta) fixed a link to the FAQ.
255. [Arseniy Terekhin](https://github.com/senyai) made the GDB pretty-printer robust against unset variable names. 255. [Florian Albrechtskirchinger](https://github.com/falbrechtskirchinger) implemented `std::string_view` support for object keys and made dozens of other improvements.
256. [Amir Masoud Abdol](https://github.com/amirmasoudabdol) updated the Homebrew command as nlohmann/json is now in homebrew-core. 256. [Qianqian Fang](https://github.com/fangq) implemented the Binary JData (BJData) format.
257. [Hallot](https://github.com/Hallot) fixed some `-Wextra-semi-stmt warnings`. 257. [pketelsen](https://github.com/pketelsen) added macros `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` and `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`.
258. [Giovanni Cerretani](https://github.com/gcerretani) fixed `-Wunused` warnings on `JSON_DIAGNOSTICS`. 258. [DarkZeros](https://github.com/DarkZeros) adjusted to code to not clash with Arduino defines.
259. [Bogdan Popescu](https://github.com/Kapeli) hosts the [docset](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for offline documentation viewers. 259. [flagarde](https://github.com/flagarde) fixed the output of `meta()` for MSVC.
260. [Carl Smedstad](https://github.com/carlsmedstad) fixed an assertion error when using `JSON_DIAGNOSTICS`. 260. [Dimitris Apostolou](https://github.com/rex4539) fixed a typo.
261. [miikka75](https://github.com/miikka75) provided an important fix to compile C++17 code with Clang 9. 261. [Michael Nosthoff](https://github.com/heinemml) fixed a typo.
262. [Maarten Becker](https://github.com/kernie) fixed a warning for shadowed variables. 262. [JungHoon Lee](https://github.com/jhnlee) fixed a typo.
263. [Cristi Vîjdea](https://github.com/axnsan12) fixed typos in the `operator[]` documentation. 263. [Faruk D.](https://github.com/fdiblen) fixed the CITATION.CFF file.
264. [Alex Beregszaszi](https://github.com/axic) fixed spelling mistakes in comments. 264. [Andrea Cocito](https://github.com/puffetto) added a clarification on macro usage to the documentation.
265. [Dirk Stolle](https://github.com/striezel) fixed typos in documentation. 265. [Krzysiek Karbowiak](https://github.com/kkarbowiak) refactored the tests to use `CHECK_THROWS_WITH_AS`.
266. [Daniel Albuschat](https://github.com/daniel-kun) corrected the parameter name in the `parse` documentation. 266. [Chaoqi Zhang](https://github.com/prncoprs) fixed a typo.
267. [Prince Mendiratta](https://github.com/Prince-Mendiratta) fixed a link to the FAQ. 267. [ivanovmp](https://github.com/ivanovmp) fixed a whitespace error.
268. [Florian Albrechtskirchinger](https://github.com/falbrechtskirchinger) implemented `std::string_view` support for object keys and made dozens of other improvements. 268. [KsaNL](https://github.com/KsaNL) fixed a build error when including `<windows.h>`.
269. [Qianqian Fang](https://github.com/fangq) implemented the Binary JData (BJData) format. 269. [Andrea Pappacoda](https://github.com/Tachi107) moved `.pc` and `.cmake` files to `share` directory.
270. [pketelsen](https://github.com/pketelsen) added macros `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` and `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`. 270. [Wolf Vollprecht](https://github.com/wolfv) added the `patch_inplace` function.
271. [DarkZeros](https://github.com/DarkZeros) adjusted to code to not clash with Arduino defines. 271. [Jake Zimmerman](https://github.com/jez) highlighted common usage patterns in the README file.
272. [flagarde](https://github.com/flagarde) fixed the output of `meta()` for MSVC. 272. [NN](https://github.com/NN---) added the Visual Studio output directory to `.gitignore`.
273. [Giovanni Cerretani](https://github.com/gcerretani) fixed a check for `std::filesystem`. 273. [Romain Reignier](https://github.com/romainreignier) improved the performance of the vector output adapter.
274. [Dimitris Apostolou](https://github.com/rex4539) fixed a typo. 274. [Mike](https://github.com/Mike-Leo-Smith) fixed the `std::iterator_traits`.
275. [Ferry Huberts](https://github.com/fhuberts) fixed a typo. 275. [Richard Hozák](https://github.com/richardhozak) added macro `JSON_NO_ENUM` to disable default enum conversions.
276. [Michael Nosthoff](https://github.com/heinemml) fixed a typo. 276. [vakokako](https://github.com/vakokako) fixed tests when compiling with C++20.
277. [JungHoon Lee](https://github.com/jhnlee) fixed a typo. 277. [Alexander “weej” Jones](https://github.com/alexweej) fixed an example in the README.
278. [Faruk D.](https://github.com/fdiblen) fixed the CITATION.CFF file. 278. [Eli Schwartz](https://github.com/eli-schwartz) added more files to the `include.zip` archive.
279. [Andrea Cocito](https://github.com/puffetto) added a clarification on macro usage to the documentation. 279. [Kevin Lu](https://github.com/kevinlul) fixed a compilation issue when typedefs with certain names were present.
280. [Krzysiek Karbowiak](https://github.com/kkarbowiak) refactored the tests to use `CHECK_THROWS_WITH_AS`. 280. [Trevor Hickey](https://github.com/luxe) improved the description of an example.
281. [Chaoqi Zhang](https://github.com/prncoprs) fixed a typo. 281. [Jef LeCompte](https://github.com/jef) updated the year in the README file.
282. [ivanovmp](https://github.com/ivanovmp) fixed a whitespace error. 282. [Alexandre Hamez](https://github.com/ahamez) fixed a warning.
283. [KsaNL](https://github.com/KsaNL) fixed a build error when including `<windows.h>`. 283. [Maninderpal Badhan](https://github.com/mbadhan) fixed a typo.
284. [Andrea Pappacoda](https://github.com/Tachi107) moved `.pc` and `.cmake` files to `share` directory. 284. [kevin--](https://github.com/kevin--) added a note to an example in the README file.
285. [Wolf Vollprecht](https://github.com/wolfv) added the `patch_inplace` function. 285. [I](https://github.com/wx257osn2) fixed a typo.
286. [Jake Zimmerman](https://github.com/jez) highlighted common usage patterns in the README file. 286. [Gregorio Litenstein](https://github.com/Lord-Kamina) fixed the Clang detection.
287. [NN](https://github.com/NN---) added the Visual Studio output directory to `.gitignore`. 287. [Andreas Smas](https://github.com/andoma) added a Doozer badge.
288. [Romain Reignier](https://github.com/romainreignier) improved the performance of the vector output adapter. 288. [WanCW](https://github.com/wancw) fixed the string conversion with Clang.
289. [Mike](https://github.com/Mike-Leo-Smith) fixed the `std::iterator_traits`. 289. [zhaohuaxishi](https://github.com/zhaohuaxishi) fixed a Doxygen error.
290. [Richard Hozák](https://github.com/richardhozak) added macro `JSON_NO_ENUM` to disable default enum conversions. 290. [emvivre](https://github.com/emvivre) removed an invalid parameter from CMake.
291. [vakokako](https://github.com/vakokako) fixed tests when compiling with C++20. 291. [Tobias Hermann](https://github.com/Dobiasd) fixed a link in the README file.
292. [Alexander “weej” Jones](https://github.com/alexweej) fixed an example in the README. 292. [Michael](https://github.com/traits) fixed a warning.
293. [Eli Schwartz](https://github.com/eli-schwartz) added more files to the `include.zip` archive. 293. [Ryan Mulder](https://github.com/ryanjmulder) added `ensure_ascii` to the `dump` function.
294. [Kevin Lu](https://github.com/kevinlul) fixed a compilation issue when typedefs with certain names were present. 294. [Muri Nicanor](https://github.com/murinicanor) fixed the `sed` discovery in the Makefile.
295. [Trevor Hickey](https://github.com/luxe) improved the description of an example. 295. [David Avedissian](https://github.com/dgavedissian) implemented SFINAE-friendly `iterator_traits`.
296. [Jef LeCompte](https://github.com/jef) updated the year in the README file. 296. [AQNOUCH Mohammed](https://github.com/aqnouch) fixed a typo in the README.
297. [Alexandre Hamez](https://github.com/ahamez) fixed a warning. 297. [Gareth Sylvester-Bradley](https://github.com/garethsb) added `operator/=` and `operator/` to construct JSON pointers.
298. [Maninderpal Badhan](https://github.com/mbadhan) fixed a typo. 298. [Michael Macnair](https://github.com/mykter) added support for afl-fuzz testing.
299. [kevin--](https://github.com/kevin--) added a note to an example in the README file. 299. [Berkus Decker](https://github.com/berkus) fixed a typo in the README.
300. [I](https://github.com/wx257osn2) fixed a typo. 300. [Illia Polishchuk](https://github.com/ilqvya) improved the CMake testing.
301. [Gregorio Litenstein](https://github.com/Lord-Kamina) fixed the Clang detection. 301. [Ikko Ashimine](https://github.com/eltociear) fixed two typos.
302. [Andreas Smas](https://github.com/andoma) added a Doozer badge. 302. [Raphael Grimm](https://github.com/barcode) added the possibility to define a custom base class.
303. [WanCW](https://github.com/wancw) fixed the string conversion with Clang. 303. [tocic](https://github.com/tocic) fixed typos in the documentation.
304. [zhaohuaxishi](https://github.com/zhaohuaxishi) fixed a Doxygen error. 304. [Vertexwahn](https://github.com/Vertexwahn) added Bazel build support.
305. [emvivre](https://github.com/emvivre) removed an invalid parameter from CMake. 305. [DavidKorczynski](https://github.com/DavidKorczynski) added a CIFuzz CI GitHub action.
306. [Tobias Hermann](https://github.com/Dobiasd) fixed a link in the README file. 306. [Florian Segginger](https://github.com/floriansegginger) bumped the years in the README.
307. [Michael](https://github.com/traits) fixed a warning. 307. [haadfida](https://github.com/haadfida) cleaned up the badges of used services.
308. [Ryan Mulder](https://github.com/ryanjmulder) added `ensure_ascii` to the `dump` function. 308. [Arsen Arsenović](https://github.com/ArsenArsen) fixed a build error.
309. [Muri Nicanor](https://github.com/murinicanor) fixed the `sed` discovery in the Makefile. 309. [theevilone45](https://github.com/theevilone45) fixed a typo in a CMake file.
310. [David Avedissian](https://github.com/dgavedissian) implemented SFINAE-friendly `iterator_traits`. 310. [Sergei Trofimovich](https://github.com/trofi) fixed the custom allocator support.
311. [AQNOUCH Mohammed](https://github.com/aqnouch) fixed a typo in the README. 311. [Joyce](https://github.com/joycebrum) fixed some security issues in the GitHub workflows.
312. [Gareth Sylvester-Bradley](https://github.com/garethsb) added `operator/=` and `operator/` to construct JSON pointers. 312. [Nicolas Jakob](https://github.com/njakob) add vcpkg version badge.
313. [Michael Macnair](https://github.com/mykter) added support for afl-fuzz testing. 313. [Tomerkm](https://github.com/Tomerkm) added tests.
314. [Berkus Decker](https://github.com/berkus) fixed a typo in the README. 314. [No.](https://github.com/tusooa) fixed the use of `get<>` calls.
315. [Illia Polishchuk](https://github.com/ilqvya) improved the CMake testing. 315. [taro](https://github.com/tarolling) fixed a typo in the `CODEOWNERS` file.
316. [Ikko Ashimine](https://github.com/eltociear) fixed a typo. 316. [Felix Yan](https://github.com/felixonmars) fixed a typo in the README.
317. [Raphael Grimm](https://github.com/barcode) added the possibility to define a custom base class. 317. [HO-COOH](https://github.com/HO-COOH) fixed a parenthesis in the documentation.
318. [tocic](https://github.com/tocic) fixed typos in the documentation. 318. [miny1233](https://github.com/miny1233) fixed a parenthesis in the documentation.
319. [Vertexwahn](https://github.com/Vertexwahn) added Bazel build support. 319. [tomalakgeretkal](https://github.com/tomalakgeretkal) fixed a compilation error.
320. [Dirk Stolle](https://github.com/striezel) fixed typos in the documentation. 320. [alferov](https://github.com/ALF-ONE) fixed a compilation error and fixed a version in the documentation.
321. [DavidKorczynski](https://github.com/DavidKorczynski) added a CIFuzz CI GitHub action. 321. [Craig Scott](https://github.com/craigscott-crascit) fixed a deprecation warning in CMake.
322. [Finkman](https://github.com/Finkman) fixed the debug pretty-printer. 322. [Vyacheslav Zhdanovskiy](https://github.com/ZeronSix) added macros for serialization-only types.
323. [Florian Segginger](https://github.com/floriansegginger) bumped the years in the README. 323. [Mathieu Westphal](https://github.com/mwestphal) fixed typos.
324. [haadfida](https://github.com/haadfida) cleaned up the badges of used services. 324. [scribam](https://github.com/scribam) fixed the MinGW workflow.
325. [Arsen Arsenović](https://github.com/ArsenArsen) fixed a build error. 325. [Aleksei Sapitskii](https://github.com/aleksproger) added support for Apple's Swift Package Manager.
326. [theevilone45](https://github.com/theevilone45) fixed a typo in a CMake file. 326. [Benjamin Buch](https://github.com/bebuch) fixed the installation path in CMake.
327. [Sergei Trofimovich](https://github.com/trofi) fixed the custom allocator support. 327. [Colby Haskell](https://github.com/colbychaskell) clarified the parse error message in case a file cannot be opened.
328. [Joyce](https://github.com/joycebrum) fixed some security issues in the GitHub workflows. 328. [Juan Carlos Arevalo Baeza](https://github.com/TheJCAB) fixed the enum conversion.
329. [Nicolas Jakob](https://github.com/njakob) add vcpkg version badge. 329. [ss](https://github.com/serge-s) fixed the amalgamation call.
330. [Tomerkm](https://github.com/Tomerkm) added tests. 330. [AniketDhemare](https://github.com/AniketDhemare) fixed a version in the documentation.
331. [No.](https://github.com/tusooa) fixed the use of `get<>` calls. 331. [Philip Müller](https://github.com/philip-paul-mueller) fixed an example.
332. [taro](https://github.com/tarolling) fixed a typo in the `CODEOWNERS` file. 332. [Leila Shcheglova](https://github.com/LeilaShcheglova) fixed a warning in a test.
333. [Ikko Eltociear Ashimine](https://github.com/eltociear) fixed a typo. 333. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation.
334. [Felix Yan](https://github.com/felixonmars) fixed a typo in the README. 334. [laterlaugh](https://github.com/laterlaugh) fixed some typos.
335. [HO-COOH](https://github.com/HO-COOH) fixed a parenthesis in the documentation. 335. [Yuanhao Jia](https://github.com/MrJia1997) fixed the GDB pretty printer.
336. [Ivor Wanders](https://github.com/iwanders) fixed the examples to catch exception by `const&`. 336. [Fallen_Breath](https://github.com/Fallen-Breath) fixed an example for JSON Pointer.
337. [miny1233](https://github.com/miny1233) fixed a parenthesis in the documentation. 337. [Nikhil Idiculla](https://github.com/tsnl) fixed some typos.
338. [tomalakgeretkal](https://github.com/tomalakgeretkal) fixed a compilation error. 338. [Griffin Myers](https://github.com/gmyers18) updated the Natvis file.
339. [alferov](https://github.com/ALF-ONE) fixed a compilation error. 339. [thetimr](https://github.com/thetimr) fixed a typo in the documentation.
340. [Craig Scott](https://github.com/craigscott-crascit) fixed a deprecation warning in CMake. 340. [Balazs Erseki](https://github.com/zerocukor287) fixed a URL in the contribution guidelines.
341. [Vyacheslav Zhdanovskiy](https://github.com/ZeronSix) added macros for serialization-only types. 341. [Niccolò Iardella](https://github.com/rotolof) added `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros.
342. [Mathieu Westphal](https://github.com/mwestphal) fixed typos. 342. [Borislav Stanimirov](https://github.com/iboB) allowed overriding the CMake target name.
343. [scribam](https://github.com/scribam) fixed the MinGW workflow. 343. [Captain Crutches](https://github.com/captaincrutches) made `iterator_proxy_value` a `std::forward_iterator`.
344. [Aleksei Sapitskii](https://github.com/aleksproger) added support for Apple's Swift Package Manager. 344. [Fredrik Sandhei](https://github.com/fsandhei) added type conversion support for `std::optional`.
345. [Benjamin Buch](https://github.com/bebuch) fixed the installation path in CMake. 345. [jh96](https://github.com/jordan-hoang) added exceptions when `nullptr` is passed to `parse`.
346. [Colby Haskell](https://github.com/colbychaskell) clarified the parse error message in case a file cannot be opened. 346. [Stuart Gorman](https://github.com/StuartGorman) fixed number parsing when `EINTR` set in `errno`.
347. [Juan Carlos Arevalo Baeza](https://github.com/TheJCAB) fixed the enum conversion. 347. [Dylan Baker](https://github.com/dcbaker) generated a pkg-config file that follows the pkg-config conventions.
348. [alferov](https://github.com/ALF-ONE) fixed a version in the documentation. 348. [Tianyi Chen](https://github.com/TianyiChen) optimized the binary `get_number` implementation.
349. [ss](https://github.com/serge-s) fixed the amalgamation call. 349. [peng-wang-cn](https://github.com/peng-wang-cn) added type conversion support for multidimensional arrays.
350. [AniketDhemare](https://github.com/AniketDhemare) fixed a version in the documentation. 350. [Einars Netlis-Galejs](https://github.com/EinarsNG) added `ONLY_SERIALIZE` for `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros.
351. [Philip Müller](https://github.com/philip-paul-mueller) fixed an example. 351. [Marcel](https://github.com/mering) removed `alwayslink=True` Bazel flag.
352. [Leila Shcheglova](https://github.com/LeilaShcheglova) fixed a warning in a test. 352. [Harinath Nampally](https://github.com/hnampally) added diagnostic positions to exceptions.
353. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation. 353. [Nissim Armand Ben Danan](https://github.com/NissimBendanan) fixed `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` with an empty JSON instance.
354. [laterlaugh](https://github.com/laterlaugh) fixed some typos. 354. [Michael Valladolid](https://github.com/codenut) added support for BSON uint64 serialization/deserialization.
355. [Yuanhao Jia](https://github.com/MrJia1997) fixed the GDB pretty printer. 355. [Nikhil](https://github.com/nikhilreddydev) updated the documentation.
356. [Fallen_Breath](https://github.com/Fallen-Breath) fixed an example for JSON Pointer. 356. [Nebojša Cvetković](https://github.com/nebkat) added support for BJDATA optimized binary array type.
357. [Nikhil Idiculla](https://github.com/tsnl) fixed some typos. 357. [Sushrut Shringarputale](https://github.com/sushshring) added support for diagnostic positions.
358. [Griffin Myers](https://github.com/gmyers18) updated the Natvis file. 358. [kimci86](https://github.com/kimci86) templated to `NLOHMANN_DEFINE_TYPE` macros to also support `ordered_json`.
359. [thetimr](https://github.com/thetimr) fixed a typo in the documentation. 359. [Richard Topchii](https://github.com/richardtop) added support for VisionOS in the Swift Package Manager.
360. [Balazs Erseki](https://github.com/zerocukor287) fixed a URL in the contribution guidelines. 360. [Robert Chisholm](https://github.com/Robadob) fixed a typo.
361. [Niccolò Iardella](https://github.com/rotolof) added `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros. 361. [zjyhjqs](https://github.com/zjyhjqs) added CPack support.
362. [Borislav Stanimirov](https://github.com/iboB) allowed overriding the CMake target name. 362. [bitFiedler](https://github.com/bitFiedler) made GDB pretty printer work with Python 3.8.
363. [Captain Crutches](https://github.com/captaincrutches) made `iterator_proxy_value` a `std::forward_iterator`. 363. [Gianfranco Costamagna](https://github.com/LocutusOfBorg) fixed a compiler warning.
364. [Fredrik Sandhei](https://github.com/fsandhei) added type conversion support for `std::optional`. 364. [risa2000](https://github.com/risa2000) made `std::filesystem::path` conversion to/from UTF-8 encoded string explicit.
365. [jh96](https://github.com/jordan-hoang) added exceptions when `nullptr` is passed to `parse`. 365. [AM](https://github.com/maqnouch) fixed typos in the README.
366. [Stuart Gorman](https://github.com/StuartGorman) fixed number parsing when `EINTR` set in `errno`. 366. [dmenendez-gruposantander](https://github.com/dmenendez-gruposantander) fixed typos in the comments of the examples.
367. [Dylan Baker](https://github.com/dcbaker) generated a pkg-config file that follows the pkg-config conventions. 367. [Mihai Stan](https://github.com/mstan-xx) fixed comparisons against the literal `0`.
368. [Tianyi Chen](https://github.com/TianyiChen) optimized the binary `get_number` implementation. 368. [Matt Gumbel](https://github.com/intelmatt) fixed some `-Weffc++` warnings.
369. [peng-wang-cn](https://github.com/peng-wang-cn) added type conversion support for multidimensional arrays. 369. [vimpunk](https://github.com/vimpunk) moved a lambda out of an unevaluated context to support older compilers.
370. [Einars Netlis-Galejs](https://github.com/EinarsNG) added `ONLY_SERIALIZE` for `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros. 370. [Chris Harris](https://github.com/cjh1) fixed the compilation with GCC 4.8.
371. [Marcel](https://github.com/mering) removed `alwayslink=True` Bazel flag. 371. [Palmer Dabbelt](https://github.com/palmer-dabbelt) generated and installed a pkg-config file.
372. [Harinath Nampally](https://github.com/hnampally) added diagnostic positions to exceptions. 372. [Gus Pozuelo](https://github.com/ap-viavi) made `ordered_map` compatible with GCC 5.5, Clang 3.6, and Xcode 9.
373. [Nissim Armand Ben Danan](https://github.com/NissimBendanan) fixed `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` with an empty JSON instance. 373. [AK](https://github.com/Lioncky) fixed an MSVC build error caused by the `min`/`max` macros from `windows.h`.
374. [Michael Valladolid](https://github.com/codenut) added support for BSON uint64 serialization/deserialization. 374. [Sergiu Deitsch](https://github.com/sergiud) provided a fallback for missing `char8_t` support.
375. [Nikhil](https://github.com/nikhilreddydev) updated the documentation. 375. [Xiaochuan Ye](https://github.com/XueSongTap) fixed `from_msgpack` for `std::byte` input by specializing `std::char_traits`.
376. [Nebojša Cvetković](https://github.com/nebkat) added support for BJDATA optimized binary array type. 376. [Ville Vesilehto](https://github.com/thevilledev) fixed an overflow in the BJData size calculation and rejected overflowing negative integers in CBOR.
377. [Sushrut Shringarputale](https://github.com/sushshring) added support for diagnostic positions. 377. [NmPassTHFan](https://github.com/nmpassthf) replaced the deprecated `std::is_trivial` for C++26.
378. [kimci86](https://github.com/kimci86) templated to `NLOHMANN_DEFINE_TYPE` macros to also support `ordered_json`. 378. [Chris Ever](https://github.com/chirsz-ever) added the `ignore_trailing_commas` parser option.
379. [Richard Topchii](https://github.com/richardtop) added support for VisionOS in the Swift Package Manager. 379. [Kuan-Fu Wu](https://github.com/kfwu1999) fixed the example code for `json_pointer` initialization.
380. [Robert Chisholm](https://github.com/Robadob) fixed a typo. 380. [David Kilzer](https://github.com/ddkilzer) added a missing header to the input adapters.
381. [zjyhjqs](https://github.com/zjyhjqs) added CPack support. 381. [Miko](https://github.com/mikomikotaishi) added proper C++20 module support, simplified the module API, and fixed missing exports.
382. [bitFiedler](https://github.com/bitFiedler) made GDB pretty printer work with Python 3.8. 382. [hitgirl](https://github.com/hitgil) fixed the CMake configuration when cross-compiling.
383. [Gianfranco Costamagna](https://github.com/LocutusOfBorg) fixed a compiler warning. 383. [Devon Thomas](https://github.com/ThomaDevOSU) mentioned the Artistic Style formatting in the contribution guidelines.
384. [risa2000](https://github.com/risa2000) made `std::filesystem::path` conversion to/from UTF-8 encoded string explicit. 384. [Erik Hu](https://github.com/Erikhu1) made Coveralls upload errors non-fatal in the CI.
385. [AM](https://github.com/maqnouch) fixed typos in the README. 385. [co63oc](https://github.com/co63oc) fixed typos.
386. [dmenendez-gruposantander](https://github.com/dmenendez-gruposantander) fixed typos in the comments of the examples. 386. [DmitriBogdanov](https://github.com/DmitriBogdanov) fixed broken package manager links in the documentation.
387. [Mihai Stan](https://github.com/mstan-xx) fixed comparisons against the literal `0`. 387. [Bander](https://github.com/banderzhm) improved the MSVC compatibility of the C++ modules.
388. [Matt Gumbel](https://github.com/intelmatt) fixed some `-Weffc++` warnings. 388. [Andy Choi](https://github.com/ccpong) removed an unnecessary `template` keyword before `get` in the README and the documentation.
389. [vimpunk](https://github.com/vimpunk) moved a lambda out of an unevaluated context to support older compilers. 389. [SamareshSingh](https://github.com/ssam18) fixed single-element brace initialization to copy/move instead of wrapping in an array, fixed the `WITH_DEFAULT` macros for `ordered_map`, and handled moved events in `serve_header.py`.
390. [Chris Harris](https://github.com/cjh1) fixed the compilation with GCC 4.8. 390. [Aditya](https://github.com/Lumowhisp) improved the documentation of the documentation generation.
391. [Palmer Dabbelt](https://github.com/palmer-dabbelt) generated and installed a pkg-config file. 391. [cheese1](https://github.com/cheese1) clarified the README.
392. [Gus Pozuelo](https://github.com/ap-viavi) made `ordered_map` compatible with GCC 5.5, Clang 3.6, and Xcode 9. 392. [KhloodElhossiny](https://github.com/khloodelhossiny) enabled `std::string_view` keys in `operator[]`.
393. [AK](https://github.com/Lioncky) fixed an MSVC build error caused by the `min`/`max` macros from `windows.h`. 393. [Charles Cabergs](https://github.com/cacharle) fixed a `-Wtautological-constant-out-of-range-compare` warning.
394. [Sergiu Deitsch](https://github.com/sergiud) provided a fallback for missing `char8_t` support. 394. [EALePain](https://github.com/EALePain) made the `std::tuple` conversion work with reference types such as `std::tie`.
395. [Xiaochuan Ye](https://github.com/XueSongTap) fixed `from_msgpack` for `std::byte` input by specializing `std::char_traits`. 395. [koala_oishi](https://github.com/chibi-dogs) fixed grammatical wording in the README.
396. [Ville Vesilehto](https://github.com/thevilledev) fixed an overflow in the BJData size calculation and rejected overflowing negative integers in CBOR. 396. [riccardoori11](https://github.com/riccardoori11) fixed a typo in the documentation.
397. [NmPassTHFan](https://github.com/nmpassthf) replaced the deprecated `std::is_trivial` for C++26. 397. [Swastik Bose](https://github.com/VasuBhakt) fixed the parent pointers after `update()` with `JSON_DIAGNOSTICS` and fixed the Doxygen autolinking of requirements.
398. [Chris Ever](https://github.com/chirsz-ever) added the `ignore_trailing_commas` parser option. 398. [trdesilva](https://github.com/trdesilva) added `front`, `pop_front`, and `push_front` to `json_pointer`.
399. [Kuan-Fu Wu](https://github.com/kfwu1999) fixed the example code for `json_pointer` initialization. 399. [Akhilesh Arora](https://github.com/akhilesharora) fixed an incomplete-type error with `ordered_json`.
400. [David Kilzer](https://github.com/ddkilzer) added a missing header to the input adapters. 400. [Hariom Phulre](https://github.com/hariomphulre) fixed the C++20 modules compilation with GCC.
401. [Miko](https://github.com/mikomikotaishi) added proper C++20 module support, simplified the module API, and fixed missing exports. 401. [Kirill Lokotkov](https://github.com/RUSLoker) fixed printing `long double` values.
402. [hitgirl](https://github.com/hitgil) fixed the CMake configuration when cross-compiling. 402. [George Sedov](https://github.com/radistmorse) added the `NLOHMANN_DEFINE_TYPE_*_WITH_NAMES` macros.
403. [Devon Thomas](https://github.com/ThomaDevOSU) mentioned the Artistic Style formatting in the contribution guidelines. 403. [Caillin Nugent](https://github.com/nugentcaillin) added the `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macro.
404. [Erik Hu](https://github.com/Erikhu1) made Coveralls upload errors non-fatal in the CI. 404. [Cosmin D.](https://github.com/drcosmin) fixed `std::filesystem::path` conversions and added an MSVC workaround for `std::unique_ptr`.
405. [co63oc](https://github.com/co63oc) fixed typos. 405. [Paul Dreik](https://github.com/pauldreik) fixed a test relying on implementation-specific behavior.
406. [DmitriBogdanov](https://github.com/DmitriBogdanov) fixed broken package manager links in the documentation. 406. [Daniel Falk](https://github.com/daniel-falk) added missing copyright notices to the SBOM.
407. [Bander](https://github.com/banderzhm) improved the MSVC compatibility of the C++ modules. 407. [Federico Sfriso](https://github.com/federicosfriso05-dotcom) added support for constructing JSON values from C++20 range views.
408. [Andy Choi](https://github.com/ccpong) removed an unnecessary `template` keyword before `get` in the README and the documentation. 408. [Luke Banicevic](https://github.com/banaboi) fixed corrupt BSON output for lengths exceeding `INT32_MAX`, cleaned up the BSON writer, and improved the documentation.
409. [SamareshSingh](https://github.com/ssam18) fixed single-element brace initialization to copy/move instead of wrapping in an array, fixed the `WITH_DEFAULT` macros for `ordered_map`, and handled moved events in `serve_header.py`. 409. [Patrick Armstrong](https://github.com/Patrick10199) updated the CBOR references and the half-precision float assertions.
410. [Aditya](https://github.com/Lumowhisp) improved the documentation of the documentation generation. 410. [Yash Bavadiya](https://github.com/xevrion) added checks to all BSON reads.
411. [cheese1](https://github.com/cheese1) clarified the README. 411. [hum4nBeing](https://github.com/hum4nBeing) fixed the overflow handling of high-precision numbers in UBJSON.
412. [KhloodElhossiny](https://github.com/khloodelhossiny) enabled `std::string_view` keys in `operator[]`. 412. [tomatotomata](https://github.com/tomatotomata) added checks for reading CBOR tagged subtypes.
413. [Charles Cabergs](https://github.com/cacharle) fixed a `-Wtautological-constant-out-of-range-compare` warning. 413. [YingqiDuan](https://github.com/YingqiDuan) documented the BSON interoperability.
414. [EALePain](https://github.com/EALePain) made the `std::tuple` conversion work with reference types such as `std::tie`. 414. [KBS](https://github.com/youdie006) documented the standards compliance and the strictness of `parse()` and `operator>>`.
415. [koala_oishi](https://github.com/chibi-dogs) fixed grammatical wording in the README. 415. [Petr Bělohlávek](https://github.com/petrbel) added Clang 21 and 22 to the CI.
416. [riccardoori11](https://github.com/riccardoori11) fixed a typo in the documentation. 416. [Dmitry Rantovov](https://github.com/darkdi) fixed the placement of a CBOR documentation block.
417. [Swastik Bose](https://github.com/VasuBhakt) fixed the parent pointers after `update()` with `JSON_DIAGNOSTICS` and fixed the Doxygen autolinking of requirements. 417. [ljcjclljc](https://github.com/ljcjclljc) fixed the comparison of large unsigned integers with signed integers.
418. [trdesilva](https://github.com/trdesilva) added `front`, `pop_front`, and `push_front` to `json_pointer`. 418. [Sahil Kamate](https://github.com/sahilkamate03) fixed the handling of CBOR tags 0-5 and 21-23.
419. [Akhilesh Arora](https://github.com/akhilesharora) fixed an incomplete-type error with `ordered_json`. 419. [Krishnanand G](https://github.com/Krishnanand-G) made the UBJSON writer reject `use_type` without `use_size`.
420. [Hariom Phulre](https://github.com/hariomphulre) fixed the C++20 modules compilation with GCC. 420. [whn](https://github.com/Whning0513) documented the lenient BSON input handling and corrected the complexity of `to_bson`.
421. [Kirill Lokotkov](https://github.com/RUSLoker) fixed printing `long double` values. 421. [elix3r](https://github.com/22elix3r) fixed `update()` with `merge_objects` when merging a primitive into an object.
422. [George Sedov](https://github.com/radistmorse) added the `NLOHMANN_DEFINE_TYPE_*_WITH_NAMES` macros. 422. [Avionic Harshit](https://github.com/avionicharshit-byte) made `diff()` linear when an array shrinks.
423. [Caillin Nugent](https://github.com/nugentcaillin) added the `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macro. 423. [Qatadaha Bin Matloob](https://github.com/qatcod) fixed comparisons between integers and floats and fixed unparsable BJData output.
424. [Cosmin D.](https://github.com/drcosmin) fixed `std::filesystem::path` conversions and added an MSVC workaround for `std::unique_ptr`. 424. [Wu Shuwen](https://github.com/dajiaohuang) removed an unused include.
425. [Paul Dreik](https://github.com/pauldreik) fixed a test relying on implementation-specific behavior.
426. [Daniel Falk](https://github.com/daniel-falk) added missing copyright notices to the SBOM.
427. [Federico Sfriso](https://github.com/federicosfriso05-dotcom) added support for constructing JSON values from C++20 range views.
428. [Luke Banicevic](https://github.com/banaboi) fixed corrupt BSON output for lengths exceeding `INT32_MAX`, cleaned up the BSON writer, and improved the documentation.
429. [Patrick Armstrong](https://github.com/Patrick10199) updated the CBOR references and the half-precision float assertions.
430. [Yash Bavadiya](https://github.com/xevrion) added checks to all BSON reads.
431. [hum4nBeing](https://github.com/hum4nBeing) fixed the overflow handling of high-precision numbers in UBJSON.
432. [tomatotomata](https://github.com/tomatotomata) added checks for reading CBOR tagged subtypes.
433. [YingqiDuan](https://github.com/YingqiDuan) documented the BSON interoperability.
434. [KBS](https://github.com/youdie006) documented the standards compliance and the strictness of `parse()` and `operator>>`.
435. [Petr Bělohlávek](https://github.com/petrbel) added Clang 21 and 22 to the CI.
436. [Dmitry Rantovov](https://github.com/darkdi) fixed the placement of a CBOR documentation block.
437. [ljcjclljc](https://github.com/ljcjclljc) fixed the comparison of large unsigned integers with signed integers.
438. [Sahil Kamate](https://github.com/sahilkamate03) fixed the handling of CBOR tags 0-5 and 21-23.
439. [Krishnanand G](https://github.com/Krishnanand-G) made the UBJSON writer reject `use_type` without `use_size`.
440. [whn](https://github.com/Whning0513) documented the lenient BSON input handling and corrected the complexity of `to_bson`.
441. [elix3r](https://github.com/22elix3r) fixed `update()` with `merge_objects` when merging a primitive into an object.
442. [Avionic Harshit](https://github.com/avionicharshit-byte) made `diff()` linear when an array shrinks.
443. [Qatadaha Bin Matloob](https://github.com/qatcod) fixed comparisons between integers and floats and fixed unparsable BJData output.
444. [Wu Shuwen](https://github.com/dajiaohuang) removed an unused include.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
@@ -1879,21 +1850,28 @@ The library itself consists of a single header file licensed under the MIT licen
- [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows - [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
- [**Artistic Style**](https://astyle.sourceforge.net) for automatic source code indentation - [**Artistic Style**](https://astyle.sourceforge.net) for automatic source code indentation
- [**Clang**](https://clang.llvm.org) for compilation with code sanitizers - [**Clang**](https://clang.llvm.org) for compilation with code sanitizers
- [**Clang-Tidy**](https://clang.llvm.org/extra/clang-tidy/) for static analysis and style checks
- [**CMake**](https://cmake.org) for build automation - [**CMake**](https://cmake.org) for build automation
- [**Codacy**](https://www.codacy.com) for further [code analysis](https://app.codacy.com/gh/nlohmann/json/dashboard) - [**Codacy**](https://www.codacy.com) for further [code analysis](https://app.codacy.com/gh/nlohmann/json/dashboard)
- [**CodeQL**](https://codeql.github.com) for code scanning
- [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json) - [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
- [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json) - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
- [**cppcheck**](https://cppcheck.sourceforge.io) for static analysis - [**cppcheck**](https://cppcheck.sourceforge.io) for static analysis
- [**doctest**](https://github.com/onqtam/doctest) for the unit tests - [**cpplint**](https://github.com/cpplint/cpplint) for style checks
- [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md) - [**doctest**](https://github.com/doctest/doctest) for the unit tests
- [**Flawfinder**](https://dwheeler.com/flawfinder/) for static analysis
- [**GitHub Changelog Generator**](https://github.com/github-changelog-generator/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
- [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks - [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks
- [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros - [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros
- [**include-what-you-use**](https://include-what-you-use.org) to check the included headers
- [**Infer**](https://fbinfer.com) for static analysis
- [**lcov**](https://github.com/linux-test-project/lcov) to process coverage information and create an HTML view - [**lcov**](https://github.com/linux-test-project/lcov) to process coverage information and create an HTML view
- [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz - [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
- [**Material for MkDocs**](https://squidfunk.github.io/mkdocs-material/) for the style of the documentation site - [**Material for MkDocs**](https://squidfunk.github.io/mkdocs-material/) for the style of the documentation site
- [**MkDocs**](https://www.mkdocs.org) for the documentation site - [**MkDocs**](https://www.mkdocs.org) for the documentation site
- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json)) - [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json))
- [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments. - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments.
- [**Semgrep**](https://semgrep.dev) for static analysis
- [**Valgrind**](https://valgrind.org) to check for correct memory management - [**Valgrind**](https://valgrind.org) to check for correct memory management
## Notes ## Notes
@@ -1987,10 +1965,10 @@ If the testdata is not found, several test suites will fail like this:
``` ```
=============================================================================== ===============================================================================
json/tests/src/make_test_data_available.hpp:21: json/tests/src/make_test_data_available.hpp:26:
TEST CASE: check test suite is downloaded TEST CASE: check test suite is downloaded
json/tests/src/make_test_data_available.hpp:23: FATAL ERROR: REQUIRE( utils::check_testsuite_downloaded() ) is NOT correct! json/tests/src/make_test_data_available.hpp:28: FATAL ERROR: REQUIRE( utils::check_testsuite_downloaded() ) is NOT correct!
values: REQUIRE( false ) values: REQUIRE( false )
logged: Test data not found in 'json/cmake-build-debug/json_test_data'. logged: Test data not found in 'json/cmake-build-debug/json_test_data'.
Please execute target 'download_test_data' before running this test suite. Please execute target 'download_test_data' before running this test suite.
@@ -2001,10 +1979,10 @@ json/tests/src/make_test_data_available.hpp:23: FATAL ERROR: REQUIRE( utils::che
In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure` will fail. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure` will fail. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information.
Some tests are requiring network to be properly execute. They are labeled as `git_required`. Please execute `ctest -LE git_required` to skip these tests. See [issue #4851](https://github.com/nlohmann/json/issues/4851) for more information. Some tests require network access to execute properly. They are labeled as `git_required`. Please execute `ctest -LE git_required` to skip these tests. See [issue #4851](https://github.com/nlohmann/json/issues/4851) for more information.
Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. Furthermore, assertions must be switched off to ensure reproducible builds (see [discussion 4494](https://github.com/nlohmann/json/discussions/4494)). Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. Furthermore, assertions must be switched off to ensure reproducible builds (see [discussion 4494](https://github.com/nlohmann/json/discussions/4494)).
Note you need to call `cmake -LE "not_reproducible|git_required"` to exclude both labels. See [issue #2596](https://github.com/nlohmann/json/issues/2596) for more information. Note you need to call `ctest -LE "not_reproducible|git_required"` to exclude both labels. See [issue #2596](https://github.com/nlohmann/json/issues/2596) for more information.
As Intel compilers use unsafe floating point optimization by default, the unit tests may fail. Use flag [`/fp:precise`](https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/fp-model-fp.html) then. As Intel compilers use unsafe floating point optimization by default, the unit tests may fail. Use flag [`/fp:precise`](https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/fp-model-fp.html) then.
+8 -2
View File
@@ -10,10 +10,10 @@ SPDX-FileCopyrightText = "2013-2026 Niels Lohmann <https://nlohmann.me>"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"
[[annotations]] [[annotations]]
path = "include/nlohmann/thirdparty/hedley.hpp" path = "include/nlohmann/thirdparty/hedley/hedley.hpp"
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "2016-2021 Evan Nemerson <evan@nemerson.com>" SPDX-FileCopyrightText = "2016-2021 Evan Nemerson <evan@nemerson.com>"
SPDX-License-Identifier = "CC0" SPDX-License-Identifier = "CC0-1.0"
[[annotations]] [[annotations]]
path = "include/nlohmann/detail/meta/cpp_future.hpp" path = "include/nlohmann/detail/meta/cpp_future.hpp"
@@ -50,3 +50,9 @@ path = "tools/gdb_pretty_printer/**"
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "2020 Hannes Domani <https://github.com/ssbssa>" SPDX-FileCopyrightText = "2020 Hannes Domani <https://github.com/ssbssa>"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"
[[annotations]]
path = "docs/docset/icon*.png"
precedence = "override"
SPDX-FileCopyrightText = "JSON logo (public domain), https://commons.wikimedia.org/wiki/File:JSON_vector_logo.svg"
SPDX-License-Identifier = "CC0-1.0"
+14 -17
View File
@@ -5,15 +5,14 @@ set(N 10)
# Needed tools. # Needed tools.
############################################################################### ###############################################################################
include(FindPython3)
find_package(Python3 COMPONENTS Interpreter) find_package(Python3 COMPONENTS Interpreter)
find_program(CLANG_TOOL NAMES clang++-HEAD clang++ clang++-22 clang++-21 clang++-20 clang++-19 clang++-18 clang++-17 clang++-16 clang++-15 clang++-14 clang++-13 clang++-12 clang++-11 clang++) find_program(CLANG_TOOL NAMES clang++ clang++-HEAD clang++-22 clang++-21 clang++-20 clang++-19 clang++-18 clang++-17 clang++-16 clang++-15 clang++-14 clang++-13 clang++-12 clang++-11)
execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION) execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION)
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}") string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}")
message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})") message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})")
find_program(CLANG_TIDY_TOOL NAMES clang-tidy-22 clang-tidy-21 clang-tidy-20 clang-tidy-19 clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11 clang-tidy) find_program(CLANG_TIDY_TOOL NAMES clang-tidy clang-tidy-22 clang-tidy-21 clang-tidy-20 clang-tidy-19 clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11)
execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION) execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION)
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}") string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}")
message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})") message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})")
@@ -69,9 +68,7 @@ string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" VALGRIND_TOOL_VERSION "${VALGRIND_TOOL_V
message(STATUS "🔖 Valgrind ${VALGRIND_TOOL_VERSION} (${VALGRIND_TOOL})") message(STATUS "🔖 Valgrind ${VALGRIND_TOOL_VERSION} (${VALGRIND_TOOL})")
find_program(GENHTML_TOOL NAMES genhtml) find_program(GENHTML_TOOL NAMES genhtml)
find_program(PLOG_CONVERTER_TOOL NAMES plog-converter) find_program(SCAN_BUILD_TOOL NAMES scan-build scan-build-22 scan-build-21 scan-build-20 scan-build-19 scan-build-18 scan-build-17 scan-build-16 scan-build-15 scan-build-14 scan-build-13 scan-build-12 scan-build-11)
find_program(PVS_STUDIO_ANALYZER_TOOL NAMES pvs-studio-analyzer)
find_program(SCAN_BUILD_TOOL NAMES scan-build-15 scan-build-14 scan-build-13 scan-build-12 scan-build-11 scan-build)
# the individual source files # the individual source files
file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp) file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp)
@@ -367,7 +364,7 @@ add_custom_target(ci_test_coverage
COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused
COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused
COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept
COMMAND genhtml --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept COMMAND ${GENHTML_TOOL} --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept
COMMENT "Compile and test with coverage" COMMENT "Compile and test with coverage"
) )
@@ -392,8 +389,8 @@ add_custom_target(ci_test_clang_sanitizer
# Check if header is amalgamated and sources are properly indented. # Check if header is amalgamated and sources are properly indented.
############################################################################### ###############################################################################
# Same file set as .github/workflows/check_amalgamation.yml, so a direct push to develop/master/release/* # Same file set as .github/workflows/check_amalgamation.yml, so this CMake target (which can be run locally) is held
# (which only this CMake target checks, not the pull_request-only workflow) is held to the same standard. # to the same standard as the workflow (which runs for pull requests and for pushes to develop/master/release/*).
file(GLOB_RECURSE INDENT_FILES file(GLOB_RECURSE INDENT_FILES
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.hpp ${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.hpp
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cpp ${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cpp
@@ -423,7 +420,7 @@ add_custom_target(ci_test_amalgamation
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s . COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s .
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s . COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp ${include_dir}/json_literals.hpp
COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/scripts/gen_bazel_build_file.cmake COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/scripts/gen_bazel_build_file.cmake
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
@@ -434,6 +431,8 @@ add_custom_target(ci_test_amalgamation
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES} COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
COMMAND rm -f ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~ ${include_dir}/json_literals.hpp~ ${PROJECT_SOURCE_DIR}/BUILD.bazel~
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Check amalgamation, formatting, and BUILD.bazel" COMMENT "Check amalgamation, formatting, and BUILD.bazel"
) )
@@ -719,7 +718,7 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0 # the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind JSON_DisableTupleReferenceConversion JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
JSON_StrictNulHandling JSON_StrictBinaryUTF8 JSON_DeleteDeprecatedFunctions) JSON_StrictNulHandling JSON_StrictBinaryUTF8 JSON_DeleteDeprecatedFunctions)
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0}) set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0}) set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
@@ -776,17 +775,14 @@ add_custom_target(ci_cmake_flags
# Use more installed compilers. # Use more installed compilers.
############################################################################### ###############################################################################
foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 g++-11 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13 clang++-14 clang++-15 clang++-16 clang++-17 clang++-18 clang++-19 clang++-20) foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6)
find_program(COMPILER_TOOL NAMES ${COMPILER}) find_program(COMPILER_TOOL NAMES ${COMPILER})
if (COMPILER_TOOL) if (COMPILER_TOOL)
unset(ADDITIONAL_FLAGS)
add_custom_target(ci_test_compiler_${COMPILER} add_custom_target(ci_test_compiler_${COMPILER}
COMMAND CXX=${COMPILER} ${CMAKE_COMMAND} COMMAND CXX=${COMPILER} ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_BuildTests=ON -DJSON_FastTests=ON
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_${COMPILER}
${ADDITIONAL_FLAGS}
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER}
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
COMMENT "Compile and test with ${COMPILER}" COMMENT "Compile and test with ${COMPILER}"
@@ -800,7 +796,6 @@ add_custom_target(ci_test_compiler_default
-DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_BuildTests=ON -DJSON_FastTests=ON
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_default -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_default
${ADDITIONAL_FLAGS}
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_default --parallel ${N} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_default --parallel ${N}
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_default && ${CMAKE_CTEST_COMMAND} --parallel ${N} -LE git_required --output-on-failure COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_default && ${CMAKE_CTEST_COMMAND} --parallel ${N} -LE git_required --output-on-failure
COMMENT "Compile and test with default C++ compiler" COMMENT "Compile and test with default C++ compiler"
@@ -823,10 +818,12 @@ add_custom_target(ci_cuda_example
add_custom_target(ci_module_cpp20 add_custom_target(ci_module_cpp20
COMMAND ${CMAKE_COMMAND} COMMAND ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_CI=ON -DNLOHMANN_JSON_BUILD_MODULES=ON -DJSON_Install=ON -DJSON_CI=ON -DNLOHMANN_JSON_BUILD_MODULES=ON -DJSON_Install=ON
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
-S${PROJECT_SOURCE_DIR}/tests/module_cpp20 -B${PROJECT_BINARY_DIR}/ci_module_cpp20 -S${PROJECT_SOURCE_DIR}/tests/module_cpp20 -B${PROJECT_BINARY_DIR}/ci_module_cpp20
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/ci_module_cpp20 COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/ci_module_cpp20
VERBATIM
) )
############################################################################### ###############################################################################
+1
View File
@@ -49,6 +49,7 @@ cc_library(
hdrs = [ hdrs = [
"single_include/nlohmann/json.hpp", "single_include/nlohmann/json.hpp",
"single_include/nlohmann/json_fwd.hpp", "single_include/nlohmann/json_fwd.hpp",
"single_include/nlohmann/json_literals.hpp",
], ],
includes = ["single_include"], includes = ["single_include"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
+2 -2
View File
@@ -7,12 +7,12 @@ This URL provides the most recent documentation and also applies to previous ver
functions is not removed; instead, it is marked as deprecated. functions is not removed; instead, it is marked as deprecated.
If you want to view the documentation for a specific tag or commit hash, you can generate it locally as follows (example If you want to view the documentation for a specific tag or commit hash, you can generate it locally as follows (example
using tag `v3.10.2`): using tag `v3.12.0`):
```shell ```shell
git clone https://github.com/nlohmann/json.git git clone https://github.com/nlohmann/json.git
cd json cd json
git checkout v3.10.2 git checkout v3.12.0
make install_venv serve -C docs/mkdocs make install_venv serve -C docs/mkdocs
``` ```
+11 -55
View File
@@ -1,48 +1,24 @@
SHELL=/usr/bin/env bash SHELL=/usr/bin/env bash
SED ?= $(shell which gsed 2>/dev/null || which sed) PYTHON=../mkdocs/venv/bin/python3
MKDOCS_PAGES=$(shell cd ../mkdocs/docs/ && find * -type f -name '*.md' | sort)
.PHONY: all .PHONY: all
all: JSON_for_Modern_C++.tgz all: JSON_for_Modern_C++.tgz
docSet.dsidx: docSet.sql # generate the search index (the docset target does this itself, this is
# generate index # only handy for inspecting the index)
sqlite3 docSet.dsidx <docSet.sql docSet.dsidx: generate_docset.py
$(PYTHON) generate_docset.py index docSet.dsidx
JSON_for_Modern_C++.docset: Info.plist docSet.dsidx # build the documentation and turn it into a self-contained docset
rm -fr JSON_for_Modern_C++.docset JSON_for_Modern_C++.tgz .PHONY: JSON_for_Modern_C++.docset
mkdir -p JSON_for_Modern_C++.docset/Contents/Resources/Documents/ JSON_for_Modern_C++.docset:
cp icon*.png JSON_for_Modern_C++.docset
cp Info.plist JSON_for_Modern_C++.docset/Contents
# build and copy documentation
$(MAKE) install_venv -C ../mkdocs $(MAKE) install_venv -C ../mkdocs
$(MAKE) build -C ../mkdocs $(MAKE) build -C ../mkdocs
cp -r ../mkdocs/site/* JSON_for_Modern_C++.docset/Contents/Resources/Documents $(PYTHON) generate_docset.py docset ../mkdocs/site .
# patch CSS to hide navigation items
echo -e "\n\nheader, footer, nav.md-tabs, nav.md-tabs--active, div.md-sidebar--primary, a.md-content__button { display: none; }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
# fix spacing
echo -e "\n\ndiv.md-sidebar div.md-sidebar--secondary, div.md-main__inner { top: 0; margin-top: 0 }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
# remove "JSON for Modern C++" from page titles (fallback)
find JSON_for_Modern_C++.docset/Contents/Resources/Documents -type f -exec $(SED) -i 's| - JSON for Modern C++</title>|</title>|' {} +
# replace page titles with name from index, if available
for page in $(MKDOCS_PAGES); do \
case "$$page" in \
*/index.md) path=$${page/\/index.md/} ;; \
*) path=$${page/.md/} ;; \
esac; \
title=$$(sqlite3 docSet.dsidx "SELECT name FROM searchIndex WHERE path='$$path/index.html'" | tr '\n' ',' | $(SED) -e 's/,/, /g' -e 's/, $$/\n/'); \
if [ "x$$title" != "x" ]; then \
$(SED) -i "s%<title>.*</title>%<title>$$title</title>%" "JSON_for_Modern_C++.docset/Contents/Resources/Documents/$$path/index.html"; \
fi \
done
# clean up
rm JSON_for_Modern_C++.docset/Contents/Resources/Documents/sitemap.*
# copy index
cp docSet.dsidx JSON_for_Modern_C++.docset/Contents/Resources/
.PHONY: JSON_for_Modern_C++.tgz
JSON_for_Modern_C++.tgz: JSON_for_Modern_C++.docset JSON_for_Modern_C++.tgz: JSON_for_Modern_C++.docset
tar --exclude='.DS_Store' -cvzf JSON_for_Modern_C++.tgz JSON_for_Modern_C++.docset $(PYTHON) generate_docset.py tgz .
# install docset for Zeal documentation browser (https://zealdocs.org/) # install docset for Zeal documentation browser (https://zealdocs.org/)
.PHONY: install_docset_zeal .PHONY: install_docset_zeal
@@ -52,26 +28,6 @@ install_docset_zeal: JSON_for_Modern_C++.docset
mkdir -p $$docset_root; \ mkdir -p $$docset_root; \
cp -r JSON_for_Modern_C++.docset $$docset_root/ cp -r JSON_for_Modern_C++.docset $$docset_root/
# both targets below compare the docset search index with the mkdocs page
# set. They share the same normalization (docs/foo/index.md and
# docs/foo.md both become foo/index.html, the URL mkdocs itself would
# give the page; the top-level index.md is excluded, as it is not part
# of the hand-curated docSet.sql) and use comm(1) on two sorted lists
# instead of running a sqlite3 query, or an O(n*m) nested shell loop,
# once per page.
DOCSET_INDEX_PATHS=$(shell sqlite3 docSet.dsidx "SELECT DISTINCT path FROM searchIndex" | sort)
DOCSET_PAGE_PATHS=$(shell echo '$(MKDOCS_PAGES)' | tr ' ' '\n' | grep -v '^index\.md$$' | $(SED) -E 's@/index\.md$$@/index.html@; s@\.md$$@/index.html@' | sort)
# list mkdocs pages missing from the docset index
.PHONY: list_missing_pages
list_missing_pages: docSet.dsidx
@comm -23 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
# list paths in the docset index without a corresponding mkdocs page
.PHONY: list_removed_paths
list_removed_paths: docSet.dsidx
@comm -13 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
.PHONY: clean .PHONY: clean
clean: clean:
rm -f docSet.dsidx rm -f docSet.dsidx
+3 -1
View File
@@ -4,7 +4,8 @@ The folder contains the required files to create a [docset](https://kapeli.com/d
documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), or documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), or
[Zeal](https://zealdocs.org). [Zeal](https://zealdocs.org).
The docset can be created with The docset (pages and search index) is generated by `generate_docset.py` from the mkdocs site and `mkdocs.yml`. It
can be created with
```sh ```sh
make JSON_for_Modern_C++.docset make JSON_for_Modern_C++.docset
@@ -12,6 +13,7 @@ make JSON_for_Modern_C++.docset
The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a
`JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly. `JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly.
`make docSet.dsidx` builds only the search index.
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B). A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
-289
View File
@@ -1,289 +0,0 @@
DROP TABLE IF EXISTS searchIndex;
CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);
CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path);
-- API
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer', 'Class', 'api/adl_serializer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::from_json', 'Function', 'api/adl_serializer/from_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::to_json', 'Function', 'api/adl_serializer/to_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype', 'Class', 'api/byte_container_with_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::byte_container_with_subtype', 'Constructor', 'api/byte_container_with_subtype/byte_container_with_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::clear_subtype', 'Method', 'api/byte_container_with_subtype/clear_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::has_subtype', 'Method', 'api/byte_container_with_subtype/has_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator!=', 'Operator', 'api/byte_container_with_subtype/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator==', 'Operator', 'api/byte_container_with_subtype/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::set_subtype', 'Method', 'api/byte_container_with_subtype/set_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype', 'Method', 'api/byte_container_with_subtype/subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/basic_json/format_as/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::begin', 'Method', 'api/basic_json/begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary', 'Function', 'api/basic_json/binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary_t', 'Type', 'api/basic_json/binary_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::boolean_t', 'Type', 'api/basic_json/boolean_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbegin', 'Method', 'api/basic_json/cbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbor_tag_handler_t', 'Enum', 'api/basic_json/cbor_tag_handler_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cend', 'Method', 'api/basic_json/cend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::clear', 'Method', 'api/basic_json/clear/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::contains', 'Method', 'api/basic_json/contains/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::count', 'Method', 'api/basic_json/count/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crbegin', 'Method', 'api/basic_json/crbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crend', 'Method', 'api/basic_json/crend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::default_object_comparator_t', 'Type', 'api/basic_json/default_object_comparator_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::diff', 'Function', 'api/basic_json/diff/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::dump', 'Method', 'api/basic_json/dump/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace', 'Method', 'api/basic_json/emplace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace_back', 'Method', 'api/basic_json/emplace_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::empty', 'Method', 'api/basic_json/empty/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end', 'Method', 'api/basic_json/end/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end_pos', 'Method', 'api/basic_json/end_pos/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::erase', 'Method', 'api/basic_json/erase/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::error_handler_t', 'Enum', 'api/basic_json/error_handler_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::exception', 'Class', 'api/basic_json/exception/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::find', 'Method', 'api/basic_json/find/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::flatten', 'Method', 'api/basic_json/flatten/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bjdata', 'Function', 'api/basic_json/from_bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bson', 'Function', 'api/basic_json/from_bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_cbor', 'Function', 'api/basic_json/from_cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_msgpack', 'Function', 'api/basic_json/from_msgpack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bon8', 'Function', 'api/basic_json/from_bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_ubjson', 'Function', 'api/basic_json/from_ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::front', 'Method', 'api/basic_json/front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get', 'Method', 'api/basic_json/get/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_allocator', 'Function', 'api/basic_json/get_allocator/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_binary', 'Method', 'api/basic_json/get_binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ptr', 'Method', 'api/basic_json/get_ptr/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ref', 'Method', 'api/basic_json/get_ref/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_to', 'Method', 'api/basic_json/get_to/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::input_format_t', 'Enum', 'api/basic_json/input_format_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::insert', 'Method', 'api/basic_json/insert/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::invalid_iterator', 'Class', 'api/basic_json/invalid_iterator/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_array', 'Method', 'api/basic_json/is_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_binary', 'Method', 'api/basic_json/is_binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_boolean', 'Method', 'api/basic_json/is_boolean/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_discarded', 'Method', 'api/basic_json/is_discarded/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_null', 'Method', 'api/basic_json/is_null/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number', 'Method', 'api/basic_json/is_number/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_float', 'Method', 'api/basic_json/is_number_float/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_integer', 'Method', 'api/basic_json/is_number_integer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_unsigned', 'Method', 'api/basic_json/is_number_unsigned/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_object', 'Method', 'api/basic_json/is_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_primitive', 'Method', 'api/basic_json/is_primitive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_string', 'Method', 'api/basic_json/is_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_structured', 'Method', 'api/basic_json/is_structured/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::items', 'Method', 'api/basic_json/items/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_base_class_t', 'Type', 'api/basic_json/json_base_class_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_serializer', 'Class', 'api/basic_json/json_serializer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::max_size', 'Method', 'api/basic_json/max_size/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::merge_patch', 'Method', 'api/basic_json/merge_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::meta', 'Function', 'api/basic_json/meta/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_float_t', 'Type', 'api/basic_json/number_float_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_integer_t', 'Type', 'api/basic_json/number_integer_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_unsigned_t', 'Type', 'api/basic_json/number_unsigned_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object', 'Function', 'api/basic_json/object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_comparator_t', 'Type', 'api/basic_json/object_comparator_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_t', 'Type', 'api/basic_json/object_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator ValueType', 'Operator', 'api/basic_json/operator_ValueType/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator value_t', 'Operator', 'api/basic_json/operator_value_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator[]', 'Operator', 'api/basic_json/operator[]/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator=', 'Operator', 'api/basic_json/operator=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator+=', 'Operator', 'api/basic_json/operator+=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator==', 'Operator', 'api/basic_json/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator!=', 'Operator', 'api/basic_json/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<', 'Operator', 'api/basic_json/operator_lt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=', 'Operator', 'api/basic_json/operator_le/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>', 'Operator', 'api/basic_json/operator_gt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>=', 'Operator', 'api/basic_json/operator_ge/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=>', 'Operator', 'api/basic_json/operator_spaceship/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::out_of_range', 'Class', 'api/basic_json/out_of_range/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::other_error', 'Class', 'api/basic_json/other_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse', 'Function', 'api/basic_json/parse/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_error', 'Class', 'api/basic_json/parse_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_event_t', 'Enum', 'api/basic_json/parse_event_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parser_callback_t', 'Type', 'api/basic_json/parser_callback_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch', 'Method', 'api/basic_json/patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch_inplace', 'Method', 'api/basic_json/patch_inplace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::push_back', 'Method', 'api/basic_json/push_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rbegin', 'Method', 'api/basic_json/rbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rend', 'Method', 'api/basic_json/rend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::sax_parse', 'Function', 'api/basic_json/sax_parse/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::size', 'Method', 'api/basic_json/size/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::start_pos', 'Method', 'api/basic_json/start_pos/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::string_t', 'Type', 'api/basic_json/string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::swap', 'Method', 'api/basic_json/swap/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type', 'Method', 'api/basic_json/type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_error', 'Class', 'api/basic_json/type_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_name', 'Method', 'api/basic_json/type_name/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::unflatten', 'Method', 'api/basic_json/unflatten/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::update', 'Method', 'api/basic_json/update/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bjdata', 'Function', 'api/basic_json/to_bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bson', 'Function', 'api/basic_json/to_bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_cbor', 'Function', 'api/basic_json/to_cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_msgpack', 'Function', 'api/basic_json/to_msgpack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bon8', 'Function', 'api/basic_json/to_bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Method', 'api/basic_json/to_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value_t', 'Enum', 'api/basic_json/value_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::with_t', 'Type', 'api/basic_json/with_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Method', 'api/basic_json/~basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::back', 'Method', 'api/json_pointer/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::empty', 'Method', 'api/json_pointer/empty/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::front', 'Method', 'api/json_pointer/front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::json_pointer', 'Constructor', 'api/json_pointer/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator==', 'Operator', 'api/json_pointer/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator!=', 'Operator', 'api/json_pointer/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/', 'Operator', 'api/json_pointer/operator_slash/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/=', 'Operator', 'api/json_pointer/operator_slasheq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator string_t', 'Operator', 'api/json_pointer/operator_string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator<=>', 'Operator', 'api/json_pointer/operator_spaceship/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::parent_pointer', 'Method', 'api/json_pointer/parent_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_back', 'Method', 'api/json_pointer/pop_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_front', 'Method', 'api/json_pointer/pop_front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_back', 'Method', 'api/json_pointer/push_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_front', 'Method', 'api/json_pointer/push_front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::string_t', 'Type', 'api/json_pointer/string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::to_string', 'Method', 'api/json_pointer/to_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax', 'Class', 'api/json_sax/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary', 'Method', 'api/json_sax/binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::boolean', 'Method', 'api/json_sax/boolean/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_array', 'Method', 'api/json_sax/end_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_object', 'Method', 'api/json_sax/end_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::key', 'Method', 'api/json_sax/key/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::null', 'Method', 'api/json_sax/null/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float', 'Method', 'api/json_sax/number_float/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer', 'Method', 'api/json_sax/number_integer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned', 'Method', 'api/json_sax/number_unsigned/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::parse_error', 'Method', 'api/json_sax/parse_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_array', 'Method', 'api/json_sax/start_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_object', 'Method', 'api/json_sax/start_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string', 'Method', 'api/json_sax/string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json', 'Literal', 'api/operator_literal_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json_pointer', 'Literal', 'api/operator_literal_json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api/operator_ltlt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator>>', 'Operator', 'api/operator_gtgt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json', 'Class', 'api/ordered_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map', 'Class', 'api/ordered_map/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::formatter<basic_json>', 'Class', 'api/basic_json/std_formatter/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::hash<basic_json>', 'Class', 'api/basic_json/std_hash/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::swap<basic_json>', 'Function', 'api/basic_json/std_swap/index.html');
-- Features
INSERT INTO searchIndex(name, type, path) VALUES ('Arbitrary Type Conversions', 'Guide', 'features/arbitrary_types/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats', 'Guide', 'features/binary_formats/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BJData', 'Guide', 'features/binary_formats/bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BSON', 'Guide', 'features/binary_formats/bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: CBOR', 'Guide', 'features/binary_formats/cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: MessagePack', 'Guide', 'features/binary_formats/messagepack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BON8', 'Guide', 'features/binary_formats/bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: UBJSON', 'Guide', 'features/binary_formats/ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Values', 'Guide', 'features/binary_values/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Comments', 'Guide', 'features/comments/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access', 'Guide', 'features/element_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Access with default value: value', 'Guide', 'features/element_access/default_value/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Checked access: at', 'Guide', 'features/element_access/checked_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Unchecked access: operator[]', 'Guide', 'features/element_access/unchecked_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Exceptions', 'Guide', 'home/exceptions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Migration Guide', 'Guide', 'integration/migration_guide/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: CMake', 'Guide', 'integration/cmake/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Header only', 'Guide', 'integration/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Package Managers', 'Guide', 'integration/package_managers/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Pkg-config', 'Guide', 'integration/pkg-config/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Iterators', 'Guide', 'features/iterators/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Merge Patch', 'Guide', 'features/merge_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Patch and Diff', 'Guide', 'features/json_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Pointer', 'Guide', 'features/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('nlohmann Namespace', 'Guide', 'features/namespace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Types', 'Guide', 'features/types/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Types: Number Handling', 'Guide', 'features/types/number_handling/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Object Order', 'Guide', 'features/object_order/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Performance', 'Guide', 'features/performance/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing', 'Guide', 'features/parsing/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: JSON Lines', 'Guide', 'features/parsing/json_lines/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parser Callbacks', 'Guide', 'features/parsing/parser_callbacks/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parsing and Exceptions', 'Guide', 'features/parsing/parse_exceptions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: SAX Interface', 'Guide', 'features/parsing/sax_interface/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Untrusted Input', 'Guide', 'features/parsing/untrusted_input/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Runtime Assertions', 'Guide', 'features/assertions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Specializing enum conversion', 'Guide', 'features/enum_conversion/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Supported Macros', 'Guide', 'features/macros/index.html');
-- Macros
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'api/macros/json_assert/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_BRACE_INIT_COPY_SEMANTICS', 'Macro', 'api/macros/json_brace_init_copy_semantics/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DELETE_DEPRECATED_FUNCTIONS', 'Macro', 'api/macros/json_delete_deprecated_functions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_TUPLE_REFERENCE_CONVERSION', 'Macro', 'api/macros/json_disable_tuple_reference_conversion/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_20', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_23', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_26', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_EXPERIMENTAL_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_RANGES', 'Macro', 'api/macros/json_has_ranges/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STATIC_RTTI', 'Macro', 'api/macros/json_has_static_rtti/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STD_FORMAT', 'Macro', 'api/macros/json_has_std_format/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_THREE_WAY_COMPARISON', 'Macro', 'api/macros/json_has_three_way_comparison/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NOEXCEPTION', 'Macro', 'api/macros/json_noexception/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_AUTOMATIC_UDLS', 'Macro', 'api/macros/json_no_automatic_udls/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_IO', 'Macro', 'api/macros/json_no_io/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_THREAD_LOCAL', 'Macro', 'api/macros/json_no_thread_local/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_PRECISE_STREAM_POSITION', 'Macro', 'api/macros/json_precise_stream_position/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_LIBRARY_VERSION_CHECK', 'Macro', 'api/macros/json_skip_library_version_check/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_UNSUPPORTED_COMPILER_CHECK', 'Macro', 'api/macros/json_skip_unsupported_compiler_check/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_BINARY_UTF8', 'Macro', 'api/macros/json_strict_binary_utf8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_NUL_HANDLING', 'Macro', 'api/macros/json_strict_nul_handling/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_THROW_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_GLOBAL_UDLS', 'Macro', 'api/macros/json_use_global_udls/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_IMPLICIT_CONVERSIONS', 'Macro', 'api/macros/json_use_implicit_conversions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON', 'Macro', 'api/macros/json_use_legacy_discarded_value_comparison/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS', 'Macro', 'api/macros/json_use_objects_for_enum_keyed_maps/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_SIMDUTF', 'Macro', 'api/macros/json_use_simdutf/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Macros', 'Macro', 'api/macros/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE', 'Macro', 'api/macros/nlohmann_json_namespace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_BEGIN', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_END', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_NO_VERSION', 'Macro', 'api/macros/nlohmann_json_namespace_no_version/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM', 'Macro', 'api/macros/nlohmann_json_serialize_enum/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM_STRICT', 'Macro', 'api/macros/nlohmann_json_serialize_enum_strict/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MAJOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MINOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_PATCH', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
+532
View File
@@ -0,0 +1,532 @@
#!/usr/bin/env python3
"""Generate the Dash docset search index from the mkdocs sources."""
import argparse
import glob
import hashlib
import html
import os
import re
import shutil
import sqlite3
import sys
import tarfile
import urllib.parse
import urllib.request
import yaml
HERE = os.path.dirname(os.path.abspath(__file__))
MKDOCS_YML = os.path.join(HERE, '..', 'mkdocs', 'mkdocs.yml')
PAGES = os.path.join(HERE, '..', 'mkdocs', 'docs')
# api pages whose (name, type) cannot be derived by the heuristics
OVERRIDES = {
}
DOCSET = 'JSON_for_Modern_C++.docset'
TITLE_SUFFIX = ' - JSON for Modern C++</title>'
# CSS rules appended to the stylesheet: hide navigation items and fix spacing
# hide the navigation (the documentation browser has its own); Material's class selectors would win over element
# selectors, hence the classes and !important
CSS_PATCH = (
'\n\n.md-header, .md-footer, .md-tabs, .md-sidebar--primary, .md-content__button { display: none !important; }'
'\n\n.md-sidebar--secondary, .md-main__inner { top: 0 !important; margin-top: 0 !important; }'
)
USER_AGENT = ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/124.0 Safari/537.36')
CONTENT_TYPE_EXT = {'image/svg+xml': '.svg', 'image/png': '.png', 'image/jpeg': '.jpg',
'image/gif': '.gif', 'image/webp': '.webp'}
# remote loads that are allowed to remain (URL -> reason)
ALLOWED_REMOTE = {
# Material only loads this polyfill if the browser has no ResizeObserver
'https://unpkg.com/resize-observer-polyfill': 'fallback for browsers without ResizeObserver',
}
problems = []
def problem(page, reason) -> None:
"""Record a problem; all problems are reported at the end."""
problems.append(f'generate_docset.py: {page}: {reason}')
class Loader(yaml.SafeLoader):
"""YAML loader that tolerates the custom tags used in mkdocs.yml."""
Loader.add_multi_constructor('', lambda loader, suffix, node: None)
def walk_nav(items, groups=()):
"""Yield (group titles, nav title or None, md path) for all nav leaves."""
for item in items:
if isinstance(item, str):
yield list(groups), None, item
elif isinstance(item, dict):
for title, value in item.items():
if isinstance(value, list):
yield from walk_nav(value, groups + (str(title),))
else:
yield list(groups), str(title), value
def page_path(md_path) -> str:
"""Map a markdown path to the HTML path of the rendered page."""
if md_path.endswith('/index.md'):
return md_path[:-len('index.md')] + 'index.html'
return md_path[:-len('.md')] + '/index.html'
def read_page(md_path) -> str:
"""Read a page (resolving a snippet include), return '' if it does not exist."""
try:
with open(os.path.join(PAGES, md_path), encoding='utf-8') as f:
text = f.read()
m = re.match(r'--8<-- "(.+)"\s*$', text)
if m:
with open(os.path.join(PAGES, m.group(1)), encoding='utf-8') as f:
text = f.read()
return text
except OSError:
return ''
def clean(text) -> str:
"""Strip tags and entities from a heading and normalize whitespace."""
text = text.replace('\\>', '\x00') # escaped '>' is not the end of a tag
text = html.unescape(re.sub(r'</?[a-zA-Z][^>]*>', '', text)).replace('\x00', '>')
return re.sub(r'\s+', ' ', text).strip()
def strip_fences(text) -> str:
"""Remove fenced code blocks (their lines may start with '# ')."""
return re.sub(r'^(```|~~~).*?^\1[^\n]*$', '', text, flags=re.M | re.S)
def get_h1(text):
"""Return the cleaned first H1 of a page or None."""
body = strip_fences(text)
m = re.search(r'^# (.+)$', body, flags=re.M)
if m:
return clean(m.group(1))
m = re.search(r'<h1>(.*?)</h1>', body, flags=re.S)
return clean(m.group(1)) if m else None
def split_top_level(text, seps=','):
"""Split at separators that are not nested in <>, () or []."""
parts, depth, current = [], 0, ''
for c in text:
if c in '<([':
depth += 1
elif c in '>)]':
depth -= 1
if c in seps and depth <= 0:
parts.append(current)
current = ''
else:
current += c
parts.append(current)
return [p.strip() for p in parts if p.strip()]
OPERATOR_SYMBOLS = ('<=>', '<<', '>>', '<=', '>=', '<', '>')
def api_names(h1) -> list:
"""Derive the entry names from the H1 of an api page."""
# hide the angle brackets of operators from the nesting detection
for i, sym in enumerate(OPERATOR_SYMBOLS):
h1 = h1.replace('operator' + sym, f'operator\x01{i}\x01')
names = []
for part in split_top_level(h1):
name = part.replace('\\', '').replace('nlohmann::', '')
name = re.sub(r'\x01(\d)\x01', lambda m: OPERATOR_SYMBOLS[int(m.group(1))], name)
# drop qualifiers like "operator<<(basic_json)", but keep "operator()"
if not name.endswith('operator()'):
name = re.sub(r'(?<=\w|[<>=!+\-*/\[\]])\([^()]*\)$', '', name)
if name not in names:
names.append(name)
return names
def first_cpp_block(text):
"""Return the first ```cpp block following the H1."""
m = re.search(r'^# .*?^```cpp\n(.*?)^```', text, flags=re.M | re.S)
return m.group(1) if m else None
def api_type(name, decl):
"""Determine the Dash entry type from name and declaration."""
parts = name.split('::')
last = parts[-1]
if name.startswith('operator""'):
return 'Literal'
if last.startswith('operator'):
return 'Operator'
if decl is None:
return None
if re.search(r'\benum\b', decl):
return 'Enum'
if re.search(r'^\s*(template\s*<.*>\s*)?(class|struct)\s+\w+\s*(final\b|[:{;<]|$)', decl, flags=re.M):
return 'Class'
if re.search(r'\busing\s+\w+\s*=', decl) or 'typedef' in decl:
return 'Type'
if len(parts) > 1 and last == parts[-2]:
return 'Constructor'
if last.startswith('~'):
return 'Method'
if re.search(r'\bstatic\b', decl) or len(parts) == 1 or parts[0] == 'std':
return 'Function'
return 'Method'
def macro_names(h1) -> list:
"""Split the H1 of a macro page into macro names."""
return [n.strip() for n in re.split(r'[,/]', h1) if n.strip()]
def api_entries(md_path):
"""Return the (name, type) pairs of an api page."""
if md_path in OVERRIDES:
return OVERRIDES[md_path]
if md_path == 'api/macros/index.md':
return [('Macros', 'Macro')]
text = read_page(md_path)
h1 = get_h1(text)
if not h1:
problem(md_path, 'no H1 found')
return []
if md_path.startswith('api/macros/'):
return [(n, 'Macro') for n in macro_names(h1)]
names = api_names(h1)
if not names:
problem(md_path, 'no names found')
return []
decl = first_cpp_block(text)
result = []
for name in names:
kind = api_type(name, decl)
if kind is None:
problem(md_path, f'no type determinable for {name}')
else:
result.append((name, kind))
return result
def guide_entries(nav):
"""Yield (name, type, md path) for all non-api nav pages."""
for groups, title, md_path in walk_nav(nav):
if md_path.startswith('api/') or md_path == 'index.md':
continue
if not md_path.endswith('.md'):
continue
groups = groups[1:] # drop the top-level tab
if title is None:
title = get_h1(read_page(md_path))
if not title:
problem(md_path, 'no title found')
continue
# "Parsing: Parsing Untrusted Input" -> "Parsing: Untrusted Input"
if groups and title.startswith(groups[-1] + ' ') and title[len(groups[-1]) + 1:][:1].isupper():
title = title[len(groups[-1]) + 1:]
if md_path.endswith('/index.md') and groups and title == groups[-1]:
name = ': '.join(groups)
else:
name = ': '.join(groups + [title])
yield name, 'Guide', md_path
def build_entries() -> list:
"""Return the sorted list of (name, type, path) index entries."""
nav = load_mkdocs_yml()['nav']
entries = set()
for name, kind, md_path in guide_entries(nav):
entries.add((name, kind, page_path(md_path)))
api_root = os.path.join(PAGES, 'api')
on_disk = set()
for root, _, files in os.walk(api_root):
for file in files:
if file.endswith('.md'):
rel = os.path.relpath(os.path.join(root, file), PAGES)
on_disk.add(rel.replace(os.sep, '/'))
for _, _, md_path in walk_nav(nav):
if md_path.startswith('api/') and md_path not in on_disk:
problem(md_path, 'listed in nav but missing on disk')
for md_path in sorted(on_disk):
for name, kind in api_entries(md_path):
entries.add((name, kind, page_path(md_path)))
return sorted(entries)
def write_index(entries, out) -> None:
"""Write the entries into a SQLite search index."""
if os.path.exists(out):
os.remove(out)
con = sqlite3.connect(out)
con.execute('CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT)')
con.execute('CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path)')
con.executemany('INSERT INTO searchIndex(name, type, path) VALUES (?, ?, ?)', entries)
con.commit()
con.close()
def html_files(root):
"""Yield all HTML files below root."""
for base, _, files in os.walk(root):
for file in files:
if file.endswith('.html'):
yield os.path.join(base, file)
def read(path) -> str:
with open(path, encoding='utf-8') as f:
return f.read()
def write(path, text) -> None:
with open(path, 'w', encoding='utf-8') as f:
f.write(text)
def remove_source_widget(docs) -> None:
"""Drop data-md-component=source so Material does not query api.github.com for the stars and version."""
pattern = re.compile(r'\sdata-md-component=(?:"source"|source\b)')
for path in html_files(docs):
text = read(path)
new = pattern.sub('', text)
if new != text:
write(path, new)
def patch_titles(docs, entries) -> None:
"""Strip the site name from all titles; use the index names where available."""
names = {}
for name, _, path in entries:
names.setdefault(path, []).append(name)
for file in html_files(docs):
rel = os.path.relpath(file, docs).replace(os.sep, '/')
text = read(file).replace(TITLE_SUFFIX, '</title>')
if rel in names:
title = html.escape(', '.join(names[rel]), quote=False)
text = re.sub(r'<title>.*?</title>', lambda _: f'<title>{title}</title>', text, count=1, flags=re.S)
write(file, text)
IMG_RE = re.compile(r'<img\b[^>]*>', re.I)
ATTR_RE = r'''(?:{0})\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))'''
def attr(tag, name):
"""Return the value of an attribute in a tag or None."""
m = re.search(r'(?<![\w-])' + ATTR_RE.format(name), tag, flags=re.I)
return next(g for g in m.groups() if g is not None) if m else None
def is_remote(url) -> bool:
return re.match(r'(https?:)?//', url.strip(), flags=re.I) is not None
def download(url, docs) -> str:
"""Download url into assets/external and return the path relative to docs."""
u = urllib.parse.urlparse(url if not url.startswith('//') else 'https:' + url)
if u.scheme.lower() not in ('http', 'https'):
raise ValueError(f'not an http(s) URL: {url}')
req = urllib.request.Request(u.geturl(), headers={'User-Agent': USER_AGENT})
# (the scheme is checked above)
with urllib.request.urlopen(req, timeout=20) as r: # nosec B310
data = r.read()
ctype = r.headers.get_content_type()
path = urllib.parse.unquote(u.path).lstrip('/')
ext = os.path.splitext(path)[1]
if u.query or not ext or path.endswith('/'):
digest = hashlib.sha1(url.encode(), usedforsecurity=False).hexdigest()[:12]
path = os.path.join(os.path.dirname(path), digest + CONTENT_TYPE_EXT.get(ctype, ext or '.bin'))
rel = os.path.normpath(os.path.join('assets', 'external', u.hostname, path))
out = os.path.join(docs, rel)
os.makedirs(os.path.dirname(out), exist_ok=True)
with open(out, 'wb') as f:
f.write(data)
return rel.replace(os.sep, '/')
def localize_images(docs) -> None:
"""Download remote images and rewrite their src; drop them on failure."""
cache = {}
for file in html_files(docs):
text = read(file)
def repl(m):
tag = m.group(0)
src = attr(tag, 'src')
if src is None or not is_remote(src):
return tag
if src not in cache:
try:
cache[src] = download(src, docs)
except Exception as e: # noqa: BLE001
print(f'generate_docset.py: warning: cannot download {src}: {e}', file=sys.stderr)
cache[src] = None
if cache[src] is None:
return attr(tag, 'alt') or ''
local = os.path.relpath(os.path.join(docs, cache[src]), os.path.dirname(file))
local = local.replace(os.sep, '/')
return re.sub(ATTR_RE.format('src'), lambda _: f'src="{local}"', tag, count=1, flags=re.I)
new = IMG_RE.sub(repl, text)
if new != text:
write(file, new)
def load_mkdocs_yml() -> dict:
"""Load mkdocs.yml, ignoring tags like !ENV and !!python/name."""
with open(MKDOCS_YML, encoding='utf-8') as f:
# (Loader is a yaml.SafeLoader)
return yaml.load(f, Loader=Loader) # nosec B506
def localize_site_urls(docs, site_url) -> None:
"""Load assets that the theme's JavaScript references by absolute site URL from the docset.
The privacy plugin rewrites the mermaid loader to "<site_url>assets/external/unpkg.com/mermaid@11/...", so the
docset would fetch mermaid from the live site. __md_scope is the site root that Material defines in every page.
"""
pattern = re.compile(r'"' + re.escape(site_url) + r'(assets/[^"]*)"')
for base, _, files in os.walk(docs):
for file in (f for f in files if f.endswith('.js')):
path = os.path.join(base, file)
text = read(path)
new = pattern.sub(r'new URL("\1",__md_scope).href', text)
if new != text:
write(path, new)
def remote_loads(docs, site_url) -> list:
"""Return 'file: url' strings for resources that would be loaded remotely."""
found = []
cdn = re.compile(r'https://(?:unpkg\.com|cdn\.jsdelivr\.net|cdnjs\.cloudflare\.com|'
r'fonts\.googleapis\.com|fonts\.gstatic\.com)/[^\s"\'`)\\]*')
css_url = re.compile(r'url\(\s*["\']?((?:https?:)?//[^)"\']+)', re.I)
css_import = re.compile(r'@import\s+(?:url\(\s*)?["\']?((?:https?:)?//[^)"\'; ]+)', re.I)
for base, _, files in os.walk(docs):
for file in files:
path = os.path.join(base, file)
rel = os.path.relpath(path, docs)
urls = []
if file.endswith('.html'):
text = read(path)
for m in re.finditer(r'<[a-zA-Z][^>]*>', text):
tag = m.group(0)
for name in ('src', 'poster'):
v = attr(tag, name)
if v and is_remote(v):
urls.append(v)
v = attr(tag, 'srcset')
if v:
urls += [c.split()[0] for c in v.split(',') if c.strip() and is_remote(c.strip())]
if re.match(r'<link\b', tag, flags=re.I):
rel_attr = (attr(tag, 'rel') or '').lower()
v = attr(tag, 'href')
if v and is_remote(v) and re.search(r'stylesheet|icon|preload|modulepreload|manifest', rel_attr):
urls.append(v)
urls += css_url.findall(text) + css_import.findall(text)
elif file.endswith('.css'):
text = read(path)
urls += css_url.findall(text) + css_import.findall(text)
elif file.endswith('.js'):
text = read(path)
urls += cdn.findall(text)
urls += re.findall(re.escape(site_url) + r'assets/[^\s"\'`)\\]*', text)
found += [f'{rel}: {u}' for u in urls if u not in ALLOWED_REMOTE]
return found
def make_docset(site, out_dir) -> int:
entries = build_entries()
if problems:
print('\n'.join(problems), file=sys.stderr)
return 1
docset = os.path.join(out_dir, DOCSET)
docs = os.path.join(docset, 'Contents', 'Resources', 'Documents')
if os.path.exists(docset):
shutil.rmtree(docset)
shutil.copytree(site, docs)
for icon in ('icon.png', 'icon@2x.png'):
shutil.copy(os.path.join(HERE, icon), docset)
shutil.copy(os.path.join(HERE, 'Info.plist'), os.path.join(docset, 'Contents'))
write_index(entries, os.path.join(docset, 'Contents', 'Resources', 'docSet.dsidx'))
# patch CSS to hide navigation items and fix spacing
css = glob.glob(os.path.join(docs, 'assets', 'stylesheets', 'main.*.min.css'))
if len(css) != 1:
print(f'generate_docset.py: expected exactly one main.*.min.css, found {len(css)}', file=sys.stderr)
return 1
with open(css[0], 'a', encoding='utf-8') as f:
f.write(CSS_PATCH)
patch_titles(docs, entries)
remove_source_widget(docs)
for sitemap in glob.glob(os.path.join(docs, 'sitemap.*')):
os.remove(sitemap)
# make the docset self-contained
site_url = load_mkdocs_yml()['site_url']
localize_images(docs)
localize_site_urls(docs, site_url)
remote = remote_loads(docs, site_url)
if remote:
print('generate_docset.py: remote resources remain in the docset:', file=sys.stderr)
print('\n'.join(' ' + r for r in remote), file=sys.stderr)
return 1
return 0
def make_tgz(out_dir) -> int:
docset = os.path.join(out_dir, DOCSET)
if not os.path.isdir(docset):
print(f'generate_docset.py: {docset} does not exist', file=sys.stderr)
return 1
with tarfile.open(os.path.join(out_dir, 'JSON_for_Modern_C++.tgz'), 'w:gz') as tar:
tar.add(docset, arcname=DOCSET, filter=lambda i: None if os.path.basename(i.name) == '.DS_Store' else i)
return 0
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest='command', required=True)
sub.add_parser('list', help='print the index entries as TSV')
p = sub.add_parser('index', help='write the SQLite search index')
p.add_argument('out')
p = sub.add_parser('docset', help='build the docset from a built mkdocs site')
p.add_argument('site_dir')
p.add_argument('out_dir')
p = sub.add_parser('tgz', help='pack the docset into a tarball')
p.add_argument('out_dir')
args = parser.parse_args()
if args.command == 'docset':
return make_docset(args.site_dir, args.out_dir)
if args.command == 'tgz':
return make_tgz(args.out_dir)
entries = build_entries()
if problems:
print('\n'.join(problems), file=sys.stderr)
return 1
if args.command == 'list':
for entry in entries:
print('\t'.join(entry))
elif args.command == 'index':
write_index(entries, args.out)
return 0
if __name__ == '__main__':
sys.exit(main())
+1
View File
@@ -17,6 +17,7 @@ build: style_check
style_check: style_check:
@cd docs ; ../venv/bin/python3 ../scripts/check_structure.py @cd docs ; ../venv/bin/python3 ../scripts/check_structure.py
@venv/bin/python3 ../docset/generate_docset.py list > /dev/null
# check that all Mermaid diagrams parse (needs Node.js) # check that all Mermaid diagrams parse (needs Node.js)
# This target is used in the CI (ci_test_documentation_mermaid). # This target is used in the CI (ci_test_documentation_mermaid).
+3 -1
View File
@@ -241,4 +241,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as 3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`operator[]`](operator[].md), [`value`](value.md), [`find`](find.md), and other lookup already supported by [`operator[]`](operator[].md), [`value`](value.md), [`find`](find.md), and other lookup
functions. functions.
4. Added in version 2.0.0. 4. Added in version 2.0.0. Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is not
a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
@@ -10,6 +10,10 @@ bool contains(KeyType&& key) const;
// (3) // (3)
bool contains(const json_pointer& ptr) const; bool contains(const json_pointer& ptr) const;
// (4)
template<typename T>
bool contains(T) const = delete;
``` ```
1. Check whether an element exists in a JSON object with a key equivalent to `key`. If the element is not found or the 1. Check whether an element exists in a JSON object with a key equivalent to `key`. If the element is not found or the
@@ -17,6 +21,9 @@ bool contains(const json_pointer& ptr) const;
2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and 2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and
`#!cpp typename object_comparator_t::is_transparent` denotes a type. `#!cpp typename object_comparator_t::is_transparent` denotes a type.
3. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value. 3. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value.
4. Deleted: this overload is only available if `T` is an integral type and is declared as deleted, so that a call with
an integer `key` (for example, `#!cpp j.contains(0)`) fails to compile. Otherwise, the integer literal `0` would convert to
a null `#!cpp const char*` and, from there, to the key type, causing undefined behavior at runtime.
## Template parameters ## Template parameters
@@ -39,6 +46,7 @@ bool contains(const json_pointer& ptr) const;
is not an object, `#!cpp false` is returned. is not an object, `#!cpp false` is returned.
2. See 1. 2. See 1.
3. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise. 3. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise.
4. Deleted; a call with an integral argument does not compile.
## Exception safety ## Exception safety
@@ -49,6 +57,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
1. The function does not throw exceptions. 1. The function does not throw exceptions.
2. The function does not throw exceptions. 2. The function does not throw exceptions.
3. The function does not throw exceptions. 3. The function does not throw exceptions.
4. Deleted; a call with an integral argument does not compile.
## Complexity ## Complexity
+7
View File
@@ -7,12 +7,19 @@ size_type count(const typename object_t::key_type& key) const;
// (2) // (2)
template<typename KeyType> template<typename KeyType>
size_type count(KeyType&& key) const; size_type count(KeyType&& key) const;
// (3)
template<typename T>
size_type count(T) const = delete;
``` ```
1. Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will 1. Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will
always be `0` (`key` was not found) or `1` (`key` was found). always be `0` (`key` was not found) or `1` (`key` was found).
2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and 2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and
`#!cpp typename object_comparator_t::is_transparent` denotes a type. `#!cpp typename object_comparator_t::is_transparent` denotes a type.
3. Deleted: this overload is only available if `T` is an integral type and is declared as deleted, so that a call with
an integer `key` (for example, `#!cpp j.count(0)`) fails to compile. Otherwise, the integer literal `0` would convert to
a null `#!cpp const char*` and, from there, to the key type, causing undefined behavior at runtime.
## Template parameters ## Template parameters
@@ -37,8 +37,8 @@ ignore
: ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped : ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped
keep keep
: keep invalid UTF-8 sequences unchanged; only meaningful for the binary formats mentioned above, since [`dump`] : keep invalid UTF-8 sequences unchanged; only meaningful for the binary formats mentioned above, since
(dump.md) itself must produce text, and `keep` there writes the ill-formed bytes to the output as is, so the [`dump`](dump.md) itself must produce text, and `keep` there writes the ill-formed bytes to the output as is, so the
result is then not valid UTF-8 (but still equals the input bytes exactly, including around any well-formed result is then not valid UTF-8 (but still equals the input bytes exactly, including around any well-formed
characters, which are still escaped as usual) characters, which are still escaped as usual)
+9
View File
@@ -10,12 +10,21 @@ template<typename KeyType>
iterator find(KeyType&& key); iterator find(KeyType&& key);
template<typename KeyType> template<typename KeyType>
const_iterator find(KeyType&& key) const; const_iterator find(KeyType&& key) const;
// (3)
template<typename T>
iterator find(T) = delete;
template<typename T>
const_iterator find(T) const = delete;
``` ```
1. Finds an element in a JSON object with a key equivalent to `key`. If the element is not found or the 1. Finds an element in a JSON object with a key equivalent to `key`. If the element is not found or the
JSON value is not an object, `end()` is returned. JSON value is not an object, `end()` is returned.
2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and 2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and
`#!cpp typename object_comparator_t::is_transparent` denotes a type. `#!cpp typename object_comparator_t::is_transparent` denotes a type.
3. Deleted: this overload is only available if `T` is an integral type and is declared as deleted, so that a call with
an integer `key` (for example, `#!cpp j.find(0)`) fails to compile. Otherwise, the integer literal `0` would convert to
a null `#!cpp const char*` and, from there, to the key type, causing undefined behavior at runtime.
## Template parameters ## Template parameters
@@ -124,7 +124,8 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_bjdata` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_bjdata` with a pointer and a length as first two parameters, which has been
deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.13.0. In version 4.0.0, this overload
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_bjdata(ptr, len, ...);` with `#!cpp from_bjdata(ptr, ptr+len, ...);`. `#!cpp from_bjdata(ptr, len, ...);` with `#!cpp from_bjdata(ptr, ptr+len, ...);`.
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
+2 -1
View File
@@ -110,7 +110,8 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_bon8` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_bon8` with a pointer and a length as first two parameters, which has been
deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.13.0. In version 4.0.0, this overload
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_bon8(ptr, len, ...);` with `#!cpp from_bon8(ptr, ptr+len, ...);`. `#!cpp from_bon8(ptr, len, ...);` with `#!cpp from_bon8(ptr, ptr+len, ...);`.
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
+2 -1
View File
@@ -126,7 +126,8 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_bson` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_bson` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. In version 4.0.0, this overload
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_bson(ptr, len, ...);` with `#!cpp from_bson(ptr, ptr+len, ...);`. `#!cpp from_bson(ptr, len, ...);` with `#!cpp from_bson(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_bson` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_bson` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
+2 -1
View File
@@ -135,7 +135,8 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_cbor` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_cbor` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. In version 4.0.0, this overload
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_cbor(ptr, len, ...);` with `#!cpp from_cbor(ptr, ptr+len, ...);`. `#!cpp from_cbor(ptr, len, ...);` with `#!cpp from_cbor(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_cbor` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_cbor` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
@@ -127,7 +127,8 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_msgpack` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_msgpack` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. In version 4.0.0, this overload
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_msgpack(ptr, len, ...);` with `#!cpp from_msgpack(ptr, ptr+len, ...);`. `#!cpp from_msgpack(ptr, len, ...);` with `#!cpp from_msgpack(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_msgpack` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_msgpack` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
@@ -125,7 +125,8 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_ubjson` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_ubjson` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. In version 4.0.0, this overload
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_ubjson(ptr, len, ...);` with `#!cpp from_ubjson(ptr, ptr+len, ...);`. `#!cpp from_ubjson(ptr, len, ...);` with `#!cpp from_ubjson(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_ubjson` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_ubjson` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
@@ -62,5 +62,5 @@ same name. Hidden members remain accessible via [`as_base_class`](as_base_class.
## Version history ## Version history
- Added in version 3.12.0. - Added in version 3.11.3.
- Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class. - Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class.
@@ -23,9 +23,10 @@ type to use.
## Template parameters ## Template parameters
`NumberFloatType` `NumberFloatType`
: the type to store floating-point numbers. Parsing and serialization are implemented in terms of : the type to store floating-point numbers. The type must be `#!cpp float`, `#!cpp double`, or
`#!cpp std::strtof`/`#!cpp std::strtod`/`#!cpp std::strtold` and `#!cpp std::snprintf`, so the type must be `#!cpp long double`. The parser converts `#!cpp float`, `#!cpp double`, and a `#!cpp long double` that is IEEE 754
`#!cpp float`, `#!cpp double`, or `#!cpp long double`. The binary64 itself. It converts other `#!cpp long double` formats with `#!cpp std::from_chars` where available, or
with `#!cpp std::strtold` otherwise. Serialization falls back to `#!cpp std::snprintf`. The
[binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`, [binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`,
because they have no encoding for `#!cpp long double`. See because they have no encoding for `#!cpp long double`. See
[Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype). [Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype).
+1 -2
View File
@@ -26,8 +26,7 @@ To store objects in C++, a type is defined by the template parameters described
`StringType` `StringType`
: the type of the keys or names (e.g., `std::string`). The comparison function `std::less<StringType>` is used to : the type of the keys or names (e.g., `std::string`). The comparison function `std::less<StringType>` is used to
order elements inside the container. `object_t::key_type` must be implicitly convertible to `string_t` (required by the order elements inside the container.
binary formats).
`AllocatorType` `AllocatorType`
: the allocator to use for objects (e.g., `std::allocator`) : the allocator to use for objects (e.g., `std::allocator`)
@@ -286,4 +286,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as 3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions. already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since 4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since
version 3.13.0. version 3.13.0. Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is not
a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
+3
View File
@@ -112,3 +112,6 @@ is thrown. In any case, the original value is not changed: the patch is applied
location has a non-object/non-array parent in version 3.13.0. location has a non-object/non-array parent in version 3.13.0.
- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper - Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper
prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0. prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0.
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
@@ -110,3 +110,6 @@ function throws an exception.
location has a non-object/non-array parent in version 3.13.0. location has a non-object/non-array parent in version 3.13.0.
- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper - Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper
prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0. prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0.
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
@@ -41,6 +41,8 @@ With (2), the bytes written before the exception remain in the output adapter.
above 9223372036854775807, which BON8 cannot represent above 9223372036854775807, which BON8 cannot represent
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not
valid UTF-8 valid UTF-8
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
discarded; example: `"cannot serialize discarded value to BON8"`
## Complexity ## Complexity
+6 -2
View File
@@ -36,8 +36,9 @@ The function can throw the following exceptions:
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a
number; example: `"array index 'one' is not a number"` number; example: `"array index 'one' is not a number"`
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array - Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array
(because one of its keys is `0`) and another key at that level cannot be an array index; example: (because one of its keys is `0`) and another key at that level begins with a digit but is not a valid array index
`"unresolved reference token 'x'"` (such as `1a`), or is `-`; example:
`"unresolved reference token '-'"`
## Complexity ## Complexity
@@ -80,3 +81,6 @@ Apart from these two cases, for a JSON value `j`, the following is always true:
- Added in version 2.0.0. - Added in version 2.0.0.
- Made the array/object decision independent of the object's iteration order in version 3.13.0. - Made the array/object decision independent of the object's iteration order in version 3.13.0.
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
+11 -1
View File
@@ -119,6 +119,14 @@ changes to any JSON value.
## Notes ## Notes
!!! warning "`null` members are not missing"
The default value is used only if the key (or JSON Pointer) does not exist. A member that exists but is
`#!json null` is converted like any other value, so `#!cpp j.value("k", 0)` throws a
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `"k"` is `#!json null`. See
[Access with default value](../../features/element_access/default_value.md)
for alternatives.
!!! warning "Return type" !!! warning "Return type"
The value function is a template, and the return type of the function is determined by the type of the provided The value function is a template, and the return type of the function is determined by the type of the provided
@@ -227,4 +235,6 @@ changes to any JSON value.
[`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), and other lookup functions. [`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), and other lookup functions.
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving 3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or `ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
`default_value`, as documented). `default_value`, as documented). Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109)
instead of returning `default_value` for a one-character array index that is not a digit (e.g., `/x`) in version
3.13.0, as it already did for longer ones.
+1
View File
@@ -27,6 +27,7 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_HAS_CPP_11**<br>**JSON_HAS_CPP_14**<br>**JSON_HAS_CPP_17**<br>**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard - [**JSON_HAS_CPP_11**<br>**JSON_HAS_CPP_14**<br>**JSON_HAS_CPP_17**<br>**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard
- [**JSON_HAS_FILESYSTEM**<br>**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support - [**JSON_HAS_FILESYSTEM**<br>**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support
- [**JSON_HAS_RANGE_VIEW_CONVERSION**](json_has_range_view_conversion.md) - control construction from `std::ranges` views
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support - [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
- [**JSON_HAS_STATIC_RTTI**](json_has_static_rtti.md) - control RTTI (run time type information) support - [**JSON_HAS_STATIC_RTTI**](json_has_static_rtti.md) - control RTTI (run time type information) support
- [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support - [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support
@@ -49,6 +49,6 @@ The default value is detected based on preprocessor macros such as `#!cpp __cplu
## Version history ## Version history
- Added in version 3.10.5. - Added in version 3.10.0.
- Added `JSON_HAS_CPP_23` in version 3.12.0. - Added `JSON_HAS_CPP_23` in version 3.12.0.
- Added `JSON_HAS_CPP_26` in version 3.13.0. - Added `JSON_HAS_CPP_26` in version 3.13.0.
@@ -0,0 +1,43 @@
# JSON_HAS_RANGE_VIEW_CONVERSION
```cpp
#define JSON_HAS_RANGE_VIEW_CONVERSION /* value */
```
This macro indicates whether a JSON array can be constructed directly from a C++20 range view (`std::ranges::view`),
such as the result of `std::views::filter` or `std::views::transform`. Possible values are `1` when supported or `0`
when unsupported.
## Default definition
The default value is `1` if [`JSON_HAS_RANGES`](json_has_ranges.md) is `1` and the compiler is not MinGW (that is,
`#!cpp __MINGW32__` is not defined), and `0` otherwise.
When the macro is not defined, the library will define it to its default value.
!!! info "Known compiler/stdlib exclusions"
- **MinGW** -- disabled, because its `std::ranges` support is incomplete ([issue #4916](https://github.com/nlohmann/json/issues/4916)).
- All toolchains for which [`JSON_HAS_RANGES`](json_has_ranges.md#default-definition) is disabled.
## Examples
??? example
The code below forces the library to disable the conversion from range views:
```cpp
#define JSON_HAS_RANGE_VIEW_CONVERSION 0
#include <nlohmann/json.hpp>
...
```
## See also
- [JSON_HAS_RANGES](json_has_ranges.md) - control `std::ranges` support
- [Constructing from a C++20 range view](../../features/conversions.md#putting-values-in) - usage of the feature
## Version history
- Added in version 3.13.0.
@@ -20,7 +20,7 @@ When the macro is not defined, the library will define it to its default value.
- **GCC 11.1.0** — disabled (the shipped `<ranges>` header has a syntax error; [issue #4440](https://github.com/nlohmann/json/issues/4440)) - **GCC 11.1.0** — disabled (the shipped `<ranges>` header has a syntax error; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440)) - **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440)) - **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #5161](https://github.com/nlohmann/json/issues/5161))
- **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440)) - **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **nvcc (CUDA) 12.0.x and 12.1.x** — disabled (the `enable_borrowed_range` variable-template syntax triggers a parse error - **nvcc (CUDA) 12.0.x and 12.1.x** — disabled (the `enable_borrowed_range` variable-template syntax triggers a parse error
under these two toolkit versions; fixed in CUDA 12.2; [issue #3907](https://github.com/nlohmann/json/issues/3907)) under these two toolkit versions; fixed in CUDA 12.2; [issue #3907](https://github.com/nlohmann/json/issues/3907))
@@ -44,9 +44,12 @@ When the macro is not defined, the library will define it to its default value.
- [JSON_HAS_CPP_11 / JSON_HAS_CPP_14 / JSON_HAS_CPP_17 / JSON_HAS_CPP_20 / JSON_HAS_CPP_23 / - [JSON_HAS_CPP_11 / JSON_HAS_CPP_14 / JSON_HAS_CPP_17 / JSON_HAS_CPP_20 / JSON_HAS_CPP_23 /
JSON_HAS_CPP_26](json_has_cpp_11.md) - set supported C++ standard JSON_HAS_CPP_26](json_has_cpp_11.md) - set supported C++ standard
- [JSON_HAS_RANGE_VIEW_CONVERSION](json_has_range_view_conversion.md) - control construction from `std::ranges` views
- [JSON_HAS_STD_FORMAT](json_has_std_format.md) - a similar feature-detection macro, for `std::format`/`std::formatter` - [JSON_HAS_STD_FORMAT](json_has_std_format.md) - a similar feature-detection macro, for `std::format`/`std::formatter`
support support
## Version history ## Version history
- Added in version 3.11.0. - Added in version 3.11.0.
- Added the exclusions for libstdc++ < 11, Clang < 16 with libstdc++, libc++ < 16, and nvcc (CUDA) 12.0.x and 12.1.x in
version 3.13.0. Before, only GCC 11.1.0 was excluded.
@@ -20,7 +20,10 @@ The macro does not affect:
- The binary readers ([`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md), - The binary readers ([`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md),
[`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md), [`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md),
[`from_bson`](../basic_json/from_bson.md)): none of these formats requires a decoder to reject ill-formed UTF-8, so [`from_bson`](../basic_json/from_bson.md)): none of these formats requires a decoder to reject ill-formed UTF-8, so
they always return the bytes unchanged. by default they return the bytes unchanged (`error_handler_t::keep`), independent of this macro. Pass an
`error_handler` argument explicitly to validate or sanitize the strings they read.
- [`from_bon8`](../basic_json/from_bon8.md): BON8 always validates, because the UTF-8 lead bytes mark where a string
ends.
## Default definition ## Default definition
@@ -190,3 +190,5 @@ void to_json(BasicJsonType& j, const B& b) {
4. Added in version 3.12.0. 4. Added in version 3.12.0.
5. Added in version 3.12.0. 5. Added in version 3.12.0.
6. Added in version 3.12.0. 6. Added in version 3.12.0.
All six macros were changed to allow an empty member list in version 3.13.0.
@@ -186,3 +186,6 @@ See the examples below for the concrete generated code.
1. Added in version 3.9.0. 1. Added in version 3.9.0.
2. Added in version 3.11.0. 2. Added in version 3.11.0.
3. Added in version 3.11.3. 3. Added in version 3.11.3.
All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0,
and to allow an empty member list in version 3.13.0.
@@ -185,3 +185,6 @@ See the examples below for the concrete generated code.
1. Added in version 3.9.0. 1. Added in version 3.9.0.
2. Added in version 3.11.0. 2. Added in version 3.11.0.
3. Added in version 3.11.3. 3. Added in version 3.11.3.
All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0,
and to allow an empty member list in version 3.13.0.
@@ -38,4 +38,6 @@ the library.
## Version history ## Version history
- Added in version 3.11.0. Changed inline namespace name in version 3.11.2. - Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in
version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, and `_ekmo` in version 3.13.0; see
[`nlohmann` Namespace](../../features/namespace.md#structure).
@@ -14,7 +14,7 @@ These macros can be used to open and close the `nlohmann` namespace. See
## Default definition ## Default definition
The default definitions open and close the `nlohmann` namespace. The precise definition of The default definitions open and close the `nlohmann` namespace. The precise definition of
[`NLOHMANN_JSON_NAMESPACE_BEGIN`] varies as described [here](../../features/namespace.md#structure). `NLOHMANN_JSON_NAMESPACE_BEGIN` varies as described [here](../../features/namespace.md#structure).
1. Default definition of `NLOHMANN_JSON_NAMESPACE_BEGIN`: 1. Default definition of `NLOHMANN_JSON_NAMESPACE_BEGIN`:
+1 -1
View File
@@ -10,7 +10,7 @@ The library parses, stores, and serializes JSON values in memory. It does not op
files (it only reads from streams or `std::FILE*` handles that the caller has already opened), does not read environment files (it only reads from streams or `std::FILE*` handles that the caller has already opened), does not read environment
variables, and does not implement cryptography or handle credentials. variables, and does not implement cryptography or handle credentials.
The primary threat is therefore **untrusted input**: JSON text or binary data (BJData, BSON, CBOR, MessagePack, UBJSON) The primary threat is therefore **untrusted input**: JSON text or binary data (BJData, BON8, BSON, CBOR, MessagePack, UBJSON)
that an attacker controls, passed to [`parse`](../api/basic_json/parse.md), [`accept`](../api/basic_json/accept.md), that an attacker controls, passed to [`parse`](../api/basic_json/parse.md), [`accept`](../api/basic_json/accept.md),
[`sax_parse`](../api/basic_json/sax_parse.md), or one of the `from_*` functions such as [`sax_parse`](../api/basic_json/sax_parse.md), or one of the `from_*` functions such as
[`from_cbor`](../api/basic_json/from_cbor.md). Such input may try to [`from_cbor`](../api/basic_json/from_cbor.md). Such input may try to
@@ -12,7 +12,7 @@ violations will result in a failed build.
Note: C++20 modules support may hit compiler-specific issues not covered by the general compiler matrix below. See [Modules](../features/modules.md#known-issues) for known issues and workarounds. Note: C++20 modules support may hit compiler-specific issues not covered by the general compiler matrix below. See [Modules](../features/modules.md#known-issues) for known issues and workarounds.
Note: Some modern features (like C++20 ranges or filesystem support) may be disabled on specific broken or incomplete toolchains even when standard feature-test macros indicate support. See [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) and [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) for details on known exclusions. Note: Some modern features (like C++20 ranges or filesystem support) may be disabled on specific broken or incomplete toolchains even when standard feature-test macros indicate support. See [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md), [`JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md), and [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) for details on known exclusions.
- [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms, - [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms,
including the oldest versions known to compile the library. including the oldest versions known to compile the library.
@@ -107,7 +107,7 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
- [x] The library is compiled with all C++ language revisions (C++11, C++14, C++17, C++20, C++23, and C++26) to detect - [x] The library is compiled with all C++ language revisions (C++11, C++14, C++17, C++20, C++23, and C++26) to detect
and fix language deprecations early. and fix language deprecations early.
- [x] The library is checked for compiler warnings: - [x] The library is checked for compiler warnings:
- On Clang, `-Weverything` is used with 8 exceptions. - On Clang, `-Weverything` is used with 7 exceptions.
??? abstract "Clang warnings" ??? abstract "Clang warnings"
@@ -347,6 +347,13 @@ struct adl_serializer<boost::optional<T>> {
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
``` ```
!!! tip "`std::optional` needs no serializer"
Since version 3.12.0, `std::optional<T>` is supported out of the box when compiling with C++17
(`std::nullopt` is converted to and from `null`). Do not write an `adl_serializer` for it; this pattern is only
needed for types such as `boost::optional` or for custom semantics. See [Conversions](conversions.md) and
[Omitting a field when serializing `std::optional`](conversions.md#omitting-a-field-when-serializing-stdoptional).
!!! note "ABI compatibility" !!! note "ABI compatibility"
Use [`NLOHMANN_JSON_NAMESPACE_BEGIN`](../api/macros/nlohmann_json_namespace_begin.md) and `NLOHMANN_JSON_NAMESPACE_END` Use [`NLOHMANN_JSON_NAMESPACE_BEGIN`](../api/macros/nlohmann_json_namespace_begin.md) and `NLOHMANN_JSON_NAMESPACE_END`
+2 -1
View File
@@ -217,7 +217,8 @@ json j = numbers; // [1,2,3]
``` ```
This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due
to incomplete C++20 ranges support there. to incomplete C++20 ranges support there; see
[`JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md).
## Your own types ## Your own types
@@ -30,6 +30,47 @@ you want to access and a default value in case there is no value stored with tha
| `#!cpp j.value("append", false)` | `#!json true` | | `#!cpp j.value("append", false)` | `#!json true` |
| `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` | | `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` |
## Nested values
To read a value deep inside a document, pass a [JSON Pointer](../json_pointer.md) instead of a key. The default value is
returned if the value at the pointer does not exist, including the case that an intermediate key is missing. There is
no need to check each level with [`contains`](../../api/basic_json/contains.md) first.
```cpp
json j = {{"server", {{"port", 8080}}}, {"list", {10, 20}}};
int port = j.value("/server/port"_json_pointer, 80); // 8080
int timeout = j.value("/server/limits/timeout"_json_pointer, 30); // 30 (missing intermediate key)
int second = j.value("/list/1"_json_pointer, 0); // 20 (numeric tokens index arrays)
int third = j.value("/list/5"_json_pointer, 0); // 0 (index out of range)
bool has_port = j.contains("/server/port"_json_pointer); // true
bool has_host = j.contains("/server/host/name"_json_pointer); // false
```
If the path is only available as a dotted string such as `#!cpp "server.port"`, do not build the pointer by
concatenating `#!cpp "/"` and the parts: keys containing `/` or `~` would be misinterpreted. Append each part as a
reference token with [`operator/=`](../../api/json_pointer/operator_slasheq.md) instead. It escapes the token for you.
```cpp
json::json_pointer to_pointer(const std::string& dotted)
{
json::json_pointer ptr;
std::istringstream in(dotted);
for (std::string token; std::getline(in, token, '.');)
{
ptr /= token;
}
return ptr;
}
int port = j.value(to_pointer("server.port"), 80); // 8080
int first = j.value(to_pointer("list.0"), 0); // 10
```
The key `#!cpp "a/b"` yields the pointer `#!cpp "/a~1b"`; the dot-splitting itself is up to the caller, so keys
containing `.` need a different separator.
## Notes ## Notes
!!! failure "Exceptions" !!! failure "Exceptions"
@@ -37,6 +78,31 @@ you want to access and a default value in case there is no value stored with tha
- With string keys, `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown. - With string keys, `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
- With JSON Pointers, `value` can be used with both objects and arrays. For other types (null, boolean, number, string), a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown. - With JSON Pointers, `value` can be used with both objects and arrays. For other types (null, boolean, number, string), a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
!!! warning "`null` and mistyped members are not missing"
`value` returns the default value only if the key is **absent**. If the member exists, it is converted to the type
of the default value, even if it is `#!json null`. For `#!json {"k": null}`, the call `#!cpp j.value("k", 0)` throws
a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error302), and so does a member of another
type such as a string where a number is expected. The same holds for JSON Pointers.
To treat `#!json null` like a missing value, check for it explicitly:
```cpp
int n = (j.contains("k") && !j["k"].is_null()) ? j["k"].get<int>() : 0;
```
With C++17, [`get<std::optional<T>>()`](../../api/basic_json/get.md) maps `#!json null` to an empty optional. As
[`at`](../../api/basic_json/at.md) throws [`out_of_range`](../../home/exceptions.md#jsonexceptionout_of_range403)
for an absent key, use [`find`](../../api/basic_json/find.md) to cover both cases:
```cpp
std::optional<int> n; // empty if "k" is absent or null
if (const auto it = j.find("k"); it != j.end())
{
n = it->get<std::optional<int>>(); // still throws for a non-number such as "text"
}
```
!!! warning "Return type" !!! warning "Return type"
The value function is a template, and the return type of the function is determined by the type of the provided The value function is a template, and the return type of the function is determined by the type of the provided
@@ -63,3 +129,6 @@ you want to access and a default value in case there is no value stored with tha
- [`value`](../../api/basic_json/value.md) for access with default value - [`value`](../../api/basic_json/value.md) for access with default value
- documentation on [checked access](checked_access.md) - documentation on [checked access](checked_access.md)
- documentation on [JSON Pointer](../json_pointer.md)
- [`contains`](../../api/basic_json/contains.md) to check whether a key or JSON Pointer exists
- [`json_pointer::operator/=`](../../api/json_pointer/operator_slasheq.md) to build a pointer token by token
@@ -77,6 +77,10 @@ auto val2 = j.at(json::json_pointer("/nested/three/1")); // false
auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0 auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0
``` ```
To read a value with a fallback, use [`value`](../api/basic_json/value.md) with a JSON Pointer; to test for existence,
use [`contains`](../api/basic_json/contains.md). Neither needs intermediate checks, see
[Nested values](element_access/default_value.md#nested-values).
!!! note "Creating intermediate levels that don't exist" !!! note "Creating intermediate levels that don't exist"
See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is
@@ -126,6 +130,8 @@ auto j_original = j_flat.unflatten();
## See also ## See also
- Class [`json_pointer`](../api/json_pointer/index.md) - Class [`json_pointer`](../api/json_pointer/index.md)
- Functions [`value`](../api/basic_json/value.md), [`contains`](../api/basic_json/contains.md), and
[`at`](../api/basic_json/at.md) accept JSON Pointers; see [Nested values](element_access/default_value.md#nested-values)
- Function [`flatten`](../api/basic_json/flatten.md) - Function [`flatten`](../api/basic_json/flatten.md)
- Function [`unflatten`](../api/basic_json/unflatten.md) - Function [`unflatten`](../api/basic_json/unflatten.md)
- [JSON Patch](json_patch.md) - paths inside a patch are JSON Pointers - [JSON Patch](json_patch.md) - paths inside a patch are JSON Pointers
+39
View File
@@ -82,6 +82,45 @@ To override the built-in check, define `JSON_HAS_FILESYSTEM` or `JSON_HAS_EXPERI
See [full documentation of `JSON_HAS_FILESYSTEM` and `JSON_HAS_EXPERIMENTAL_FILESYSTEM`](../api/macros/json_has_filesystem.md). See [full documentation of `JSON_HAS_FILESYSTEM` and `JSON_HAS_EXPERIMENTAL_FILESYSTEM`](../api/macros/json_has_filesystem.md).
## `JSON_HAS_RANGES`
The library uses `std::ranges` (and concepts) where available, for example, to parse from C++20 ranges and to
construct JSON arrays from range views. The library detects whether the standard library supports ranges and
disables the support on toolchains with an incomplete implementation. To override the built-in check, define
`JSON_HAS_RANGES` to `1` or `0`.
See [full documentation of `JSON_HAS_RANGES`](../api/macros/json_has_ranges.md).
## `JSON_HAS_RANGE_VIEW_CONVERSION`
When `JSON_HAS_RANGES` is enabled (and the compiler is not MinGW), a JSON array can be constructed directly from a C++20
range view such as `std::views::filter(...)`. To override the built-in check, define `JSON_HAS_RANGE_VIEW_CONVERSION` to
`1` or `0`.
See [full documentation of `JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md).
## `JSON_HAS_STATIC_RTTI`
The library detects whether the compiler supports run time type information (RTTI), which it needs, for instance, to
exclude `std::any` from the candidate types of the implicit conversion on C++17. To override the built-in check, define
`JSON_HAS_STATIC_RTTI` to `1` or `0`.
See [full documentation of `JSON_HAS_STATIC_RTTI`](../api/macros/json_has_static_rtti.md).
## `JSON_HAS_STD_FORMAT`
When compiling with C++20 and a standard library that provides `<format>`, the library provides a `std::formatter`
specialization for JSON values. To override the built-in check, define `JSON_HAS_STD_FORMAT` to `1` or `0`.
See [full documentation of `JSON_HAS_STD_FORMAT`](../api/macros/json_has_std_format.md).
## `JSON_HAS_THREE_WAY_COMPARISON`
When the compiler and standard library support 3-way comparison (the spaceship operator `<=>`), the library provides it
for JSON values. To override the built-in check, define `JSON_HAS_THREE_WAY_COMPARISON` to `1` or `0`.
See [full documentation of `JSON_HAS_THREE_WAY_COMPARISON`](../api/macros/json_has_three_way_comparison.md).
## `JSON_NOEXCEPTION` ## `JSON_NOEXCEPTION`
Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`.
+8
View File
@@ -9,6 +9,13 @@ syntax that closely mimics the document being modified. Unlike [JSON Patch](json
express every kind of change (e.g., it cannot reorder array elements or remove a specific array element), but it is express every kind of change (e.g., it cannot reorder array elements or remove a specific array element), but it is
easier to read and write for object-shaped documents. easier to read and write for object-shaped documents.
!!! tip "Not a general deep merge"
A merge patch is not a general deep merge: a `#!json null` value in the patch deletes the key from the target.
To merge two objects recursively (e.g., defaults and user settings), use
[`update`](../api/basic_json/update.md) with `merge_objects` set to `#!cpp true`; see
[Merging objects](modifying_values.md#merging-objects).
??? example ??? example
The following code shows how a JSON Merge Patch is applied to a JSON document. The following code shows how a JSON Merge Patch is applied to a JSON document.
@@ -28,3 +35,4 @@ easier to read and write for object-shaped documents.
- [JSON Patch and Diff](json_patch.md) - a more expressive alternative that describes a sequence of operations - [JSON Patch and Diff](json_patch.md) - a more expressive alternative that describes a sequence of operations
- [JSON Pointer](json_pointer.md) - the addressing scheme used by JSON Patch - [JSON Pointer](json_pointer.md) - the addressing scheme used by JSON Patch
- Function [`merge_patch`](../api/basic_json/merge_patch.md) - Function [`merge_patch`](../api/basic_json/merge_patch.md)
- Function [`update`](../api/basic_json/update.md) - merge objects, optionally recursively
+24 -6
View File
@@ -35,8 +35,12 @@ the insertion happened — useful for "add if absent" semantics.
## Merging objects ## Merging objects
To merge one object into another, [`update`](../api/basic_json/update.md) copies all members from another object, To merge one object into another, [`update`](../api/basic_json/update.md) copies all members from another object
overwriting existing keys (similar to Python's `dict.update`). This is the idiomatic way to combine two objects. (similar to Python's `dict.update`). This is the idiomatic way to combine two objects. It has two modes:
- By default, the merge is shallow: existing keys are overwritten, even if both values are objects.
- With `merge_objects = #!cpp true`, keys whose values are objects in both JSON values are merged recursively.
Everything else is overwritten. In particular, arrays are replaced, not concatenated.
??? example ??? example
@@ -50,9 +54,22 @@ overwriting existing keys (similar to Python's `dict.update`). This is the idiom
--8<-- "examples/update.output" --8<-- "examples/update.output"
``` ```
For a recursive merge that follows [RFC 7386](https://tools.ietf.org/html/rfc7386), see A common use of the recursive mode is combining defaults with user settings. Nested defaults that the user did not set
[JSON Merge Patch](merge_patch.md). To apply a sequence of well-defined edit operations, see are kept:
[JSON Patch](json_patch.md).
```cpp
json defaults = {{"log", {{"level", "info"}, {"file", "app.log"}}}, {"retries", 3}};
json user_settings = {{"log", {{"level", "debug"}}}};
json config = defaults;
config.update(user_settings, true);
// {"log":{"file":"app.log","level":"debug"},"retries":3}
```
[JSON Merge Patch](merge_patch.md) ([RFC 7386](https://tools.ietf.org/html/rfc7386)) also merges objects recursively,
but it is a different tool: a `#!json null` in the patch means "remove this key". It is meant for applying merge patch
documents (e.g., received via HTTP PATCH). To merge configuration-like objects, use `#!cpp update(..., true)`. To apply
a sequence of well-defined edit operations, see [JSON Patch](json_patch.md).
## Removing elements ## Removing elements
@@ -72,6 +89,7 @@ a.erase(1); // [1,3,4] (erase by index)
- [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array - [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
- [`emplace`](../api/basic_json/emplace.md) - insert into an object if the key is absent - [`emplace`](../api/basic_json/emplace.md) - insert into an object if the key is absent
- [`update`](../api/basic_json/update.md) - merge objects - [`update`](../api/basic_json/update.md) - merge objects (shallow, or recursive with `merge_objects`)
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7386 merge patch
- [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements - [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements
- [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications - [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications
+6
View File
@@ -101,3 +101,9 @@ follows:
- Introduced inline namespace (`json_v3_11_0[_abi-tag]*`) in version 3.11.0. - Introduced inline namespace (`json_v3_11_0[_abi-tag]*`) in version 3.11.0.
- Changed structure of inline namespace in version 3.11.2. - Changed structure of inline namespace in version 3.11.2.
- Added ABI tag `_dp` ([`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md)) in version 3.12.0.
- Added ABI tags `_bics` ([`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md)), `_psp`
([`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md)), `_snul`
([`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md)), `_sbu8`
([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), and `_ekmo`
([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)) in version 3.13.0.
@@ -82,12 +82,13 @@ flowchart TD
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`. - Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
- The number types can be changed, see [Template number types](#template-number-types). - The number types can be changed, see [Template number types](#template-number-types).
- Integers are converted by the library's own digit parser. Floating-point numbers are converted with - The library converts integers and floating-point numbers itself, independent of the locale. Floating-point
[`std::from_chars`](https://en.cppreference.com/w/cpp/utility/from_chars) if the library is compiled with C++17 numbers are correctly rounded (to nearest, ties to even). Only a `#!c long double` that is not IEEE 754 binary64
and the standard library supports it, then with an exact fast path for `#!c double` values with few significant (e.g., the 80-bit x87 format) is converted with `#!cpp std::from_chars` where available, or else with
digits, and otherwise with the locale-aware [`std::strtold`](https://en.cppreference.com/w/cpp/string/byte/strtof). For that call, the library temporarily
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) (`std::strtof`/`std::strtold` for the replaces the `.` with the decimal point of the current locale (which may be longer than one byte, e.g., in
other floating-point types). Before version 3.13.0, the conversion was realized by `fa_IR.UTF-8`), so the result does not depend on the locale either. Changing the locale in another thread during
parsing is undefined behavior of the C library, though. Before version 3.13.0, the conversion was realized by
[`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul), [`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul),
[`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and `std::strtod`, respectively. [`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and `std::strtod`, respectively.
@@ -100,10 +101,10 @@ flowchart TD
### Number limits ### Number limits
- Any 64-bit signed or unsigned integer can be stored without loss of precision. - Any 64-bit signed or unsigned integer can be stored without loss of precision.
- Numbers exceeding the limits of `#!c double` (i.e., numbers that after conversion via - Numbers exceeding the limits of `#!c double` (i.e., numbers whose rounded value is not satisfying
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) are not satisfying
[`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception [`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception
[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing. [`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing. Numbers too
small for `#!c double` (such as `#!c 1E-400`) become zero, with the sign of the number.
- Floating-point numbers are rounded to the next number representable as `double`. For instance - Floating-point numbers are rounded to the next number representable as `double`. For instance
`#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18). `#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18).
This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`. This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`.
@@ -30,9 +30,9 @@ Requirements are split into two groups:
diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside
the library. Four violations are not caught at compile time at all: the library. Four violations are not caught at compile time at all:
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and can silently misparse - A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and silently misparses numbers
floating-point numbers, because the lexer may hand the buffer to `#!cpp std::strtod`, which reads up to the stored as a `#!cpp long double` that is not IEEE 754 binary64 (e.g., the 80-bit x87 format), because the lexer
terminating null character. hands the buffer to `#!cpp std::strtold`.
- A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation, - A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation,
and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance. and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance.
- The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion - The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion
@@ -353,16 +353,21 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
### Always required ### Always required
- A member type `value_type` that is one byte wide and `char`-compatible. The library stores and processes UTF-8 - A member type `value_type` that is one byte wide and `char`-compatible. The library stores and processes UTF-8
encoded `char` data and passes `data()` to functions that take a `#!cpp const char*`, such as `#!cpp std::strtod`. encoded `char` data and passes `data()` to functions that take a `#!cpp const char*`, such as `#!cpp std::strtold`
(only used to parse a `#!cpp long double` that is not IEEE 754 binary64, see
[`NumberFloatType`](#numberfloattype)).
`#!cpp std::wstring`, `#!cpp std::u16string`, and `#!cpp std::u32string` are **not** valid choices; see the FAQ on `#!cpp std::wstring`, `#!cpp std::u16string`, and `#!cpp std::u32string` are **not** valid choices; see the FAQ on
[wide string handling](../../home/faq.md#wide-string-handling). [wide string handling](../../home/faq.md#wide-string-handling).
- Constructors: default, copy, move, from `#!cpp const char*` (which must not be `#!cpp explicit`), from - Constructors: default, copy, move, from `#!cpp const char*` (which must not be `#!cpp explicit`), from
`#!cpp (const char*, size_type)`, and from `#!cpp (size_type, char)`; and copy or move assignment. `#!cpp (const char*, size_type)`, and from `#!cpp (size_type, char)`; and copy or move assignment.
- Member functions `size()`, `clear()`, `resize(n, c)`, `data()`, `push_back(char)`, and `operator[]` - Member functions `size()`, `clear()`, `resize(n, c)`, `data()`, `push_back(char)`, and `operator[]`
(const and non-const, returning references). `c_str()` and `back()` are **not** required. (const and non-const, returning references). `c_str()` and `back()` are **not** required.
- `data()` must return a pointer to a contiguous, **null-terminated** buffer -- the parser may hand it to - `data()` must return a pointer to a contiguous, **null-terminated** buffer. `#!cpp float`, `#!cpp double`, and a
`#!cpp std::strtod`, which reads up to the null character. A type whose `data()` is not null-terminated does not `#!cpp long double` that is IEEE 754 binary64 are converted by the library itself and do not depend on this. For any
fail to compile; it can silently misparse floating-point numbers. other `NumberFloatType` (a `#!cpp long double` of another format), the parser falls back to `#!cpp std::strtold` when
`#!cpp std::from_chars` is not available or declines the token, and `std::strtold` reads up to the null character. A type whose `data()`
is not null-terminated does not fail to compile; with such a `NumberFloatType` it can silently misparse
floating-point numbers.
- `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md), and `append(const StringType&)`, - `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md), and `append(const StringType&)`,
used by the CBOR reader for indefinite-length strings. The library's internal string concatenation additionally has used by the CBOR reader for indefinite-length strings. The library's internal string concatenation additionally has
to append a `#!cpp char` and a `#!cpp const char*`; for each it selects between `append(arg)`, `#!cpp operator+=`, to append a `#!cpp char` and a `#!cpp const char*`; for each it selects between `append(arg)`, `#!cpp operator+=`,
@@ -541,9 +546,10 @@ therefore silently changes parse results rather than raising an error. See
`NumberFloatType` must be one of `#!cpp float`, `#!cpp double`, or `#!cpp long double`: `NumberFloatType` must be one of `#!cpp float`, `#!cpp double`, or `#!cpp long double`:
- The [parser](../parsing/index.md) converts number literals with `#!cpp std::from_chars` or, as a fallback, with - The [parser](../parsing/index.md) converts number literals to `#!cpp float`, `#!cpp double`, and a
`#!cpp std::strtof`, `#!cpp std::strtod`, or `#!cpp std::strtold`; the library provides overloads for exactly these `#!cpp long double` that is IEEE 754 binary64 itself; other `#!cpp long double` formats are converted with
three types. `#!cpp std::from_chars` where available, or with `#!cpp std::strtold`. The library provides overloads for exactly
these three types.
- [`dump`](../../api/basic_json/dump.md) falls back to `#!cpp std::snprintf` with the `%g` and `%Lg` conversion - [`dump`](../../api/basic_json/dump.md) falls back to `#!cpp std::snprintf` with the `%g` and `%Lg` conversion
specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads
(`#!cpp float` is promoted to `#!cpp double`). (`#!cpp float` is promoted to `#!cpp double`).
+1 -1
View File
@@ -34,7 +34,7 @@ flowchart LR
- **JSON text** is read by an [input adapter](#input-adapters), tokenized by the lexer, and turned into SAX events by - **JSON text** is read by an [input adapter](#input-adapters), tokenized by the lexer, and turned into SAX events by
the parser. the parser.
- **Binary formats** (BJData, BSON, CBOR, MessagePack, UBJSON) are read by an input adapter and turned into the same SAX - **Binary formats** (BJData, BON8, BSON, CBOR, MessagePack, UBJSON) are read by an input adapter and turned into the same SAX
events by the `binary_reader`. events by the `binary_reader`.
- A [SAX consumer](#sax-interface) receives the events. The one used by [`parse`](../api/basic_json/parse.md) builds a - A [SAX consumer](#sax-interface) receives the events. The one used by [`parse`](../api/basic_json/parse.md) builds a
`basic_json` value tree. `basic_json` value tree.
+2 -2
View File
@@ -6,7 +6,7 @@ There are myriads of [JSON](https://json.org) libraries out there, and each may
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [OpenSSF Best Practices](https://www.bestpractices.dev/projects/289). - **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [OpenSSF Best Practices](https://www.bestpractices.dev/projects/289).
Other aspects were not so important to us: Other aspects were not so important to us:
@@ -14,4 +14,4 @@ Other aspects were not so important to us:
- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set. - **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set.
See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information. See the [contribution guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#please-dont) for more information.
+13 -5
View File
@@ -104,7 +104,7 @@ See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for
## Parse errors ## Parse errors
The library throws this exception when a parse error occurs. Parse errors The library throws this exception when a parse error occurs. Parse errors
can occur during the deserialization of JSON text, CBOR, MessagePack, as well can occur during the deserialization of JSON text or of one of the binary formats (BJData, BON8, BSON, CBOR, MessagePack, UBJSON), as well
as when using JSON Patch. as when using JSON Patch.
Exceptions have ids 1xx. Exceptions have ids 1xx.
@@ -190,7 +190,7 @@ This error indicates a syntax error while deserializing a JSON text. The error m
!!! tip !!! tip
- Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened. - Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened.
- Paste the input to a JSON validator like <http://jsonlint.com> or a tool like [jq](https://stedolan.github.io/jq/). - Paste the input to a JSON validator like <https://jsonlint.com> or a tool like [jq](https://jqlang.github.io/jq/).
### json.exception.parse_error.102 ### json.exception.parse_error.102
@@ -278,7 +278,9 @@ In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
### json.exception.parse_error.109 ### json.exception.parse_error.109
A JSON Pointer array index must be a number. A JSON Pointer array index must be a number. This exception is thrown for an array index that does not begin with a
digit, except for `-` and the empty reference token, which throw [`out_of_range.404`](#jsonexceptionout_of_range404)
where they cannot be resolved.
!!! failure "Example messages" !!! failure "Example messages"
@@ -289,6 +291,11 @@ A JSON Pointer array index must be a number.
[json.exception.parse_error.109] parse error: array index '+1' is not a number [json.exception.parse_error.109] parse error: array index '+1' is not a number
``` ```
!!! note
Before version 3.13.0, a one-character array index that is not a digit (e.g., `x`) threw
[`out_of_range.404`](#jsonexceptionout_of_range404) instead.
### json.exception.parse_error.110 ### json.exception.parse_error.110
When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has
@@ -810,7 +817,7 @@ does not list an enumerator and it is therefore converted like the first listed
A discarded value (one created by [`parse()`](../api/basic_json/parse.md) with a callback that returns `false` for the A discarded value (one created by [`parse()`](../api/basic_json/parse.md) with a callback that returns `false` for the
value, or by default-constructing a [`basic_json`](../api/basic_json/index.md) with value, or by default-constructing a [`basic_json`](../api/basic_json/index.md) with
[`value_t::discarded`](../api/basic_json/value_t.md)) was passed to a binary serialization function, either directly or [`value_t::discarded`](../api/basic_json/value_t.md)) was passed to a binary serialization function, either directly or
nested in an array or object. There is no way to represent a discarded value in CBOR, MessagePack, UBJSON, BJData, or BSON. nested in an array or object. There is no way to represent a discarded value in CBOR, MessagePack, UBJSON, BJData, BSON, or BON8.
!!! failure "Example message" !!! failure "Example message"
@@ -871,7 +878,8 @@ The provided key was not found in the JSON object.
### json.exception.out_of_range.404 ### json.exception.out_of_range.404
A reference token in a JSON Pointer could not be resolved. A reference token in a JSON Pointer could not be resolved, for instance an array index that begins with a digit but
contains other characters (e.g., `1a`), or `-` where it cannot be used.
!!! failure "Example message" !!! failure "Example message"
+1 -1
View File
@@ -20,4 +20,4 @@ The class contains a slightly modified version of the Grisu2 algorithm from Flor
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright &copy; 2021 The fast_float authors The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright &copy; 2021 The fast_float authors
+1 -1
View File
@@ -26,7 +26,7 @@ Fixes bugs found in 3.11.3 and adds several features. All changes are backward-c
[BJData](../features/binary_formats/bjdata.md) draft 3 and unsigned 64-bit integers for [BJData](../features/binary_formats/bjdata.md) draft 3 and unsigned 64-bit integers for
[BSON](../features/binary_formats/bson.md). [BSON](../features/binary_formats/bson.md).
- Adds multidimensional C-array conversion and UTF-8 encoded `std::filesystem::path` conversions, and - Adds multidimensional C-array conversion and UTF-8 encoded `std::filesystem::path` conversions, and
lowers the minimum [CMake](../integration/cmake.md) version to allow CMake 4.0. raises the minimum [CMake](../integration/cmake.md) version to 3.5 and supports CMake 4.0.
[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.12.0). [Full release notes](https://github.com/nlohmann/json/releases/tag/v3.12.0).
@@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.15)
include("cmake/HunterGate.cmake") include("cmake/HunterGate.cmake")
HunterGate( HunterGate(
URL "https://github.com/cpp-pm/hunter/archive/v0.23.297.tar.gz" URL "https://github.com/cpp-pm/hunter/archive/v0.26.12.tar.gz"
SHA1 "3319fe6a3b08090df7df98dee75134d68e2ef5a3" SHA1 "6498c5d0dec25d7fffb2b0574ebaf265179b894d"
) )
project(json_example) project(json_example)
+28 -3
View File
@@ -2,12 +2,30 @@
# Creates a `<path>.md` sibling of each HTML output (for example, # Creates a `<path>.md` sibling of each HTML output (for example,
# `features/comments/` becomes `features/comments.md`) so agents and tools can # `features/comments/` becomes `features/comments.md`) so agents and tools can
# fetch the raw Markdown directly instead of parsing rendered HTML. # fetch the raw Markdown directly instead of parsing rendered HTML. The
# `--8<-- "path"` lines of pymdownx.snippets are expanded with the extension's
# own preprocessor and the settings from mkdocs.yml, so the copies contain the
# included example code and files instead of the include directives.
import io
import os import os
import shutil
import markdown
from pymdownx.snippets import SnippetMissingError
_pages = [] _pages = []
_snippets = None
def on_config(config):
global _snippets
md = markdown.Markdown(
extensions=["pymdownx.snippets"],
extension_configs={"pymdownx.snippets": config["mdx_configs"].get("pymdownx.snippets", {})},
)
_snippets = md.preprocessors["snippet"]
_snippets.auto_append = [] # the glossary is only needed to render abbreviations
return config
def on_files(files, config): def on_files(files, config):
@@ -22,4 +40,11 @@ def on_post_build(config):
url = file.url.rstrip("/") url = file.url.rstrip("/")
target = os.path.join(site_dir, (url or "index") + ".md") target = os.path.join(site_dir, (url or "index") + ".md")
os.makedirs(os.path.dirname(target), exist_ok=True) os.makedirs(os.path.dirname(target), exist_ok=True)
shutil.copyfile(file.abs_src_path, target) with io.open(file.abs_src_path, encoding="utf-8", newline="") as source:
text = source.read()
try:
text = "\n".join(_snippets.run(text.split("\n")))
except (SnippetMissingError, OSError):
pass # keep the include directives; the regular build reports the missing file
with io.open(target, "w", encoding="utf-8", newline="") as copy:
copy.write(text)
+1 -1
View File
@@ -1,7 +1,7 @@
<!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary -->
*[ADL]: Argument-dependent lookup *[ADL]: Argument-dependent lookup
*[API]: Application Programming Interfaces *[API]: Application Programming Interface
*[ASCII]: American Standard Code for Information Interchange *[ASCII]: American Standard Code for Information Interchange
*[BDFL]: Benevolent Dictator for Life *[BDFL]: Benevolent Dictator for Life
*[BJData]: Binary JData *[BJData]: Binary JData
+1
View File
@@ -299,6 +299,7 @@ nav:
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md - 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20, JSON_HAS_CPP_23, JSON_HAS_CPP_26': api/macros/json_has_cpp_11.md - 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20, JSON_HAS_CPP_23, JSON_HAS_CPP_26': api/macros/json_has_cpp_11.md
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md - 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
- 'JSON_HAS_RANGE_VIEW_CONVERSION': api/macros/json_has_range_view_conversion.md
- 'JSON_HAS_RANGES': api/macros/json_has_ranges.md - 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
- 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md - 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md
- 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md - 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md
-31
View File
@@ -288,36 +288,6 @@ def check_header_links() -> None:
f'link to "{match.group(0)}" does not point to a documentation page') f'link to "{match.group(0)}" does not point to a documentation page')
def check_docset() -> None:
"""Every API page and every macro has an entry in the docset index; no entry points to a missing page."""
entry_re = re.compile(r"VALUES \('((?:[^']|'')*)', '(\w+)', '([^']*)'\);")
names_by_path = {}
with open("../../docset/docSet.sql", encoding="utf-8") as sql:
for name, _, path in entry_re.findall(sql.read()):
names_by_path.setdefault(path, set()).add(name.replace("''", "'"))
def to_path(page):
if os.path.basename(page) == "index.md":
return page[:-len("index.md")] + "index.html"
return page[:-len(".md")] + "/index.html"
pages = sorted(glob.glob("**/*.md", recursive=True))
for path in sorted(set(names_by_path) - {to_path(p) for p in pages}):
report("docset/stale_entry", "../../docset/docSet.sql", f'entry "{path}" has no documentation page')
for page in (p for p in pages if p.startswith("api/")):
names = names_by_path.get(to_path(page))
if not names:
report("docset/missing_entry", page, "page has no entry in docs/docset/docSet.sql")
elif page.startswith("api/macros/") and os.path.basename(page) != "index.md":
with open(page, encoding="utf-8") as content:
text = content.read()
match = re.search(r"^# (.+)$", text, re.MULTILINE) or re.search(r"<h1>(.*?)</h1>", text, re.DOTALL)
title = re.sub(r"<[^>]+>|\s+", " ", match.group(1))
for macro in filter(None, (x.strip() for x in re.split(r"[,/]", title))):
if macro not in names:
report("docset/missing_macro", page, f'macro "{macro}" has no entry in docs/docset/docSet.sql')
if __name__ == "__main__": if __name__ == "__main__":
print(120 * "-") print(120 * "-")
check_structure() check_structure()
@@ -327,7 +297,6 @@ if __name__ == "__main__":
check_heading_levels() check_heading_levels()
check_image_alt_text() check_image_alt_text()
check_header_links() check_header_links()
check_docset()
print(120 * "-") print(120 * "-")
if warnings > 0: if warnings > 0:
+45 -6
View File
@@ -9,8 +9,9 @@
#pragma once #pragma once
#include <cstdint> // uint64_t #include <cstdint> // uint64_t
#if !defined(__SIZEOF_INT128__) && defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) #include <cstring> // memcpy
#include <intrin0.h> // __umulh, _umul128 #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) && (!defined(__SIZEOF_INT128__) || (!defined(__GNUC__) && !defined(__clang__)))
#include <intrin0.h> // __umulh, _umul128, _BitScanForward64, _BitScanReverse64
#endif #endif
#include <nlohmann/detail/abi_macros.hpp> #include <nlohmann/detail/abi_macros.hpp>
@@ -28,6 +29,10 @@ inline int count_leading_zeros(std::uint64_t x) noexcept
{ {
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
return __builtin_clzll(x); return __builtin_clzll(x);
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
_BitScanReverse64(&index, x);
return 63 - static_cast<int>(index);
#else #else
int n = 0; int n = 0;
for (int shift = 32; shift != 0; shift >>= 1) for (int shift = 32; shift != 0; shift >>= 1)
@@ -42,6 +47,29 @@ inline int count_leading_zeros(std::uint64_t x) noexcept
#endif #endif
} }
/// number of trailing zero bits of x (x != 0)
inline int count_trailing_zeros(std::uint64_t x) noexcept
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_ctzll(x);
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
_BitScanForward64(&index, x);
return static_cast<int>(index);
#else
int n = 0;
for (int shift = 32; shift != 0; shift >>= 1)
{
if ((x << (64 - shift)) == 0)
{
n += shift;
x >>= shift;
}
}
return n;
#endif
}
/// the 128-bit product of two 64-bit numbers /// the 128-bit product of two 64-bit numbers
struct uint128_parts struct uint128_parts
{ {
@@ -75,15 +103,26 @@ inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexc
#endif #endif
} }
/// eight bytes as a little-endian word (compilers fold this into one load on /// eight bytes as a little-endian word (a single load on little-endian targets)
/// little-endian targets) inline std::uint64_t read_eight_bytes(const unsigned char* b) noexcept
inline std::uint64_t read_eight_bytes(const char* p) noexcept
{ {
const auto* b = reinterpret_cast<const unsigned char*>(p); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) #if defined(_MSC_VER) || defined(__x86_64__) || defined(__i386__) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
// the byte order already matches (all MSVC targets are little-endian)
std::uint64_t result = 0;
std::memcpy(&result, b, sizeof(result));
return result;
#else
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u) return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u) | (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u) | (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u); | (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
#endif
}
/// eight bytes as a little-endian word
inline std::uint64_t read_eight_bytes(const char* p) noexcept
{
return read_eight_bytes(reinterpret_cast<const unsigned char*>(p)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
} }
} // namespace detail } // namespace detail
@@ -48,7 +48,7 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_null())) if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{ {
JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j)); throw_type_must_be("null", j);
} }
n = nullptr; n = nullptr;
} }
@@ -102,7 +102,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); throw_type_must_be("number", j);
} }
} }
@@ -111,7 +111,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{ {
JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j)); throw_type_must_be("boolean", j);
} }
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
} }
@@ -121,7 +121,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); throw_type_must_be("string", j);
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
} }
@@ -137,7 +137,7 @@ inline void from_json(const BasicJsonType& j, StringType& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); throw_type_must_be("string", j);
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
@@ -183,7 +183,7 @@ inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); throw_type_must_be("array", j);
} }
l.clear(); l.clear();
std::transform(j.rbegin(), j.rend(), std::transform(j.rbegin(), j.rend(),
@@ -200,7 +200,7 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); throw_type_must_be("array", j);
} }
l.resize(j.size()); l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l), std::transform(j.begin(), j.end(), std::begin(l),
@@ -305,7 +305,7 @@ void())
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); throw_type_must_be("array", j);
} }
from_json_array_impl(j, arr, priority_tag<3> {}); from_json_array_impl(j, arr, priority_tag<3> {});
@@ -324,7 +324,7 @@ auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); throw_type_must_be("array", j);
} }
return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {}); return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {});
@@ -335,7 +335,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{ {
JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j)); throw_type_must_be("binary", j);
} }
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@@ -356,7 +356,7 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
} }
else else
{ {
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j)); throw_type_must_be("binary or array", j);
} }
} }
@@ -377,7 +377,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); throw_type_must_be("object", j);
} }
ConstructibleObjectType ret; ConstructibleObjectType ret;
@@ -432,7 +432,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); throw_type_must_be("number", j);
} }
} }
@@ -504,7 +504,7 @@ auto from_json(const BasicJsonType& j, TupleRelated&& t)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); throw_type_must_be("array", j);
} }
return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {}); return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {});
@@ -517,14 +517,14 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); throw_type_must_be("array", j);
} }
m.clear(); m.clear();
for (const auto& p : j) for (const auto& p : j)
{ {
if (JSON_HEDLEY_UNLIKELY(!p.is_array())) if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{ {
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p)); throw_type_must_be("array", p);
} }
m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>()); m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>());
} }
@@ -591,7 +591,7 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); throw_type_must_be("string", j);
} }
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
// Checking for C++20 standard or later can be insufficient in case the // Checking for C++20 standard or later can be insufficient in case the
@@ -414,13 +414,13 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
BasicJsonType key = p.first; BasicJsonType key = p.first;
if (JSON_HEDLEY_UNLIKELY(!key.is_string())) if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
{ {
JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key)); throw_type_must_be("string", key);
} }
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>(); auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second)) if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
{ {
JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key)); JSON_THROW(type_error::create(exception_id::enum_key_duplicate, concat("duplicate object key '", key_string, "'"), &key));
} }
} }
external_constructor<value_t::object>::construct(j, std::move(obj)); external_constructor<value_t::object>::construct(j, std::move(obj));
+144
View File
@@ -45,6 +45,84 @@ namespace detail
// exceptions // // exceptions //
//////////////// ////////////////
/*!
@brief the ids of the exceptions thrown by the library
@note The values are part of the public API: they are the `id` member of the
exceptions and appear in their `what()` messages.
@sa https://json.nlohmann.me/home/exceptions/
*/
enum class exception_id : int
{
// parse_error
syntax_error = 101, ///< unexpected token or invalid literal while parsing JSON
patch_not_an_array = 104, ///< a JSON Patch document is not an array of objects
patch_invalid_operation = 105, ///< a JSON Patch operation is malformed
pointer_index_leading_zero = 106, ///< a JSON Pointer array index has a leading zero
pointer_missing_slash = 107, ///< a JSON Pointer does not start with '/'
pointer_invalid_escape = 108, ///< a JSON Pointer contains an escape other than ~0 and ~1
pointer_index_not_a_number = 109, ///< a JSON Pointer array index is not a number
unexpected_end_of_input = 110, ///< a binary input ends early, or has bytes left at its end
unexpected_byte = 112, ///< a binary input contains an unexpected byte or invalid length
invalid_string_or_size = 113, ///< a binary input contains an invalid string or size specification
bson_unsupported_type = 114, ///< a BSON record type is not supported
invalid_high_precision_number = 115, ///< a UBJSON/BJData high-precision number cannot be parsed
// invalid_iterator
iterators_incompatible = 201, ///< the iterators of a range belong to different values
iterator_from_other_value = 202, ///< an iterator does not belong to the value it is used with
iterator_range_from_other_value = 203, ///< an iterator range does not belong to the value it is used with
iterator_range_out_of_range = 204, ///< an iterator range of a primitive value is not [begin, end)
iterator_out_of_range = 205, ///< an iterator of a primitive value is not begin()
iterator_range_of_null = 206, ///< an iterator range belongs to a null value
iterator_key_not_object = 207, ///< key() is called on an iterator of a non-object
iterator_subscript_on_object = 208, ///< operator[] is called on an iterator of an object
iterator_arithmetic_on_object = 209, ///< an offset operator is used on an iterator of an object
insert_range_incompatible = 210, ///< the iterators of an inserted range belong to different values
insert_range_into_itself = 211, ///< an inserted range belongs to the value it is inserted into
iterators_compare_different_values = 212, ///< iterators of different values are compared
iterator_order_on_object = 213, ///< iterators of an object are compared by order
iterator_value_unavailable = 214, ///< an iterator does not refer to a value
// type_error
object_from_non_pairs = 301, ///< an object is created from an initializer list that is not a list of pairs
type_mismatch = 302, ///< a value has the wrong type for a conversion
incompatible_reference_type = 303, ///< get_ref() is called with a reference type that does not match the value
at_wrong_type = 304, ///< at() is called on a value of the wrong type
subscript_wrong_type = 305, ///< operator[] is called on a value of the wrong type
value_wrong_type = 306, ///< value() is called on a value of the wrong type
erase_wrong_type = 307, ///< erase() is called on a value of the wrong type
push_back_wrong_type = 308, ///< push_back() or operator+= is called on a value of the wrong type
insert_wrong_type = 309, ///< insert() is called on a value of the wrong type
swap_wrong_type = 310, ///< swap() is called on a value of the wrong type
emplace_wrong_type = 311, ///< emplace() or emplace_back() is called on a value of the wrong type
update_wrong_type = 312, ///< update() is called on a value of the wrong type
unflatten_invalid_value = 313, ///< unflatten() finds conflicting paths
unflatten_not_object = 314, ///< unflatten() is called on a non-object
unflatten_value_not_primitive = 315, ///< unflatten() is called on an object with non-primitive values
invalid_utf8 = 316, ///< dump() finds a string that is not valid UTF-8
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
discarded_value_used = 321, ///< a discarded value is used
// out_of_range
array_index_out_of_range = 401, ///< an array index is out of range
pointer_past_the_end_index = 402, ///< a JSON Pointer uses the array index '-'
key_not_found = 403, ///< an object key is not found
pointer_unresolved = 404, ///< a JSON Pointer reference token cannot be resolved
patch_on_root = 405, ///< the root of a value has no parent, e.g. for a JSON Patch 'remove' or 'add' at the root
number_overflow = 406, ///< a number cannot be stored without overflowing to NaN or INF
integer_too_large = 407, ///< an integer cannot be represented in the binary format
container_too_large = 408, ///< the size of a container in a binary input exceeds the maximal capacity
bson_key_with_null = 409, ///< a BSON key contains U+0000
value_out_of_range = 410, ///< an enum value is undefined, or a JSON Pointer array index exceeds size_type
patch_add_parent_not_container = 411, ///< the parent of a JSON Patch 'add' target is not a container
length_too_large = 412, ///< a length does not fit into the length field of a binary format
patch_remove_parent_not_container = 413, ///< the parent of a JSON Patch 'remove' target is not a container
patch_move_into_child = 414, ///< a JSON Patch 'move' moves a value into one of its children
subtype_out_of_range = 415, ///< a binary subtype does not fit into one byte
// other_error
internal_error = 500, ///< unreachable code was reached
patch_test_failed = 501, ///< a JSON Patch 'test' operation failed
size_marker_required = 502 ///< UBJSON/BJData output with type markers requires size markers
};
/// @brief general exception of the @ref basic_json class /// @brief general exception of the @ref basic_json class
/// @sa https://json.nlohmann.me/api/basic_json/exception/ /// @sa https://json.nlohmann.me/api/basic_json/exception/
class exception : public std::exception class exception : public std::exception
@@ -195,6 +273,18 @@ class parse_error : public exception
return {id_, byte_, w.c_str()}; return {id_, byte_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), pos, what_arg, context);
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), byte_, what_arg, context);
}
/*! /*!
@brief byte index of the parse error @brief byte index of the parse error
@@ -229,6 +319,12 @@ class invalid_iterator : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static invalid_iterator create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
invalid_iterator(int id_, const char* what_arg) invalid_iterator(int id_, const char* what_arg)
@@ -247,6 +343,12 @@ class type_error : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static type_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -264,6 +366,12 @@ class out_of_range : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static out_of_range create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -281,6 +389,12 @@ class other_error : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static other_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -307,6 +421,36 @@ void templated_json_throw(ExceptionType exception)
(void)exception; (void)exception;
} }
/*!
@brief throws because @a j does not have the type a conversion expects
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
@param[in] j the value with the wrong type
@throw type_error.302 always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const BasicJsonType& j)
{
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(j);
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
}
/*!
@brief throws because an operation is not supported for the type of @a j
@param[in] id_ the id of the type_error exception (at_wrong_type..update_wrong_type)
@param[in] operation the operation, e.g. "erase()"
@param[in] j the value the operation was called on
@throw type_error always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
{
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(operation);
static_cast<void>(j);
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
}
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
+84 -135
View File
@@ -12,7 +12,6 @@
#include <cmath> // ldexp #include <cmath> // ldexp
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t, uintmax_t #include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t, uintmax_t
#include <cstdio> // snprintf
#include <cstring> // memcpy #include <cstring> // memcpy
#include <iterator> // back_inserter #include <iterator> // back_inserter
#include <limits> // numeric_limits #include <limits> // numeric_limits
@@ -196,8 +195,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof())) if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))
{ {
return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, return last_byte_error(exception_id::unexpected_end_of_input, concat("expected end of input; last byte: 0x", get_token_string()), "value");
exception_message(concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
} }
} }
@@ -319,8 +317,7 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start)) if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
{ {
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read, return last_byte_error(exception_id::unexpected_byte, concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document");
exception_message(concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document"), nullptr));
} }
return true; return true;
} }
@@ -512,9 +509,7 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(len < 1)) if (JSON_HEDLEY_UNLIKELY(len < 1))
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::unexpected_byte, concat("string length must be at least 1, is ", std::to_string(len)), "string");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result))) if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result)))
@@ -524,10 +519,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(get() != 0x00)) if (JSON_HEDLEY_UNLIKELY(get() != 0x00))
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::unexpected_byte, "BSON string is not null-terminated", "string");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message("BSON string is not null-terminated",
"string"), nullptr));
} }
return check_string_utf8(result, "string"); return check_string_utf8(result, "string");
@@ -547,9 +539,7 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(len < 0)) if (JSON_HEDLEY_UNLIKELY(len < 0))
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::unexpected_byte, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
} }
// All BSON binary values have a subtype // All BSON binary values have a subtype
@@ -639,11 +629,9 @@ class binary_reader
default: // anything else is not supported (yet) default: // anything else is not supported (yet)
{ {
std::array<char, 3> cr{{}}; const std::string cr_str = hex_byte(static_cast<std::uint8_t>(element_type));
static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
const std::string cr_str{cr.data()};
return sax->parse_error(element_type_parse_position, cr_str, return sax->parse_error(element_type_parse_position, cr_str,
parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr)); parse_error::create(exception_id::bson_unsupported_type, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
} }
} }
} }
@@ -967,9 +955,7 @@ class binary_reader
{ {
if (tag_handler == cbor_tag_handler_t::error) if (tag_handler == cbor_tag_handler_t::error)
{ {
auto last_token = get_token_string(); return unexpected_byte("invalid byte", "value");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
// ignore and store: the tag value is already in the head, so // ignore and store: the tag value is already in the head, so
@@ -989,9 +975,7 @@ class binary_reader
{ {
case cbor_tag_handler_t::error: case cbor_tag_handler_t::error:
{ {
auto last_token = get_token_string(); return unexpected_byte("invalid byte", "value");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
case cbor_tag_handler_t::ignore: case cbor_tag_handler_t::ignore:
@@ -1065,9 +1049,7 @@ class binary_reader
default: // anything else (0xFF is handled inside the other types) default: // anything else (0xFF is handled inside the other types)
{ {
auto last_token = get_token_string(); return unexpected_byte("invalid byte", "value");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
} }
} }
@@ -1080,10 +1062,7 @@ class binary_reader
*/ */
bool cbor_indefinite_string_error(const char* type_name, const char* context) bool cbor_indefinite_string_error(const char* type_name, const char* context)
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, concat("indefinite-length ", type_name, " is not allowed inside indefinite-length ", type_name, "; last byte: 0x", get_token_string()), context);
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("indefinite-length ", type_name,
" is not allowed inside indefinite-length ", type_name, "; last byte: 0x", last_token), context), nullptr));
} }
/*! /*!
@@ -1160,9 +1139,7 @@ class binary_reader
default: default:
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", get_token_string()), "string");
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", last_token), "string"), nullptr));
} }
} }
} }
@@ -1292,9 +1269,7 @@ class binary_reader
break; break;
} }
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key");
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
} }
/*! /*!
@@ -1375,9 +1350,7 @@ class binary_reader
default: default:
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", get_token_string()), "binary");
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", last_token), "binary"), nullptr));
} }
} }
} }
@@ -1523,7 +1496,7 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size())) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
exception_message(concat("excessive ", context, " size"), "size"), nullptr)); exception_message(concat("excessive ", context, " size"), "size"), nullptr));
} }
result = conditional_static_cast<std::size_t>(len); result = conditional_static_cast<std::size_t>(len);
@@ -2011,9 +1984,7 @@ class binary_reader
default: // anything else default: // anything else
{ {
auto last_token = get_token_string(); return unexpected_byte("invalid byte", "value");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
} }
} }
@@ -2094,9 +2065,7 @@ class binary_reader
default: default:
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", get_token_string()), "string");
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
} }
} }
} }
@@ -2188,9 +2157,7 @@ class binary_reader
break; break;
} }
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key");
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
} }
/*! /*!
@@ -2499,8 +2466,7 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(len < 0)) if (JSON_HEDLEY_UNLIKELY(len < 0))
{ {
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, return last_byte_error(exception_id::invalid_string_or_size, "string length must not be negative", "string");
exception_message("string length must not be negative", "string"), nullptr));
} }
return true; return true;
} }
@@ -2600,18 +2566,7 @@ class binary_reader
default: default:
break; break;
} }
auto last_token = get_token_string(); return length_type_error("", "string");
std::string message;
if (input_format != input_format_t::bjdata)
{
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
}
else
{
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
}
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(message, "string"), nullptr));
} }
/*! /*!
@@ -2696,12 +2651,11 @@ class binary_reader
} }
if (JSON_HEDLEY_UNLIKELY(number < 0)) if (JSON_HEDLEY_UNLIKELY(number < 0))
{ {
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, return last_byte_error(exception_id::invalid_string_or_size, "count in an optimized container must be positive", "size");
exception_message("count in an optimized container must be positive", "size"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
exception_message("integer value overflow", "size"), nullptr)); exception_message("integer value overflow", "size"), nullptr));
} }
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
@@ -2799,7 +2753,7 @@ class binary_reader
} }
if (!value_in_range_of<std::size_t>(number)) if (!value_in_range_of<std::size_t>(number))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
exception_message("integer value overflow", "size"), nullptr)); exception_message("integer value overflow", "size"), nullptr));
} }
result = detail::conditional_static_cast<std::size_t>(number); result = detail::conditional_static_cast<std::size_t>(number);
@@ -2814,7 +2768,7 @@ class binary_reader
} }
if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array
{ {
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message("ndarray dimensional vector is not allowed", "size"), nullptr)); return last_byte_error(exception_id::invalid_string_or_size, "ndarray dimensional vector is not allowed", "size");
} }
std::vector<size_t> dim; std::vector<size_t> dim;
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim))) if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
@@ -2850,9 +2804,7 @@ class binary_reader
const char* type_name = bjd_type_name(ndarray_dtype); const char* type_name = bjd_type_name(ndarray_dtype);
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr)) if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
{ {
auto last_token = get_token_string(); return unexpected_byte("invalid byte", "type");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message("invalid byte: 0x" + last_token, "type"), nullptr));
} }
string_t type_key = "_ArrayType_"; string_t type_key = "_ArrayType_";
@@ -2880,7 +2832,7 @@ class binary_reader
// or SIZE_MAX. // or SIZE_MAX.
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i)) if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message("excessive ndarray size caused overflow", "size"), nullptr)); return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
} }
result *= i; result *= i;
// the pre-check above already rules out result becoming 0 // the pre-check above already rules out result becoming 0
@@ -2889,7 +2841,7 @@ class binary_reader
// unknown-size container (see get_ubjson_size_type()) // unknown-size container (see get_ubjson_size_type())
if (result == npos) if (result == npos)
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message("excessive ndarray size caused overflow", "size"), nullptr)); return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i))) if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i)))
{ {
@@ -2906,18 +2858,7 @@ class binary_reader
default: default:
break; break;
} }
auto last_token = get_token_string(); return length_type_error(" after '#'", "size");
std::string message;
if (input_format != input_format_t::bjdata)
{
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
}
else
{
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
}
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(message, "size"), nullptr));
} }
/*! /*!
@@ -2950,9 +2891,7 @@ class binary_reader
if (input_format == input_format_t::bjdata if (input_format == input_format_t::bjdata
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second))) && JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::unexpected_byte, concat("marker 0x", get_token_string(), " is not a permitted optimized array type"), "type");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type"))) if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
@@ -2967,9 +2906,7 @@ class binary_reader
{ {
return false; return false;
} }
auto last_token = get_token_string(); return last_byte_error(exception_id::unexpected_byte, concat("expected '#' after type information; last byte: 0x", get_token_string()), "size");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
} }
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second); const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
@@ -2988,8 +2925,7 @@ class binary_reader
const bool is_error = get_ubjson_size_value(result.first, is_ndarray); const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray) if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
{ {
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read, return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
exception_message("ndarray requires both type and size", "size"), nullptr));
} }
return is_error; return is_error;
} }
@@ -3121,9 +3057,7 @@ class binary_reader
} }
if (JSON_HEDLEY_UNLIKELY(current > 127)) if (JSON_HEDLEY_UNLIKELY(current > 127))
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", get_token_string()), "char");
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
} }
string_t s(1, static_cast<typename string_t::value_type>(current)); string_t s(1, static_cast<typename string_t::value_type>(current));
return sax->string(s); return sax->string(s);
@@ -3144,8 +3078,7 @@ class binary_reader
default: // anything else default: // anything else
break; break;
} }
auto last_token = get_token_string(); return unexpected_byte("invalid byte", "value");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message("invalid byte: 0x" + last_token, "value"), nullptr));
} }
/*! /*!
@@ -3208,7 +3141,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F') if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F')
&& size_and_type.first > max_valueless_container_size)) && size_and_type.first > max_valueless_container_size))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
exception_message("excessive array size", "size"), nullptr)); exception_message("excessive array size", "size"), nullptr));
} }
@@ -3244,9 +3177,7 @@ class binary_reader
// do not accept ND-array size in objects in BJData // do not accept ND-array size in objects in BJData
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0) if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message("BJData object does not support ND-array size in optimized format", "object"), nullptr));
} }
if (size_and_type.first != npos) if (size_and_type.first != npos)
@@ -3283,7 +3214,7 @@ class binary_reader
// the lexer would stop at a NUL and accept the digits before it // the lexer would stop at a NUL and accept the digits before it
if (JSON_HEDLEY_UNLIKELY(current == '\0')) if (JSON_HEDLEY_UNLIKELY(current == '\0'))
{ {
return sax->parse_error(chars_read, "00", parse_error::create(115, chars_read, return sax->parse_error(chars_read, "00", parse_error::create(exception_id::invalid_high_precision_number, chars_read,
exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr)); exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr));
} }
number_vector.push_back(static_cast<char>(current)); number_vector.push_back(static_cast<char>(current));
@@ -3300,7 +3231,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
{ {
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, return sax->parse_error(chars_read, number_string, parse_error::create(exception_id::invalid_high_precision_number, chars_read,
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
} }
@@ -3318,7 +3249,7 @@ class binary_reader
return sax->parse_error( return sax->parse_error(
chars_read, chars_read,
number_string, number_string,
out_of_range::create(406, concat("number overflow parsing '", number_string, '\''), nullptr)); out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", number_string, '\''), nullptr));
} }
// number_string is a std::string, while the SAX interface takes a // number_string is a std::string, while the SAX interface takes a
// string_t; convert explicitly, as the two are only implicitly // string_t; convert explicitly, as the two are only implicitly
@@ -3340,7 +3271,7 @@ class binary_reader
case token_type::end_of_input: case token_type::end_of_input:
case token_type::literal_or_value: case token_type::literal_or_value:
default: default:
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, return sax->parse_error(chars_read, number_string, parse_error::create(exception_id::invalid_high_precision_number, chars_read,
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
} }
} }
@@ -3399,20 +3330,6 @@ class binary_reader
return 0x80 <= c && c <= 0xBF; return 0x80 <= c && c <= 0xBF;
} }
/*!
@brief report a parse error at the last read byte
@param[in] detail a detailed error message
@param[in] context further context information
@return false
*/
bool bon8_error(const std::string& detail, const char* context)
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat(detail, ": 0x", last_token), context), nullptr));
}
/*! /*!
@brief read a BON8 value and everything nested inside it @brief read a BON8 value and everything nested inside it
@@ -3628,7 +3545,7 @@ class binary_reader
} }
// 0xFE: end of container where a value is expected // 0xFE: end of container where a value is expected
return bon8_error("invalid byte", "value"); return unexpected_byte("invalid byte", "value");
} }
/*! /*!
@@ -3755,7 +3672,7 @@ class binary_reader
current = byte; current = byte;
} }
return bon8_error("expected a string; last byte", "key"); return unexpected_byte("expected a string; last byte", "key");
} }
/*! /*!
@@ -3878,7 +3795,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(!valid_second)) if (JSON_HEDLEY_UNLIKELY(!valid_second))
{ {
return bon8_error("invalid UTF-8 byte", "string"); return unexpected_byte("invalid UTF-8 byte", "string");
} }
result.push_back(static_cast<typename string_t::value_type>(byte)); result.push_back(static_cast<typename string_t::value_type>(byte));
@@ -3892,7 +3809,7 @@ class binary_reader
} }
if (JSON_HEDLEY_UNLIKELY(!is_bon8_continuation(current))) if (JSON_HEDLEY_UNLIKELY(!is_bon8_continuation(current)))
{ {
return bon8_error("invalid UTF-8 byte", "string"); return unexpected_byte("invalid UTF-8 byte", "string");
} }
result.push_back(static_cast<typename string_t::value_type>(current)); result.push_back(static_cast<typename string_t::value_type>(current));
} }
@@ -3937,7 +3854,7 @@ class binary_reader
{ {
// in case of failure, advance position by 1 to report the failing location // in case of failure, advance position by 1 to report the failing location
++chars_read; ++chars_read;
sax->parse_error(chars_read, "<end of file>", parse_error::create(110, chars_read, exception_message("unexpected end of input", context), nullptr)); sax->parse_error(chars_read, "<end of file>", parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr));
return false; return false;
} }
return true; return true;
@@ -4091,7 +4008,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result))) if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
{ {
return sax->parse_error(chars_read, get_token_string(), return sax->parse_error(chars_read, get_token_string(),
out_of_range::create(406, exception_message("number overflow", "value"), nullptr)); out_of_range::create(exception_id::number_overflow, exception_message("number overflow", "value"), nullptr));
} }
return sax->number_float(result, ""); return sax->number_float(result, "");
} }
@@ -4211,9 +4128,7 @@ class binary_reader
if (error_handler == error_handler_t::strict) if (error_handler == error_handler_t::strict)
{ {
auto last_token = get_token_string(); return last_byte_error(exception_id::invalid_string_or_size, "invalid string: ill-formed UTF-8 byte", context);
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message("invalid string: ill-formed UTF-8 byte", context), nullptr));
} }
result = sanitize_utf8(result, error_handler); result = sanitize_utf8(result, error_handler);
@@ -4309,19 +4224,53 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof())) if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))
{ {
return sax->parse_error(chars_read, "<end of file>", return sax->parse_error(chars_read, "<end of file>",
parse_error::create(110, chars_read, exception_message("unexpected end of input", context), nullptr)); parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr));
} }
return true; return true;
} }
/*!
@brief reports a parse error at the last read byte
@param[in] id_ the id of the parse_error exception
@param[in] detail a detailed error message
@param[in] context further context information
@return the result of the SAX parser's parse_error()
*/
bool last_byte_error(const exception_id id_, const std::string& detail, const std::string& context) const
{
return sax->parse_error(chars_read, get_token_string(), parse_error::create(id_, chars_read, exception_message(detail, context), nullptr));
}
/*!
@brief reports the last read byte as unexpected (parse_error.112)
@param[in] detail what is wrong with the byte, e.g. "invalid byte"
@param[in] context further context information
@return the result of the SAX parser's parse_error()
*/
bool unexpected_byte(const char* detail, const std::string& context) const
{
return last_byte_error(exception_id::unexpected_byte, concat(detail, ": 0x", get_token_string()), context);
}
/*!
@brief reports that the last read byte is not a UBJSON/BJData length type (parse_error.113)
@param[in] position where the length type was expected, e.g. " after '#'"
@param[in] context further context information
@return the result of the SAX parser's parse_error()
*/
bool length_type_error(const char* position, const char* context) const
{
const char* types = input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L";
return last_byte_error(exception_id::invalid_string_or_size,
concat("expected length type specification (", types, ")", position, "; last byte: 0x", get_token_string()), context);
}
/*! /*!
@return a string representation of the last read byte @return a string representation of the last read byte
*/ */
std::string get_token_string() const std::string get_token_string() const
{ {
std::array<char, 3> cr{{}}; return hex_byte(static_cast<std::uint8_t>(current));
static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
return std::string{cr.data()};
} }
/*! /*!
@@ -581,7 +581,7 @@ class wide_string_input_adapter
template<class T> template<class T>
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1) JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
{ {
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr)); JSON_THROW(parse_error::create(exception_id::unexpected_byte, 1, "wide string type cannot be interpreted as binary data", nullptr));
} }
private: private:
@@ -793,7 +793,7 @@ inline file_input_adapter input_adapter(std::FILE* file)
{ {
if (file == nullptr) if (file == nullptr)
{ {
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return file_input_adapter(file); return file_input_adapter(file);
} }
@@ -802,7 +802,7 @@ inline input_stream_adapter input_adapter(std::istream& stream)
{ {
if (stream.rdbuf() == nullptr) if (stream.rdbuf() == nullptr)
{ {
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return input_stream_adapter(stream); return input_stream_adapter(stream);
} }
@@ -827,7 +827,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b)
{ {
if (b == nullptr) if (b == nullptr)
{ {
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
auto length = std::strlen(reinterpret_cast<const char*>(b)); auto length = std::strlen(reinterpret_cast<const char*>(b));
const auto* ptr = reinterpret_cast<const char*>(b); const auto* ptr = reinterpret_cast<const char*>(b);
+83 -76
View File
@@ -176,6 +176,27 @@ template<typename ArrayType>
inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/) inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/)
{} {}
/*!
@brief reports an object or array whose announced size exceeds max_size()
Shared by json_sax_dom_parser and json_sax_dom_callback_parser.
@param[in] sax the SAX parser to report the error to
@param[in] len the number of elements announced by the input, or unknown_size()
@param[in] kind "object" or "array"
@param[in] ref the object or array that was just created
@return whether parsing should continue (false after reporting out_of_range.408)
*/
template<typename SAX, typename BasicJsonType>
bool check_container_size(SAX& sax, std::size_t len, const char* kind, BasicJsonType* ref)
{
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref->max_size()))
{
return sax.parse_error(0, "", out_of_range::create(exception_id::container_too_large, concat("excessive ", kind, " size: ", std::to_string(len)), ref));
}
return true;
}
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
/*! /*!
@brief set the diagnostic positions of a value the DOM SAX parsers just stored @brief set the diagnostic positions of a value the DOM SAX parsers just stored
@@ -185,6 +206,35 @@ befriends this struct, as the position members are private.
*/ */
struct diagnostic_positions struct diagnostic_positions
{ {
/*!
@param[in,out] v the object or array whose opening brace or bracket was just read
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
*/
template<typename BasicJsonType, typename LexerType>
static void set_container_start(BasicJsonType& v, LexerType* lexer)
{
if (lexer)
{
// Lexer has read the first character of the container, so
// subtract 1 from the position to get the correct start position.
v.start_position = lexer->get_position() - 1;
}
}
/*!
@param[in,out] v the object or array whose closing brace or bracket was just read
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
*/
template<typename BasicJsonType, typename LexerType>
static void set_container_end(BasicJsonType& v, LexerType* lexer)
{
if (lexer)
{
// Lexer's position is past the closing brace or bracket, so set that as the end position.
v.end_position = lexer->get_position();
}
}
/*! /*!
@param[in,out] v the value that was just parsed @param[in,out] v the value that was just parsed
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone @param[in] lexer the lexer that read it, or nullptr to leave @a v alone
@@ -349,22 +399,11 @@ class json_sax_dom_parser
ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the object here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
if (m_lexer_ref) diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
{
// Lexer has read the first character of the object, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) return check_container_size(*this, len, "object", ref_stack.back());
{
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
return true;
} }
bool key(string_t& val) bool key(string_t& val)
@@ -383,11 +422,7 @@ class json_sax_dom_parser
JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(ref_stack.back()->is_object());
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
if (m_lexer_ref) diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
{
// Lexer's position is past the closing brace, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -400,17 +435,13 @@ class json_sax_dom_parser
ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the array here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
if (m_lexer_ref) diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
{
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back())))
{ {
return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); return false;
} }
if (len != detail::unknown_size()) if (len != detail::unknown_size())
@@ -427,11 +458,7 @@ class json_sax_dom_parser
JSON_ASSERT(ref_stack.back()->is_array()); JSON_ASSERT(ref_stack.back()->is_array());
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
if (m_lexer_ref) diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
{
// Lexer's position is past the closing bracket, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -611,21 +638,11 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the object here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
if (m_lexer_ref) diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
{
// Lexer has read the first character of the object, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
// check object limit return check_container_size(*this, len, "object", ref_stack.back());
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
} }
return true; return true;
} }
@@ -695,11 +712,7 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
if (m_lexer_ref) diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
{
// Lexer's position is past the closing brace, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -710,13 +723,7 @@ class json_sax_dom_callback_parser
} }
} }
JSON_ASSERT(!ref_stack.empty()); const string_t object_key = pop_container();
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
const string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
{ {
@@ -742,20 +749,13 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the array here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
if (m_lexer_ref) diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
{
// Lexer has read the first character of the array, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
// check array limit if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back())))
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{ {
return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); return false;
} }
if (len != detail::unknown_size()) if (len != detail::unknown_size())
@@ -779,11 +779,7 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
if (m_lexer_ref) diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
{
// Lexer's position is past the closing bracket, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -809,13 +805,7 @@ class json_sax_dom_callback_parser
} }
} }
JSON_ASSERT(!ref_stack.empty()); const string_t object_key = pop_container();
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
const string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
// remove discarded value // remove discarded value
if (!ref_stack.empty() && ref_stack.back()) if (!ref_stack.empty() && ref_stack.back())
@@ -902,6 +892,23 @@ class json_sax_dom_callback_parser
return string_t{}; return string_t{};
} }
/*!
@brief leave the object or array that end_object()/end_array() just closed
@return the key the container is stored under in its parent (see
container_key_stack)
*/
string_t pop_container()
{
JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
return object_key;
}
/*! /*!
@brief remove the discarded value the callback rejected from its parent, @brief remove the discarded value the callback rejected from its parent,
unless it is a duplicate key's slot with a stashed previous value, in unless it is a duplicate key's slot with a stashed previous value, in
+59 -10
View File
@@ -221,6 +221,44 @@ class lexer : public lexer_base<BasicJsonType>
// scan functions // scan functions
///////////////////// /////////////////////
/// contiguous input: try to decode the 4 hex digits following `\\u`
/// directly from the input buffer via hex_codepoint(), instead of 4 calls
/// to get(). On success, advances the adapter and the position counters
/// exactly as those 4 get() calls would (a hex digit is never '\n', so
/// only the flat counters move) and leaves @a current holding the last of
/// the 4 digits, just as the last such get() would; the codepoint is
/// written to @a out. Makes no state change and returns false - for a
/// pending unget, fewer than 4 remaining bytes, or any of the 4 bytes not
/// being a hex digit - so the caller falls back unchanged to the
/// per-character loop, which then reports the same diagnostic (stopping
/// at the first invalid digit) as before this optimization.
bool get_codepoint_bulk(std::true_type /*bulk*/, int& out)
{
if (next_unget || ia.bulk_remaining() < 4)
{
return false;
}
const char_type* const raw = ia.bulk_data();
const int codepoint = hex_codepoint(reinterpret_cast<const unsigned char*>(raw));
if (codepoint < 0)
{
return false;
}
ia.bulk_skip(4);
// a hex digit is never a newline, so only the flat counters advance
position.chars_read_total += 4;
position.chars_read_current_line += 4;
current = char_traits<char_type>::to_int_type(raw[3]);
out = codepoint;
return true;
}
/// streaming input: no bulk fast path
bool get_codepoint_bulk(std::false_type /*bulk*/, int& /*out*/) const noexcept
{
return false;
}
/*! /*!
@brief get codepoint from 4 hex characters following `\\u` @brief get codepoint from 4 hex characters following `\\u`
@@ -240,6 +278,14 @@ class lexer : public lexer_base<BasicJsonType>
{ {
// this function only makes sense after reading `\u` // this function only makes sense after reading `\u`
JSON_ASSERT(current == 'u'); JSON_ASSERT(current == 'u');
// contiguous input: decode all 4 hex digits directly from the buffer
int fast_codepoint = 0;
if (get_codepoint_bulk(std::integral_constant<bool, bulk_scan> {}, fast_codepoint))
{
return fast_codepoint;
}
int codepoint = 0; int codepoint = 0;
const auto factors = { 12u, 8u, 4u, 0u }; const auto factors = { 12u, 8u, 4u, 0u };
@@ -1044,9 +1090,11 @@ class lexer : public lexer_base<BasicJsonType>
token_type::parse_error otherwise token_type::parse_error otherwise
@note The scanner is independent of the current locale: token_buffer @note The scanner is independent of the current locale: token_buffer
always holds `.`. Only the std::strtod fallback of convert_number() always holds `.`. The conversion of float and double does not use
depends on the locale, and it looks up the decimal point right the locale either. Only the std::strtold fallback of
before converting (see detail::convert_float_locale_aware()). convert_number() for long double formats other than binary64
depends on it, and it looks up the decimal point right before
converting (see detail::convert_float_locale_aware()).
*/ */
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated. token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
{ {
@@ -1059,7 +1107,7 @@ class lexer : public lexer_base<BasicJsonType>
// offset just past the last mantissa byte in token_buffer (i.e. the // offset just past the last mantissa byte in token_buffer (i.e. the
// index of 'e'/'E', or the whole token when there is no exponent). // index of 'e'/'E', or the whole token when there is no exponent).
// convert_number() uses it to count significant digits; npos means // convert_number() uses it to split the token; npos means
// "not seen an exponent yet" and is resolved at scan_number_done // "not seen an exponent yet" and is resolved at scan_number_done
std::size_t mantissa_end = std::string::npos; std::size_t mantissa_end = std::string::npos;
@@ -1389,8 +1437,8 @@ scan_number_done:
@param[in] mantissa_end offset just past the last mantissa byte in @param[in] mantissa_end offset just past the last mantissa byte in
token_buffer (the index of 'e'/'E', or token_buffer (the index of 'e'/'E', or
token_buffer.size() when there is no exponent); token_buffer.size() when there is no exponent);
used to skip Clinger's fast path when it cannot with decimal_point_position, it locates the parts
possibly succeed - see detail::mantissa_fits_clinger() of a float token without scanning it again
*/ */
token_type convert_number(token_type number_type, std::size_t mantissa_end) token_type convert_number(token_type number_type, std::size_t mantissa_end)
{ {
@@ -1444,10 +1492,11 @@ scan_number_done:
} }
// this code is reached if we parse a floating-point number or if an // this code is reached if we parse a floating-point number or if an
// integer conversion above overflowed. Prefer std::from_chars // integer conversion above overflowed. float and double (and long
// (Eisel-Lemire, locale-independent, correctly rounded) when available; // double where it is binary64) are converted by the library itself,
// otherwise the exact Clinger fast path (double only); otherwise the // correctly rounded and independent of the locale; other long double
// locale-aware strtof/strtod/strtold. // formats use std::from_chars when available, otherwise the
// locale-aware strtold.
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float)) if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
{ {
return token_type::value_float; return token_type::value_float;
File diff suppressed because it is too large Load Diff
+28 -39
View File
@@ -156,9 +156,7 @@ class parser
// strict mode: next byte must be EOF // strict mode: next byte must be EOF
if (get_token() != token_type::end_of_input) if (get_token() != token_type::end_of_input)
{ {
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::end_of_input, "value"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
} }
} }
else else
@@ -197,10 +195,7 @@ class parser
// in strict mode, input must be completely read // in strict mode, input must be completely read
if (get_token() != token_type::end_of_input) if (get_token() != token_type::end_of_input)
{ {
sdp.parse_error(m_lexer.get_position(), syntax_error(sdp, exception_message(token_type::end_of_input, "value"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value"), nullptr));
} }
} }
else else
@@ -250,9 +245,7 @@ class parser
// parse key // parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{ {
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{ {
@@ -262,9 +255,7 @@ class parser
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
{ {
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::name_separator, "object separator"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
} }
// remember we are now inside an object // remember we are now inside an object
@@ -307,7 +298,7 @@ class parser
{ {
return sax->parse_error(m_lexer.get_position(), return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(), m_lexer.get_token_string(),
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)); out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
@@ -375,23 +366,16 @@ class parser
case token_type::parse_error: case token_type::parse_error:
{ {
// using "uninitialized" to avoid an "expected" message // using "uninitialized" to avoid an "expected" message
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::uninitialized, "value"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
} }
case token_type::end_of_input: case token_type::end_of_input:
{ {
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1)) if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
{ {
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, "attempting to parse an empty input; check that your input string or stream contains the expected JSON");
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::literal_or_value, "value"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
} }
case token_type::uninitialized: case token_type::uninitialized:
case token_type::end_array: case token_type::end_array:
@@ -401,9 +385,7 @@ class parser
case token_type::literal_or_value: case token_type::literal_or_value:
default: // the last token was unexpected default: // the last token was unexpected
{ {
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::literal_or_value, "value"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
} }
} }
} }
@@ -454,9 +436,7 @@ class parser
continue; continue;
} }
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::end_array, "array"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
} }
// states.back() is false -> object // states.back() is false -> object
@@ -473,9 +453,7 @@ class parser
// parse key // parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{ {
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
@@ -486,9 +464,7 @@ class parser
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
{ {
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::name_separator, "object separator"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
} }
// parse values // parse values
@@ -516,9 +492,7 @@ class parser
continue; continue;
} }
return sax->parse_error(m_lexer.get_position(), return syntax_error(*sax, exception_message(token_type::end_object, "object"));
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
} }
} }
@@ -535,6 +509,21 @@ class parser
return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type; return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type;
} }
/*!
@brief reports a syntax error at the current token (parse_error.101)
@param[in] sax the SAX parser to report the error to
@param[in] message the error message
@return the result of the SAX parser's parse_error()
*/
template<typename SAX>
bool syntax_error(SAX& sax, const std::string& message)
{
// MSVC 2015 reports C4100 (unreferenced parameter) when SAX::parse_error is static
static_cast<void>(sax);
return sax.parse_error(m_lexer.get_position(), m_lexer.get_token_string(),
parse_error::create(exception_id::syntax_error, m_lexer.get_position(), message, nullptr));
}
std::string exception_message(const token_type expected, const std::string& context) std::string exception_message(const token_type expected, const std::string& context)
{ {
std::string error_msg = "syntax error "; std::string error_msg = "syntax error ";
@@ -22,6 +22,10 @@ namespace detail
constexpr std::int64_t pow5_128_smallest_power = -342; constexpr std::int64_t pow5_128_smallest_power = -342;
constexpr std::int64_t pow5_128_largest_power = 308; constexpr std::int64_t pow5_128_largest_power = 308;
// every entry of pow5_128() holds two 64-bit halves of 5^q, one per covered power of 5
static_assert((pow5_128_largest_power - pow5_128_smallest_power + 1) * 2 == 1302,
"pow5_128_smallest_power/pow5_128_largest_power must match the size of the pow5_128() table");
/*! /*!
@brief 128-bit approximations of 5^q for q in [-342, 308] @brief 128-bit approximations of 5^q for q in [-342, 308]
+70 -29
View File
@@ -8,10 +8,12 @@
#pragma once #pragma once
#include <array> // array
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint64_t #include <cstdint> // uint64_t, uint8_t
#include <cstring> // memcpy #include <cstring> // memcpy
#include <nlohmann/detail/bit_ops.hpp>
#include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/macro_scope.hpp>
// Optional SIMD backend for bulk UTF-8 validation. This is an opt-in external // Optional SIMD backend for bulk UTF-8 validation. This is an opt-in external
@@ -69,18 +71,12 @@ inline std::size_t find_string_special(const unsigned char* data, std::size_t n)
std::size_t i = 0; std::size_t i = 0;
for (; i + 8 <= n; i += 8) for (; i + 8 <= n; i += 8)
{ {
std::uint64_t word = 0; const std::uint64_t special = swar_string_special(read_eight_bytes(data + i));
std::memcpy(&word, data + i, sizeof(word)); if (special != 0)
if (swar_string_special(word) != 0)
{ {
// a special byte is in this word; locate it (endian-agnostic) // the lowest flagged byte is the first special one: the borrows of
for (std::size_t j = 0; j < 8; ++j) // the subtractions can only flag bytes above a true hit
{ return i + (static_cast<std::size_t>(count_trailing_zeros(special)) / 8);
if (is_string_special(data[i + j]))
{
return i + j;
}
}
} }
} }
for (; i < n; ++i) for (; i < n; ++i)
@@ -114,8 +110,7 @@ inline std::size_t find_ascii_copyable_run(const unsigned char* data, std::size_
std::size_t i = 0; std::size_t i = 0;
for (; i + 8 <= n; i += 8) for (; i + 8 <= n; i += 8)
{ {
std::uint64_t v = 0; const std::uint64_t v = read_eight_bytes(data + i);
std::memcpy(&v, data + i, sizeof(v));
const std::uint64_t q = v ^ 0x2222222222222222ull; // '"' (0x22) const std::uint64_t q = v ^ 0x2222222222222222ull; // '"' (0x22)
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull; // '\\' (0x5C) const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull; // '\\' (0x5C)
const std::uint64_t d = v ^ 0x7F7F7F7F7F7F7F7Full; // DEL (0x7F) const std::uint64_t d = v ^ 0x7F7F7F7F7F7F7F7Full; // DEL (0x7F)
@@ -126,7 +121,9 @@ inline std::size_t find_ascii_copyable_run(const unsigned char* data, std::size_
| (v & high); // >= 0x80 | (v & high); // >= 0x80
if (stop != 0) if (stop != 0)
{ {
break; // the lowest flagged byte is the first one to stop at (see
// find_string_special())
return i + (static_cast<std::size_t>(count_trailing_zeros(stop)) / 8);
} }
} }
for (; i < n; ++i) for (; i < n; ++i)
@@ -253,12 +250,18 @@ inline std::size_t scalar_string_bulk_run(const unsigned char* data, std::size_t
{ {
break; // end of buffer, or a quote/escape/control byte break; // end of buffer, or a quote/escape/control byte
} }
const std::size_t seq = validate_one_utf8(data + pos, n - pos); // a run of multi-byte sequences (e.g. CJK text) is validated sequence
if (seq == 0) // by sequence without searching for the next special byte in between
do
{ {
break; // ill-formed or truncated: let the byte path diagnose it const std::size_t seq = validate_one_utf8(data + pos, n - pos);
if (seq == 0)
{
return pos; // ill-formed or truncated: let the byte path diagnose it
}
pos += seq;
} }
pos += seq; while (pos < n && data[pos] >= 0x80u);
} }
return pos; return pos;
} }
@@ -273,8 +276,7 @@ inline std::size_t find_string_delimiter(const unsigned char* data, std::size_t
std::size_t i = 0; std::size_t i = 0;
for (; i + 8 <= n; i += 8) for (; i + 8 <= n; i += 8)
{ {
std::uint64_t v = 0; const std::uint64_t v = read_eight_bytes(data + i);
std::memcpy(&v, data + i, sizeof(v));
const std::uint64_t q = v ^ 0x2222222222222222ull; const std::uint64_t q = v ^ 0x2222222222222222ull;
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull; const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull;
const std::uint64_t hit = ((q - ones) & ~q & high) const std::uint64_t hit = ((q - ones) & ~q & high)
@@ -282,14 +284,8 @@ inline std::size_t find_string_delimiter(const unsigned char* data, std::size_t
| ((v - 0x2020202020202020ull) & ~v & high); | ((v - 0x2020202020202020ull) & ~v & high);
if (hit != 0) if (hit != 0)
{ {
for (std::size_t j = 0; j < 8; ++j) // the lowest flagged byte is the first delimiter (see find_string_special())
{ return i + (static_cast<std::size_t>(count_trailing_zeros(hit)) / 8);
const unsigned char c = data[i + j];
if (c == '\"' || c == '\\' || c < 0x20u)
{
return i + j;
}
}
} }
} }
for (; i < n; ++i) for (; i < n; ++i)
@@ -320,5 +316,50 @@ inline std::size_t string_bulk_run(const unsigned char* data, std::size_t n) noe
return scalar_string_bulk_run(data, n); return scalar_string_bulk_run(data, n);
} }
// Decode the 4 hex digits at [data, data+4) - the digits following a `\u`
// escape - into a codepoint 0x0000..0xFFFF via one table lookup per byte
// (after yyjson's read_hex_u16), or return -1 if any of the 4 bytes is not a
// hex digit ('0'..'9', 'A'..'F', 'a'..'f'). The caller must already have
// checked that 4 bytes are available; used by lexer::get_codepoint()'s
// contiguous fast path. On -1 it falls back to the byte-at-a-time loop, which
// stops at the first invalid digit, so the reported error and position are
// unaffected by this fast path.
inline int hex_codepoint(const unsigned char* data) noexcept
{
static const std::array<std::uint8_t, 256> hex_digit_table = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
{
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 00..0F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 10..1F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 20..2F
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 30..3F ('0'..'9')
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 40..4F ('A'..'F')
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 50..5F
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 60..6F ('a'..'f')
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 70..7F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 80..8F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 90..9F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0..AF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // B0..BF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // C0..CF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // D0..DF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E0..EF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // F0..FF
}
};
const std::uint8_t d0 = hex_digit_table[data[0]];
const std::uint8_t d1 = hex_digit_table[data[1]];
const std::uint8_t d2 = hex_digit_table[data[2]];
const std::uint8_t d3 = hex_digit_table[data[3]];
// every valid digit is <= 0xF; the combined OR only exceeds it if at
// least one of the four bytes was not a hex digit (looked up as 0xFF)
if ((d0 | d1 | d2 | d3) > 0x0F)
{
return -1;
}
return (d0 << 12) | (d1 << 8) | (d2 << 4) | d3;
}
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
+18 -12
View File
@@ -279,6 +279,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
} }
} }
/// @throw invalid_iterator.214 always
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
{
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
}
public: public:
/*! /*!
@brief return a reference to the value pointed to by the iterator @brief return a reference to the value pointed to by the iterator
@@ -303,7 +309,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
} }
case value_t::null: case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); throw_cannot_get_value();
case value_t::string: case value_t::string:
case value_t::boolean: case value_t::boolean:
@@ -319,7 +325,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object; return *m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); throw_cannot_get_value();
} }
} }
} }
@@ -361,7 +367,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_object; return m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); throw_cannot_get_value();
} }
} }
} }
@@ -478,7 +484,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
} }
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -527,7 +533,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
} }
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -540,7 +546,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object)); JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object));
case value_t::array: case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator); return (m_it.array_iterator < other.m_it.array_iterator);
@@ -596,7 +602,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
case value_t::array: case value_t::array:
{ {
@@ -675,7 +681,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
case value_t::array: case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator; return m_it.array_iterator - other.m_it.array_iterator;
@@ -704,13 +710,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object)); JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object));
case value_t::array: case value_t::array:
return *std::next(m_it.array_iterator, n); return *std::next(m_it.array_iterator, n);
case value_t::null: case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); throw_cannot_get_value();
case value_t::string: case value_t::string:
case value_t::boolean: case value_t::boolean:
@@ -726,7 +732,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object; return *m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); throw_cannot_get_value();
} }
} }
} }
@@ -744,7 +750,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_it.object_iterator->first; return m_it.object_iterator->first;
} }
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object)); JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object));
} }
/*! /*!
+42 -25
View File
@@ -166,7 +166,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); throw_no_parent();
} }
reference_tokens.erase(reference_tokens.begin()); reference_tokens.erase(reference_tokens.begin());
@@ -178,7 +178,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); throw_no_parent();
} }
return reference_tokens.front(); return reference_tokens.front();
@@ -204,7 +204,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); throw_no_parent();
} }
reference_tokens.pop_back(); reference_tokens.pop_back();
@@ -216,7 +216,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); throw_no_parent();
} }
return reference_tokens.back(); return reference_tokens.back();
@@ -244,6 +244,21 @@ class json_pointer
} }
private: private:
/// @throw out_of_range.405 always
JSON_HEDLEY_NO_RETURN static void throw_no_parent()
{
JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr));
}
/// @throw out_of_range.404 always
template<typename BasicJsonContext>
JSON_HEDLEY_NO_RETURN static void throw_unresolved(const string_t& reference_token, BasicJsonContext context)
{
static_cast<void>(reference_token); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(context);
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_unresolved, detail::concat("unresolved reference token '", reference_token, "'"), context));
}
/*! /*!
@brief result of @ref parse_array_index @brief result of @ref parse_array_index
@@ -255,7 +270,7 @@ class json_pointer
{ {
ok, ///< @a s is a valid, representable array index ok, ///< @a s is a valid, representable array index
leading_zero, ///< @a s begins with '0' but has more than one character leading_zero, ///< @a s begins with '0' but has more than one character
not_a_number, ///< @a s does not begin with a digit not_a_number, ///< @a s is neither empty nor "-" and does not begin with a digit
unresolved, ///< @a s could not be converted to an integer unresolved, ///< @a s could not be converted to an integer
exceeds_size_type ///< @a s converts to an integer that exceeds size_type exceeds_size_type ///< @a s converts to an integer that exceeds size_type
}; };
@@ -282,8 +297,10 @@ class json_pointer
return array_index_status::leading_zero; return array_index_status::leading_zero;
} }
// error condition (cf. RFC 6901, Sect. 4) // error condition (cf. RFC 6901, Sect. 4); this also covers single-
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) // character tokens, so "/x" and "/xy" fail alike; "-" and the empty
// token are left to the conversion below and are reported as unresolved
if (JSON_HEDLEY_UNLIKELY(!s.empty() && s != "-" && !(s[0] >= '0' && s[0] <= '9')))
{ {
return array_index_status::not_a_number; return array_index_status::not_a_number;
} }
@@ -329,13 +346,13 @@ class json_pointer
// the branches differ in their messages, not after JSON_THROW's expansion // the branches differ in their messages, not after JSON_THROW's expansion
// NOLINTNEXTLINE(bugprone-branch-clone) // NOLINTNEXTLINE(bugprone-branch-clone)
case array_index_status::leading_zero: case array_index_status::leading_zero:
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
case array_index_status::not_a_number: case array_index_status::not_a_number:
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
case array_index_status::unresolved: case array_index_status::unresolved:
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); throw_unresolved(s, nullptr);
case array_index_status::exceeds_size_type: case array_index_status::exceeds_size_type:
JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); JSON_THROW(detail::out_of_range::create(detail::exception_id::value_out_of_range, detail::concat("array index ", s, " exceeds size_type"), nullptr));
case array_index_status::ok: case array_index_status::ok:
default: default:
break; break;
@@ -349,7 +366,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); throw_no_parent();
} }
json_pointer result = *this; json_pointer result = *this;
@@ -486,7 +503,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_invalid_value, "invalid value to unflatten", &j));
} }
id = tree.find_child(id, reference_token); id = tree.find_child(id, reference_token);
@@ -569,7 +586,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); throw_unresolved(reference_token, ptr);
} }
} }
@@ -601,7 +618,7 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{ {
// "-" always fails the range check // "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402, detail::concat( JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
") is out of range"), ptr)); ") is out of range"), ptr));
} }
@@ -610,7 +627,7 @@ class json_pointer
// Bounds check before access to avoid exception with JSON_NOEXCEPTION // Bounds check before access to avoid exception with JSON_NOEXCEPTION
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
{ {
JSON_THROW(detail::out_of_range::create(401, detail::concat( JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat(
"array index ", std::to_string(idx), " is out of range"), ptr)); "array index ", std::to_string(idx), " is out of range"), ptr));
} }
ptr = &ptr->operator[](idx); ptr = &ptr->operator[](idx);
@@ -626,7 +643,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); throw_unresolved(reference_token, ptr);
} }
} }
@@ -670,7 +687,7 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{ {
// "-" cannot be used for const access // "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access; the const operator[] // use unchecked array access; the const operator[]
@@ -688,7 +705,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); throw_unresolved(reference_token, ptr);
} }
} }
@@ -743,9 +760,9 @@ class json_pointer
// the branches differ in their messages, not after JSON_THROW's expansion // the branches differ in their messages, not after JSON_THROW's expansion
// NOLINTNEXTLINE(bugprone-branch-clone) // NOLINTNEXTLINE(bugprone-branch-clone)
case array_index_status::leading_zero: case array_index_status::leading_zero:
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr)); JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
case array_index_status::not_a_number: case array_index_status::not_a_number:
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr)); JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
case array_index_status::unresolved: case array_index_status::unresolved:
case array_index_status::exceeds_size_type: case array_index_status::exceeds_size_type:
return nullptr; return nullptr;
@@ -872,7 +889,7 @@ class json_pointer
// check if a nonempty reference string begins with slash // check if a nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{ {
JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
} }
// extract the reference tokens: // extract the reference tokens:
@@ -908,7 +925,7 @@ class json_pointer
(reference_token[pos + 1] != '0' && (reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1'))) reference_token[pos + 1] != '1')))
{ {
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
} }
} }
@@ -1072,7 +1089,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(!value.is_object())) if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{ {
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_not_object, "only objects can be unflattened", &value));
} }
BasicJsonType result; BasicJsonType result;
@@ -1096,7 +1113,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{ {
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_value_not_primitive, "values in object must be primitive", &element.second));
} }
// Assign the value to the reference pointed to by JSON pointer. Note // Assign the value to the reference pointed to by JSON pointer. Note
+2 -2
View File
@@ -312,7 +312,7 @@
return ej_pair.first == e; \ return ej_pair.first == e; \
}); \ }); \
if (it != std::end(m)) j = it->second; \ if (it != std::end(m)) j = it->second; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \ else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range,"enum value out of range for " #ENUM_TYPE, nullptr)); \
} \ } \
template<typename BasicJsonType> \ template<typename BasicJsonType> \
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
@@ -327,7 +327,7 @@
return ej_pair.second == j; \ return ej_pair.second == j; \
}); \ }); \
if (it != std::end(m)) e = it->first; \ if (it != std::end(m)) e = it->first; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \ else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
} }
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They // Ugly macros to avoid uglier copy-paste when specializing basic_json. They
@@ -172,6 +172,10 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
template<typename T> template<typename T>
using detect_key_compare = typename T::key_compare; using detect_key_compare = typename T::key_compare;
// detects whether two values of type T can be compared with operator==
template<typename T>
using detect_equal_comparable = decltype(static_cast<bool>(std::declval<const T&>() == std::declval<const T&>()));
// obtains the actual object key comparator: object_t::key_compare if the // obtains the actual object key comparator: object_t::key_compare if the
// object type defines it, and default_object_comparator_t otherwise // object type defines it, and default_object_comparator_t otherwise
// //
+174 -40
View File
@@ -157,7 +157,7 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
default: default:
{ {
JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j)); JSON_THROW(type_error::create(exception_id::type_not_serializable, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
} }
} }
} }
@@ -313,12 +313,6 @@ class binary_writer
case value_t::object: case value_t::object:
{ {
static_assert(
std::is_convertible <
typename BasicJsonType::object_t::key_type,
string_t >::value,
"object_t::key_type must be implicitly convertible to string_t");
// step 1: write control byte and the object size // step 1: write control byte and the object size
write_cbor_head(0xA0, j.m_data.m_value.object->size()); write_cbor_head(0xA0, j.m_data.m_value.object->size());
@@ -326,7 +320,7 @@ class binary_writer
{ {
// el.first is written directly (not via a temporary // el.first is written directly (not via a temporary
// basic_json), with the object as diagnostics context // basic_json), with the object as diagnostics context
write_cbor_string(el.first, j); write_cbor_key(el.first, j);
write_cbor(el.second, depth + 1); write_cbor(el.second, depth + 1);
} }
break; break;
@@ -348,7 +342,7 @@ class binary_writer
{ {
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length)))
{ {
JSON_THROW(out_of_range::create(412, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j)); JSON_THROW(out_of_range::create(exception_id::length_too_large, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j));
} }
static_cast<void>(j); static_cast<void>(j);
@@ -575,7 +569,7 @@ class binary_writer
{ {
if (JSON_HEDLEY_UNLIKELY(j.m_data.m_value.binary->subtype() > (std::numeric_limits<std::uint8_t>::max)())) if (JSON_HEDLEY_UNLIKELY(j.m_data.m_value.binary->subtype() > (std::numeric_limits<std::uint8_t>::max)()))
{ {
JSON_THROW(out_of_range::create(415, concat("subtype ", std::to_string(j.m_data.m_value.binary->subtype()), " is too large for the MessagePack ext type (max 255)"), &j)); JSON_THROW(out_of_range::create(exception_id::subtype_out_of_range, concat("subtype ", std::to_string(j.m_data.m_value.binary->subtype()), " is too large for the MessagePack ext type (max 255)"), &j));
} }
write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype())); write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype()));
@@ -591,12 +585,6 @@ class binary_writer
case value_t::object: case value_t::object:
{ {
static_assert(
std::is_convertible <
typename BasicJsonType::object_t::key_type,
string_t >::value,
"object_t::key_type must be implicitly convertible to string_t");
// step 1: write control byte and the object size // step 1: write control byte and the object size
write_msgpack_object_prefix(j.m_data.m_value.object->size(), j); write_msgpack_object_prefix(j.m_data.m_value.object->size(), j);
@@ -604,7 +592,7 @@ class binary_writer
{ {
// as in write_cbor, el.first is written directly with the // as in write_cbor, el.first is written directly with the
// object as diagnostics context // object as diagnostics context
write_msgpack_string(el.first, j); write_msgpack_key(el.first, j);
write_msgpack(el.second, depth + 1); write_msgpack(el.second, depth + 1);
} }
break; break;
@@ -730,7 +718,7 @@ class binary_writer
{ {
if (!use_count) if (!use_count)
{ {
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j)); JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j));
} }
oa.write_character(to_char_type('$')); oa.write_character(to_char_type('$'));
oa.write_character(bjdata_draft3 ? 'B' : 'U'); oa.write_character(bjdata_draft3 ? 'B' : 'U');
@@ -809,6 +797,8 @@ class binary_writer
/*! /*!
@param[in] j JSON value to serialize @param[in] j JSON value to serialize
@throw type_error.321 if @a j or a value nested in it is discarded
*/ */
void write_bon8(const BasicJsonType& j) void write_bon8(const BasicJsonType& j)
{ {
@@ -831,7 +821,7 @@ class binary_writer
{ {
static_cast<void>(j); // unused when JSON_NOEXCEPTION is defined static_cast<void>(j); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(format_name); static_cast<void>(format_name);
JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j)); JSON_THROW(type_error::create(exception_id::discarded_value_used, concat("cannot serialize discarded value to ", format_name), &j));
} }
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j) void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
@@ -878,9 +868,9 @@ class binary_writer
} }
} }
/// @brief a CBOR or MessagePack array or object whose elements /// @brief a CBOR, MessagePack, or BON8 array or object whose elements
/// @ref write_cbor_iterative or @ref write_msgpack_iterative is /// @ref write_cbor_iterative, @ref write_msgpack_iterative, or
/// still writing /// @ref write_bon8_iterative is still writing
struct binary_container_frame struct binary_container_frame
{ {
explicit binary_container_frame(const BasicJsonType* value_) noexcept explicit binary_container_frame(const BasicJsonType* value_) noexcept
@@ -992,7 +982,7 @@ class binary_writer
// the key is written directly (not via a temporary basic_json), // the key is written directly (not via a temporary basic_json),
// with the object as diagnostics context, as in write_cbor // with the object as diagnostics context, as in write_cbor
write_cbor_string(current.object_it->first, *current.value); write_cbor_key(current.object_it->first, *current.value);
const BasicJsonType* child = &(current.object_it->second); const BasicJsonType* child = &(current.object_it->second);
++stack.back().object_it; ++stack.back().object_it;
write_cbor_value_or_push(*child, stack); write_cbor_value_or_push(*child, stack);
@@ -1069,7 +1059,7 @@ class binary_writer
// as in write_cbor_iterative, the key is written directly with // as in write_cbor_iterative, the key is written directly with
// the object as diagnostics context // the object as diagnostics context
write_msgpack_string(current.object_it->first, *current.value); write_msgpack_key(current.object_it->first, *current.value);
const BasicJsonType* child = &(current.object_it->second); const BasicJsonType* child = &(current.object_it->second);
++stack.back().object_it; ++stack.back().object_it;
write_msgpack_value_or_push(*child, stack); write_msgpack_value_or_push(*child, stack);
@@ -1091,7 +1081,7 @@ class binary_writer
{ {
if (!use_count) if (!use_count)
{ {
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j)); JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j));
} }
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
const bool same_prefix = std::all_of(j.begin() + 1, j.end(), const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
@@ -1141,7 +1131,7 @@ class binary_writer
{ {
if (!use_count) if (!use_count)
{ {
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j)); JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j));
} }
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
const bool same_prefix = std::all_of(j.begin(), j.end(), const bool same_prefix = std::all_of(j.begin(), j.end(),
@@ -1358,7 +1348,7 @@ class binary_writer
const auto it = name.find(static_cast<typename string_t::value_type>(0)); const auto it = name.find(static_cast<typename string_t::value_type>(0));
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
{ {
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j)); JSON_THROW(out_of_range::create(exception_id::bson_key_with_null, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
} }
string_t storage; string_t storage;
@@ -1376,7 +1366,7 @@ class binary_writer
{ {
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
{ {
JSON_THROW(out_of_range::create(412, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr)); JSON_THROW(out_of_range::create(exception_id::length_too_large, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr));
} }
return static_cast<std::int32_t>(size); return static_cast<std::int32_t>(size);
@@ -1562,7 +1552,7 @@ class binary_writer
if (value.has_subtype() && JSON_HEDLEY_UNLIKELY(value.subtype() > (std::numeric_limits<std::uint8_t>::max)())) if (value.has_subtype() && JSON_HEDLEY_UNLIKELY(value.subtype() > (std::numeric_limits<std::uint8_t>::max)()))
{ {
JSON_THROW(out_of_range::create(415, concat("subtype ", std::to_string(value.subtype()), " is too large for the BSON binary subtype (max 255)"), &j)); JSON_THROW(out_of_range::create(exception_id::subtype_out_of_range, concat("subtype ", std::to_string(value.subtype()), " is too large for the BSON binary subtype (max 255)"), &j));
} }
return sizeof(std::int32_t) + value.size() + 1ul; return sizeof(std::int32_t) + value.size() + 1ul;
@@ -1674,6 +1664,21 @@ class binary_writer
} }
} }
/*!
@brief Writes the head of a BSON element with key @a name whose value is
the object or array @a j: its type, key, and @a size
Called while the key is in scope: an object key is a key_type, which
may only convert to a temporary string_t, so it cannot be kept by
pointer until after the loop over the entries.
*/
void write_bson_nested_head(const string_t& name, const BasicJsonType& j,
const std::size_t size)
{
write_bson_entry_header(name, j.is_object() ? 0x03 : 0x04);
write_number<std::int32_t>(to_bson_length(size), true);
}
/// @brief an object or array of the BSON document being sized or written /// @brief an object or array of the BSON document being sized or written
struct bson_frame struct bson_frame
{ {
@@ -1838,7 +1843,6 @@ class binary_writer
{ {
// write entries until the current object or array is done, or an // write entries until the current object or array is done, or an
// entry is an object or array itself // entry is an object or array itself
const string_t* nested_name = nullptr;
const BasicJsonType* nested = nullptr; const BasicJsonType* nested = nullptr;
if (current.value->is_object()) if (current.value->is_object())
{ {
@@ -1849,7 +1853,7 @@ class binary_writer
++current.member; ++current.member;
if (el.second.is_structured()) if (el.second.is_structured())
{ {
nested_name = &el.first; write_bson_nested_head(el.first, el.second, nested_sizes[next_size++]);
nested = &el.second; nested = &el.second;
} }
else else
@@ -1868,7 +1872,7 @@ class binary_writer
++current.index; ++current.index;
if (el.is_structured()) if (el.is_structured())
{ {
nested_name = &index_name; write_bson_nested_head(index_name, el, nested_sizes[next_size++]);
nested = &el; nested = &el;
} }
else else
@@ -1880,8 +1884,6 @@ class binary_writer
if (nested != nullptr) if (nested != nullptr)
{ {
write_bson_entry_header(*nested_name, nested->is_object() ? 0x03 : 0x04);
write_number<std::int32_t>(to_bson_length(nested_sizes[next_size++]), true);
parents.push_back(std::move(current)); parents.push_back(std::move(current));
current = bson_frame(nested); current = bson_frame(nested);
continue; continue;
@@ -1949,6 +1951,43 @@ class binary_writer
} }
} }
/*!
@brief write an object key as a CBOR text string
A key convertible to string_t is written directly. Other key types (only
an explicit conversion, or only a to_json overload) go through a temporary
basic_json, as in version 3.12.0; the temporary is then the diagnostics
context for strict UTF-8 checks.
*/
template<typename Key = typename BasicJsonType::object_t::key_type,
enable_if_t<std::is_convertible<Key, string_t>::value, int> = 0>
void write_cbor_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context)
{
write_cbor_string(key, context);
}
template < typename Key = typename BasicJsonType::object_t::key_type,
enable_if_t < !std::is_convertible<Key, string_t>::value, int > = 0 >
void write_cbor_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/)
{
write_cbor(BasicJsonType(key));
}
/// @brief write an object key as a MessagePack str, as in @ref write_cbor_key
template<typename Key = typename BasicJsonType::object_t::key_type,
enable_if_t<std::is_convertible<Key, string_t>::value, int> = 0>
void write_msgpack_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context)
{
write_msgpack_string(key, context);
}
template < typename Key = typename BasicJsonType::object_t::key_type,
enable_if_t < !std::is_convertible<Key, string_t>::value, int > = 0 >
void write_msgpack_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/)
{
write_msgpack(BasicJsonType(key));
}
/*! /*!
@brief write a CBOR text string @brief write a CBOR text string
@@ -2570,9 +2609,27 @@ class binary_writer
@param[in] j JSON value to serialize @param[in] j JSON value to serialize
@param[in,out] string_open whether the output ends with a non-empty @param[in,out] string_open whether the output ends with a non-empty
string that has not been terminated with 0xFF string that has not been terminated with 0xFF
@param[in] depth nesting level of @a j, counted from the
top-level value passed to @ref basic_json::to_bon8
@throw type_error.316 if a string value or an object key is not valid
UTF-8
@throw out_of_range.407 if an unsigned integer does not fit int64
@throw type_error.321 if @a j or a value nested in it is discarded
Values nested deeper than @ref recursion_depth_limit are written by
@ref write_bon8_iterative without the call stack.
@sa @ref write_cbor
@sa https://github.com/nlohmann/json/issues/5392
*/ */
void write_bon8_value(const BasicJsonType& j, bool& string_open) void write_bon8_value(const BasicJsonType& j, bool& string_open, const std::size_t depth = 0)
{ {
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()) && (j.is_array() || j.is_object()))
{
write_bon8_iterative(j, string_open);
return;
}
switch (j.type()) switch (j.type())
{ {
case value_t::null: case value_t::null:
@@ -2591,7 +2648,7 @@ class binary_writer
{ {
if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)())) if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)()))
{ {
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j)); JSON_THROW(out_of_range::create(exception_id::integer_too_large, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j));
} }
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned)); write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
string_open = false; string_open = false;
@@ -2626,7 +2683,7 @@ class binary_writer
for (const auto& el : *j.m_data.m_value.array) for (const auto& el : *j.m_data.m_value.array)
{ {
write_bon8_value(el, string_open); write_bon8_value(el, string_open, depth + 1);
} }
if (N > 4) if (N > 4)
@@ -2645,7 +2702,7 @@ class binary_writer
for (const auto& el : *j.m_data.m_value.object) for (const auto& el : *j.m_data.m_value.object)
{ {
write_bon8_string(el.first, string_open, j); write_bon8_string(el.first, string_open, j);
write_bon8_value(el.second, string_open); write_bon8_value(el.second, string_open, depth + 1);
} }
if (N > 4) if (N > 4)
@@ -2678,7 +2735,84 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
default: default:
break; throw_on_discarded(j, "BON8");
}
}
/*!
@brief write @a j with @ref write_bon8_value, or write its marker and push
a frame for @ref write_bon8_iterative to continue with its elements
A scalar, and an empty array or object, are written out in full and not
pushed.
@sa @ref write_cbor_value_or_push
*/
void write_bon8_value_or_push(const BasicJsonType& j, bool& string_open, std::vector<binary_container_frame>& stack)
{
if (j.is_array() || j.is_object())
{
// arrays use the markers 0x80..0x85, objects 0x86..0x8B; the last
// one stands for more than four elements, closed later with 0xFE
const auto N = j.size();
const std::size_t base = j.is_array() ? 0x80 : 0x86;
write_bon8_marker(static_cast<std::uint8_t>(base + (N <= 4 ? N : 5)), string_open);
if (N != 0)
{
stack.emplace_back(&j);
}
return;
}
write_bon8_value(j, string_open);
}
/*!
@brief write out @a root and everything below it without the call stack
A container with more than four elements is closed with 0xFE once its
last element is written, as in @ref write_bon8_value.
@sa @ref write_cbor_iterative
*/
void write_bon8_iterative(const BasicJsonType& root, bool& string_open)
{
// only a container with elements is ever pushed; see write_bon8_value_or_push
std::vector<binary_container_frame> stack;
write_bon8_value_or_push(root, string_open, stack);
while (!stack.empty())
{
const binary_container_frame current = stack.back();
const bool is_array = current.value->is_array();
const bool at_end = is_array
? current.array_it == current.value->m_data.m_value.array->cend()
: current.object_it == current.value->m_data.m_value.object->cend();
if (at_end)
{
if (current.value->size() > 4)
{
write_bon8_marker(0xFE, string_open);
}
stack.pop_back();
continue;
}
// read the child before pushing: entering it can move every frame
const BasicJsonType* child = nullptr;
if (is_array)
{
child = &(*current.array_it);
++stack.back().array_it;
}
else
{
write_bon8_string(current.object_it->first, string_open, *current.value);
child = &(current.object_it->second);
++stack.back().object_it;
}
write_bon8_value_or_push(*child, string_open, stack);
} }
} }
@@ -2744,7 +2878,7 @@ class binary_writer
const std::size_t valid = valid_utf8_prefix(data, s.size()); const std::size_t valid = valid_utf8_prefix(data, s.size());
if (JSON_HEDLEY_UNLIKELY(valid != s.size())) if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
{ {
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context)); JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context));
} }
} }
@@ -888,7 +888,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr)); JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
} }
case error_handler_t::ignore: case error_handler_t::ignore:
@@ -1021,7 +1021,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr)); JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr));
} }
case error_handler_t::ignore: case error_handler_t::ignore:
+116 -72
View File
@@ -639,7 +639,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
object = nullptr; // silence warning, see #821 object = nullptr; // silence warning, see #821
if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
{ {
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE JSON_THROW(other_error::create(detail::exception_id::internal_error, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
} }
break; break;
} }
@@ -1421,6 +1421,24 @@ public:
return create<object_t>(first, last); return create<object_t>(first, last);
} }
/// @brief compare two object keys for equality, if the key type supports it
/// @note object_t only needs operator< for its keys (std::map), so operator==
/// may not exist; the keys are then reported as different, which makes
/// copy_object_level pair the values via object_t::find()
template<typename K = typename object_t::key_type,
detail::enable_if_t<detail::is_detected<detail::detect_equal_comparable, K>::value, int> = 0>
static bool copy_keys_equal(const K& a, const K& b)
{
return a == b;
}
template < typename K = typename object_t::key_type,
detail::enable_if_t < !detail::is_detected<detail::detect_equal_comparable, K>::value, int > = 0 >
static bool copy_keys_equal(const K& /*a*/, const K& /*b*/)
{
return false;
}
/// @brief create the copy of the object @a src in @a dst /// @brief create the copy of the object @a src in @a dst
/// @note structured values are appended to @a worklist instead /// @note structured values are appended to @a worklist instead
static void copy_object_level(const basic_json& src, basic_json& dst, static void copy_object_level(const basic_json& src, basic_json& dst,
@@ -1453,7 +1471,7 @@ public:
auto src_it = src_object.cbegin(); auto src_it = src_object.cbegin();
for (auto& element : *dst.m_data.m_value.object) for (auto& element : *dst.m_data.m_value.object)
{ {
if (JSON_HEDLEY_LIKELY(src_it != src_object.cend() && src_it->first == element.first)) if (JSON_HEDLEY_LIKELY(src_it != src_object.cend() && copy_keys_equal(src_it->first, element.first)))
{ {
copy_shallow(src_it->second, element.second, worklist); copy_shallow(src_it->second, element.second, worklist);
++src_it; ++src_it;
@@ -2287,7 +2305,7 @@ public:
// if an object is wanted but impossible, throw an exception // if an object is wanted but impossible, throw an exception
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
{ {
JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr)); JSON_THROW(type_error::create(detail::exception_id::object_from_non_pairs, "cannot create object from initializer list", nullptr));
} }
} }
@@ -2407,7 +2425,7 @@ public:
// make sure the iterator fits the current value // make sure the iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterators_incompatible, "iterators are not compatible", nullptr));
} }
// copy type from the first iterator // copy type from the first iterator
@@ -2426,7 +2444,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end())) || !last.m_it.primitive_iterator.is_end()))
{ {
JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object));
} }
break; break;
} }
@@ -2494,7 +2512,7 @@ public:
case value_t::null: case value_t::null:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
} }
set_parents(); set_parents();
@@ -2882,7 +2900,7 @@ public:
return *ptr; return *ptr;
} }
JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj)); JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
} }
public: public:
@@ -3266,7 +3284,7 @@ public:
{ {
if (!is_binary()) if (!is_binary())
{ {
JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); detail::throw_type_must_be("binary", *this);
} }
return *get_ptr<binary_t*>(); return *get_ptr<binary_t*>();
@@ -3278,7 +3296,7 @@ public:
{ {
if (!is_binary()) if (!is_binary())
{ {
JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); detail::throw_type_must_be("binary", *this);
} }
return *get_ptr<const binary_t*>(); return *get_ptr<const binary_t*>();
@@ -3320,7 +3338,7 @@ public:
// at only works for objects // at only works for objects
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", j.type_name()), &j)); detail::throw_cannot_use_with(detail::exception_id::at_wrong_type, "at()", j);
} }
auto it = object_lookup(j, std::forward<KeyType>(key)); auto it = object_lookup(j, std::forward<KeyType>(key));
@@ -3330,11 +3348,28 @@ public:
// std::map or ordered_map) never moves from its argument, so key is still // std::map or ordered_map) never moves from its argument, so key is still
// valid here regardless of whether KeyType was deduced as an rvalue reference // valid here regardless of whether KeyType was deduced as an rvalue reference
// NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved) // NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved)
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), &j)); JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key_for_message(key), "' not found"), &j));
} }
return it->second; return it->second;
} }
/// @brief key as it is passed to detail::concat for an error message
/// @note keys with data() and size() (such as string_t itself or a string
/// view) are passed through unchanged, so a miss does not copy them;
/// other keys (such as string literals or key types that only convert
/// to string_t) are converted to string_t
template < typename KeyType, detail::enable_if_t < detail::detect_string_can_append_data<string_t, KeyType>::value, int > = 0 >
static const KeyType & key_for_message(const KeyType& key)
{
return key; // NOLINT(bugprone-return-const-ref-from-parameter): the result is only passed to concat() within the full-expression that holds key
}
template < typename KeyType, detail::enable_if_t < !detail::detect_string_can_append_data<string_t, KeyType>::value, int > = 0 >
static string_t key_for_message(const KeyType& key)
{
return string_t(key);
}
/// @brief checked array element access used by the at() overloads taking an index /// @brief checked array element access used by the at() overloads taking an index
/// @throw type_error.304 if @a j is not an array /// @throw type_error.304 if @a j is not an array
/// @throw out_of_range.401 if @a idx is out of range /// @throw out_of_range.401 if @a idx is out of range
@@ -3345,12 +3380,12 @@ public:
// at only works for arrays // at only works for arrays
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", j.type_name()), &j)); detail::throw_cannot_use_with(detail::exception_id::at_wrong_type, "at()", j);
} }
if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size())) if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size()))
{ {
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &j)); JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
} }
return (*j.m_data.m_value.array)[idx]; return (*j.m_data.m_value.array)[idx];
@@ -3383,6 +3418,15 @@ public:
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
} }
/// @brief throws because operator[] is not supported for the type of this value
/// @param[in] argument the kind of the operator's argument, "numeric" or "string"
/// @throw type_error.305 always
JSON_HEDLEY_NO_RETURN void throw_subscript_wrong_type(const char* argument) const
{
detail::throw_cannot_use_with(detail::exception_id::subscript_wrong_type,
detail::concat("operator[] with a ", argument, " argument").c_str(), *this);
}
public: public:
//////////////////// ////////////////////
// element access // // element access //
@@ -3487,7 +3531,7 @@ public:
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); throw_subscript_wrong_type("numeric");
} }
/// @brief access specified array element /// @brief access specified array element
@@ -3501,7 +3545,7 @@ public:
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); throw_subscript_wrong_type("numeric");
} }
/// @brief access specified object element /// @brief access specified object element
@@ -3521,7 +3565,7 @@ public:
return set_parent(result.first->second); return set_parent(result.first->second);
} }
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); throw_subscript_wrong_type("string");
} }
/// @brief access specified object element /// @brief access specified object element
@@ -3536,7 +3580,7 @@ public:
return it->second; return it->second;
} }
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); throw_subscript_wrong_type("string");
} }
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
@@ -3572,7 +3616,7 @@ public:
return set_parent(result.first->second); return set_parent(result.first->second);
} }
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); throw_subscript_wrong_type("string");
} }
/// @brief access specified object element /// @brief access specified object element
@@ -3589,7 +3633,7 @@ public:
return it->second; return it->second;
} }
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); throw_subscript_wrong_type("string");
} }
private: private:
@@ -3613,7 +3657,7 @@ public:
// value only works for objects // value only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::value_wrong_type, "value()", *this);
} }
const auto it = find(std::forward<KeyType>(key)); const auto it = find(std::forward<KeyType>(key));
@@ -3628,7 +3672,7 @@ public:
// value only works for arrays and objects // value only works for arrays and objects
if (JSON_HEDLEY_UNLIKELY(!is_structured())) if (JSON_HEDLEY_UNLIKELY(!is_structured()))
{ {
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::value_wrong_type, "value()", *this);
} }
return ptr.get_checked_or_null(this); return ptr.get_checked_or_null(this);
@@ -3795,7 +3839,7 @@ public:
// make sure the iterator fits the current value // make sure the iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
{ {
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
} }
IteratorType result = end(); IteratorType result = end();
@@ -3811,7 +3855,7 @@ public:
{ {
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
{ {
JSON_THROW(invalid_iterator::create(205, "iterator out of range", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_out_of_range, "iterator out of range", this));
} }
m_data.m_value.destroy(m_data.m_type); m_data.m_value.destroy(m_data.m_type);
@@ -3837,7 +3881,7 @@ public:
case value_t::null: case value_t::null:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
} }
return result; return result;
@@ -3853,7 +3897,7 @@ public:
// make sure the iterator fits the current value // make sure the iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_from_other_value, "iterators do not fit current value", this));
} }
IteratorType result = end(); IteratorType result = end();
@@ -3870,7 +3914,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end())) || !last.m_it.primitive_iterator.is_end()))
{ {
JSON_THROW(invalid_iterator::create(204, "iterators out of range", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this));
} }
m_data.m_value.destroy(m_data.m_type); m_data.m_value.destroy(m_data.m_type);
@@ -3898,7 +3942,7 @@ public:
case value_t::null: case value_t::null:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
} }
return result; return result;
@@ -3912,7 +3956,7 @@ public:
// this erase only works for objects // this erase only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
} }
const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key)); const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key));
@@ -3927,7 +3971,7 @@ public:
// this erase only works for objects // this erase only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
} }
const auto it = object_lookup(*this, std::forward<KeyType>(key)); const auto it = object_lookup(*this, std::forward<KeyType>(key));
@@ -3969,14 +4013,14 @@ public:
{ {
if (JSON_HEDLEY_UNLIKELY(idx >= size())) if (JSON_HEDLEY_UNLIKELY(idx >= size()))
{ {
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
} }
m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx)); m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
} }
else else
{ {
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
} }
} }
@@ -4465,7 +4509,7 @@ public:
// push_back only works for null objects or arrays // push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{ {
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
} }
// transform a null object into an array // transform a null object into an array
@@ -4496,7 +4540,7 @@ public:
// push_back only works for null objects or arrays // push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{ {
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
} }
// transform a null object into an array // transform a null object into an array
@@ -4526,7 +4570,7 @@ public:
// push_back only works for null objects or objects // push_back only works for null objects or objects
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{ {
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
} }
// transform a null object into an object // transform a null object into an object
@@ -4580,7 +4624,7 @@ public:
// emplace_back only works for null objects or arrays // emplace_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{ {
JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::emplace_wrong_type, "emplace_back()", *this);
} }
// transform a null object into an array // transform a null object into an array
@@ -4603,7 +4647,7 @@ public:
// emplace only works for null objects or arrays // emplace only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{ {
JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::emplace_wrong_type, "emplace()", *this);
} }
// transform a null object into an object // transform a null object into an object
@@ -4655,14 +4699,14 @@ public:
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
} }
// insert to array and return iterator // insert to array and return iterator
return insert_iterator(pos, val); return insert_iterator(pos, val);
} }
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
} }
/// @brief inserts element into array /// @brief inserts element into array
@@ -4675,7 +4719,7 @@ public:
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
} }
// moving into a local first keeps this safe even if val aliases // moving into a local first keeps this safe even if val aliases
@@ -4684,7 +4728,7 @@ public:
return insert_iterator(pos, std::move(tmp)); return insert_iterator(pos, std::move(tmp));
} }
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
} }
/// @brief inserts copies of element into array /// @brief inserts copies of element into array
@@ -4697,14 +4741,14 @@ public:
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
} }
// insert to array and return iterator // insert to array and return iterator
return insert_iterator(pos, cnt, val); return insert_iterator(pos, cnt, val);
} }
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
} }
/// @brief inserts range of elements into array /// @brief inserts range of elements into array
@@ -4714,30 +4758,30 @@ public:
// insert only works for arrays // insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array())) if (JSON_HEDLEY_UNLIKELY(!is_array()))
{ {
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
} }
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
} }
// check if range iterators belong to the same JSON object // check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
} }
if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
{ {
JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_into_itself, "passed iterators may not belong to container", this));
} }
// passed iterators must belong to arrays // passed iterators must belong to arrays
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array())) if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array()))
{ {
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to arrays", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to arrays", this));
} }
// insert to array and return iterator // insert to array and return iterator
@@ -4751,13 +4795,13 @@ public:
// insert only works for arrays // insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array())) if (JSON_HEDLEY_UNLIKELY(!is_array()))
{ {
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
} }
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
} }
// copy the values first: ilist may refer to elements of this array // copy the values first: ilist may refer to elements of this array
@@ -4779,19 +4823,19 @@ public:
// insert only works for objects // insert only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
} }
// check if range iterators belong to the same JSON object // check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
} }
// passed iterators must belong to objects // passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{ {
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to objects", this));
} }
m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
@@ -4808,7 +4852,7 @@ public:
// j, not the copy made below) // j, not the copy made below)
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", j.type_name()), &j)); detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", j);
} }
// copy first: j may be *this or one of its descendants, and is // copy first: j may be *this or one of its descendants, and is
@@ -4826,13 +4870,13 @@ public:
// check if range iterators belong to the same JSON object // check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
} }
// passed iterators must belong to objects // passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{ {
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object)); detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", *first.m_object);
} }
// copy first: the range may belong to *this or one of its // copy first: the range may belong to *this or one of its
@@ -4868,7 +4912,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", *this);
} }
} }
@@ -5031,7 +5075,7 @@ public:
} }
else else
{ {
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(array_t&)", *this);
} }
} }
@@ -5048,7 +5092,7 @@ public:
} }
else else
{ {
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(object_t&)", *this);
} }
} }
@@ -5064,7 +5108,7 @@ public:
} }
else else
{ {
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(string_t&)", *this);
} }
} }
@@ -5080,7 +5124,7 @@ public:
} }
else else
{ {
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t&)", *this);
} }
} }
@@ -5096,7 +5140,7 @@ public:
} }
else else
{ {
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this)); detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t::container_type&)", *this);
} }
} }
@@ -6558,7 +6602,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
{ {
// avoid undefined behavior // avoid undefined behavior
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent)); JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
} }
// default case: insert add offset // default case: insert add offset
@@ -6577,7 +6621,7 @@ public:
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(out_of_range::create(411, detail::concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent)); JSON_THROW(out_of_range::create(detail::exception_id::patch_add_parent_not_container, detail::concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
} }
}; };
@@ -6600,7 +6644,7 @@ public:
} }
else else
{ {
JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this)); JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", last_path, "' not found"), this));
} }
} }
else if (parent.is_array()) else if (parent.is_array())
@@ -6612,7 +6656,7 @@ public:
{ {
// the parent of a "remove" target must be an object or array // the parent of a "remove" target must be an object or array
// (see #5396) // (see #5396)
JSON_THROW(out_of_range::create(413, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent)); JSON_THROW(out_of_range::create(detail::exception_id::patch_remove_parent_not_container, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
} }
}; };
@@ -6643,7 +6687,7 @@ public:
// type check: top level value must be an array // type check: top level value must be an array
if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
{ {
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch)); JSON_THROW(parse_error::create(detail::exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &json_patch));
} }
// iterate and apply the operations // iterate and apply the operations
@@ -6664,14 +6708,14 @@ public:
if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end())) if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
{ {
// NOLINTNEXTLINE(performance-inefficient-string-concatenation) // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
} }
// check if the result is of type string // check if the result is of type string
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
{ {
// NOLINTNEXTLINE(performance-inefficient-string-concatenation) // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val)); JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
} }
// no error: return value // no error: return value
@@ -6681,7 +6725,7 @@ public:
// type check: every element of the array must be an object // type check: every element of the array must be an object
if (JSON_HEDLEY_UNLIKELY(!val.is_object())) if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
{ {
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val)); JSON_THROW(parse_error::create(detail::exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &val));
} }
// collect mandatory members // collect mandatory members
@@ -6717,7 +6761,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr))) if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
{ {
JSON_THROW(out_of_range::create(414, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result)); JSON_THROW(out_of_range::create(detail::exception_id::patch_move_into_child, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result));
} }
// the "from" location must exist - use at() // the "from" location must exist - use at()
@@ -6764,7 +6808,7 @@ public:
// throw an exception if the test fails // throw an exception if the test fails
if (JSON_HEDLEY_UNLIKELY(!success)) if (JSON_HEDLEY_UNLIKELY(!success))
{ {
JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val)); JSON_THROW(other_error::create(detail::exception_id::patch_test_failed, detail::concat("unsuccessful: ", val.dump()), &val));
} }
break; break;
@@ -6775,7 +6819,7 @@ public:
{ {
// op must be "add", "remove", "replace", "move", "copy", or // op must be "add", "remove", "replace", "move", "copy", or
// "test" // "test"
JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val)); JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat("operation value '", op, "' is invalid"), &val));
} }
} }
} }
+25 -24
View File
@@ -90,6 +90,19 @@ private:
return self.end(); return self.end();
} }
/// @brief shared implementation of the const and non-const at() overloads
/// @throw std::out_of_range if @a key is not found
template<typename Self, typename KeyType>
static auto at_impl(Self& self, const KeyType& key) -> decltype((self.begin()->second))
{
const auto it = find_impl(self, key);
if (it == self.end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
}
/// @brief remove the entry @a it points to, preserving order /// @brief remove the entry @a it points to, preserving order
/// @note keys are not movable, so the tail is destroyed and re-constructed in place /// @note keys are not movable, so the tail is destroyed and re-constructed in place
void erase_at(iterator it) void erase_at(iterator it)
@@ -156,46 +169,26 @@ public:
T& at(const key_type& key) T& at(const key_type& key)
{ {
const auto it = find_impl(*this, key); return at_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
{ {
const auto it = find_impl(*this, key); return at_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
const T& at(const key_type& key) const const T& at(const key_type& key) const
{ {
const auto it = find_impl(*this, key); return at_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
{ {
const auto it = find_impl(*this, key); return at_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
size_type erase(const key_type& key) size_type erase(const key_type& key)
@@ -247,6 +240,14 @@ public:
// ^ ^ // ^ ^
// first last // first last
// Note on conformance: before C++20, [basic.life]/8 did not allow an
// object of a type with a const member (like value_type's const Key)
// to transparently replace the destroyed one, so strictly, accessing
// it through the vector's existing pointers would have required
// std::launder (which does not exist before C++17). C++20 dropped that
// condition (P1971R0, NB comment US 041). Compilers have always treated
// this pattern as intended, so it is kept deliberately.
// Since we cannot move const Keys, we re-construct them in place. // Since we cannot move const Keys, we re-construct them in place.
// We start at first and re-construct (viz. copy) the elements from // We start at first and re-construct (viz. copy) the elements from
// the back of the vector. Example for the first iteration: // the back of the vector. Example for the first iteration:
+1631 -877
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More