diff --git a/.clang-tidy b/.clang-tidy index a16136785..19c7dc642 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,3 +1,33 @@ +# Style or vendor rule sets that do not fit a header-only library: +# altera-*, fuchsia-*, llvmlibc-*, android-cloexec-fopen, boost-use-ranges, llvm-header-guard, +# llvm-include-order, llvm-prefer-static-over-anonymous-namespace, llvm-use-ranges, +# misc-use-anonymous-namespace, google-runtime-float, google-runtime-int, hicpp-signed-bitwise, +# bugprone-signed-bitwise, hicpp-explicit-conversions, google-explicit-constructor, +# cppcoreguidelines-explicit-constructor, misc-explicit-constructor (implicit conversions are the +# API), readability-trailing-comma, +# hicpp-avoid-goto / cppcoreguidelines-avoid-goto, hicpp-no-array-decay, +# hicpp-uppercase-literal-suffix, readability-uppercase-literal-suffix, modernize-use-trailing-return-type. +# Constructs the library needs on purpose (the library supports C++11 and legacy compilers): +# cppcoreguidelines-macro-usage, cppcoreguidelines-avoid-do-while (macros), +# cppcoreguidelines-pro-bounds-*, cppcoreguidelines-pro-type-reinterpret-cast, +# cppcoreguidelines-pro-type-union-access (parsers, serializers, basic_json's value union), +# cppcoreguidelines-avoid-const-or-ref-data-members, cppcoreguidelines-rvalue-reference-param-not-moved, +# cppcoreguidelines-avoid-non-const-global-variables, misc-non-private-member-variables-in-classes, +# misc-no-recursion (value walks are recursive, bounded by detail::recursion_depth_limit()), +# modernize-type-traits, modernize-use-constraints, modernize-use-ranges, modernize-use-std-numbers, +# modernize-use-designated-initializers, modernize-use-nodiscard, modernize-concat-nested-namespaces, +# readability-redundant-typename, readability-redundant-parentheses, +# readability-redundant-access-specifiers, readability-simplify-boolean-expr, +# readability-use-concise-preprocessor-directives, modernize-use-string-view, +# modernize-use-integer-sign-comparison (needs std::cmp_less; the flagged +# static_cast(end - pos) < n comparisons are deliberate) +# (all need a newer standard or compiler than C++11, or would change macro-generated or +# deliberately explicit code). +# Too noisy to be useful (numeric parsing/formatting code, doctest, tests): +# bugprone-easily-swappable-parameters, cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, +# readability-identifier-length, readability-function-cognitive-complexity, performance-enum-size, +# concurrency-mt-unsafe, misc-confusable-identifiers, misc-include-cleaner (run on its own for +# the consumer headers in cmake/ci.cmake). # portability-avoid-pragma-once: kept disabled on purpose. #pragma once is accepted # by every supported compiler, and tools/amalgamate/amalgamate.py strips it from # single_include, so there is nothing left to fix here. @@ -10,14 +40,14 @@ Checks: '*, -android-cloexec-fopen, -boost-use-ranges, -bugprone-easily-swappable-parameters, - -cert-err58-cpp, - -clang-analyzer-webkit.NoUncountedMemberChecker, + -bugprone-signed-bitwise, -concurrency-mt-unsafe, -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-explicit-constructor, -cppcoreguidelines-macro-usage, -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, @@ -26,19 +56,15 @@ Checks: '*, -cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-rvalue-reference-param-not-moved, - -cppcoreguidelines-virtual-class-destructor, -fuchsia-default-arguments-calls, -fuchsia-default-arguments-declarations, -fuchsia-overloaded-operator, -google-explicit-constructor, - -google-readability-function-size, -google-runtime-float, -google-runtime-int, -hicpp-avoid-goto, -hicpp-explicit-conversions, - -hicpp-function-size, -hicpp-no-array-decay, - -hicpp-no-assembler, -hicpp-signed-bitwise, -hicpp-uppercase-literal-suffix, -llvm-header-guard, @@ -48,6 +74,7 @@ Checks: '*, -llvmlibc-*, -misc-use-anonymous-namespace, -misc-confusable-identifiers, + -misc-explicit-constructor, -misc-include-cleaner, -misc-no-recursion, -misc-non-private-member-variables-in-classes, @@ -55,20 +82,22 @@ Checks: '*, -modernize-type-traits, -modernize-use-constraints, -modernize-use-designated-initializers, + -modernize-use-integer-sign-comparison, -modernize-use-nodiscard, -modernize-use-ranges, + -modernize-use-string-view, -modernize-use-std-numbers, -modernize-use-trailing-return-type, -performance-enum-size, -portability-avoid-pragma-once, -readability-function-cognitive-complexity, - -readability-function-size, -readability-identifier-length, -readability-magic-numbers, -readability-redundant-access-specifiers, -readability-redundant-parentheses, -readability-redundant-typename, -readability-simplify-boolean-expr, + -readability-trailing-comma, -readability-uppercase-literal-suffix, -readability-use-concise-preprocessor-directives' diff --git a/.github/workflows/flawfinder.yml b/.github/workflows/flawfinder.yml index e8cb9f71f..2aebc6306 100644 --- a/.github/workflows/flawfinder.yml +++ b/.github/workflows/flawfinder.yml @@ -43,7 +43,8 @@ jobs: - name: flawfinder_scan uses: david-a-wheeler/flawfinder@c4216b74cf2639ffa98503768bd6e4299b5440c9 # v2.0.20 with: - arguments: '--sarif ./' + # single_include/ is omitted: it is generated from include/ and would report every finding twice + arguments: '--sarif ./cmake ./docs ./include ./src ./tests ./tools' output: 'flawfinder_results.sarif' - name: Upload analysis results to GitHub Security tab diff --git a/README.md b/README.md index c38e7e12e..87c70fc96 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ The `json` class provides an API for manipulating a JSON value. To create a `jso ```cpp #include #include -using json = nlohmann::json; +using nlohmann::json; // ... @@ -164,7 +164,7 @@ If using modules (enabled with `NLOHMANN_JSON_BUILD_MODULES`), this example beco import std; import nlohmann.json; -using json = nlohmann::json; +using nlohmann::json; // ... @@ -842,7 +842,7 @@ assert(p == p2); To make this work with one of your types, you only need to provide two functions: ```cpp -using json = nlohmann::json; +using nlohmann::json; namespace ns { void to_json(json& j, const person& p) { @@ -1257,7 +1257,7 @@ See the page [quality assurance](https://json.nlohmann.me/community/quality_assu #include // for convenience -using json = nlohmann::json; +using nlohmann::json; ``` to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang). diff --git a/docs/mkdocs/docs/api/adl_serializer/index.md b/docs/mkdocs/docs/api/adl_serializer/index.md index e83b66dec..e994a30cf 100644 --- a/docs/mkdocs/docs/api/adl_serializer/index.md +++ b/docs/mkdocs/docs/api/adl_serializer/index.md @@ -30,6 +30,14 @@ struct adl_serializer { - [**from_json**](from_json.md) - convert a JSON value to any value type - [**to_json**](to_json.md) - convert any value type to a JSON value +## See also + +- [Arbitrary type conversions](../../features/arbitrary_types.md) - how to make your types convertible +- [**get**](../basic_json/get.md) / [**get_to**](../basic_json/get_to.md) - convert a JSON value to a C++ type +- [**basic_json**](../basic_json/basic_json.md) - construct a JSON value from a C++ value +- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE**](../macros/nlohmann_define_type_intrusive.md) - generate the conversion functions for a type +- [Specializing enum conversion](../../features/enum_conversion.md) + ## Version history - Added in version 2.1.0. diff --git a/docs/mkdocs/docs/api/basic_json/accept.md b/docs/mkdocs/docs/api/basic_json/accept.md index 41c6969a1..345d0d507 100644 --- a/docs/mkdocs/docs/api/basic_json/accept.md +++ b/docs/mkdocs/docs/api/basic_json/accept.md @@ -23,6 +23,9 @@ Checks whether the input is valid JSON. respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to the iterator type with `operator!=`. + Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its + low surrogate, and a code point above `U+10FFFF` all make this function return `false`. + Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input (i.e., a parse error) nor creates diagnostic information. @@ -35,9 +38,12 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep - a `#!c FILE` pointer (throws if null) - a C-style array of characters - a pointer to a null-terminated string of single byte characters (throws if null) - - a `std::string` + - a `std::string` or a `std::string_view` (C++17) - a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators (as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`) + - wide characters (`wchar_t`, `char16_t`, `char32_t`), which are converted to UTF-8: a wide string object + (`std::wstring`, `std::u16string`, `std::u32string`) or an array (e.g. a wide string literal); a pointer to a + null-terminated wide string (e.g. `std::u16string::c_str()`) is not supported `IteratorType` : a compatible iterator type, for instance. @@ -125,6 +131,21 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by --8<-- "examples/accept__iterator_pair.output" ``` +??? example "Example: (3) reading from UTF-16 strings" + + The encoding is deduced from the character type of the input. String literals like `u"..."`, `U"..."`, and + `L"..."` work as well. + + ```cpp + --8<-- "examples/accept__wide_string.cpp" + ``` + + Output: + + ```json + --8<-- "examples/accept__wide_string.output" + ``` + ## See also - [parse](parse.md) - deserialize from a compatible input @@ -151,6 +172,9 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by ([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)). - Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0 ([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)). +- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or + a code point above `U+10FFFF`) is rejected in version 3.13.0; before that, some such sequences were accepted or + ended the input silently. !!! warning "Deprecation" diff --git a/docs/mkdocs/docs/api/basic_json/at.md b/docs/mkdocs/docs/api/basic_json/at.md index 22df4b5bb..194b123ce 100644 --- a/docs/mkdocs/docs/api/basic_json/at.md +++ b/docs/mkdocs/docs/api/basic_json/at.md @@ -239,9 +239,8 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Added in version 1.0.0. 2. Added in version 1.0.0. -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 - functions. +3. Added in version 3.11.0. Support for keys convertible to `std::string_view` added in version 3.13.0, consistently + across the lookup functions. 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. diff --git a/docs/mkdocs/docs/api/basic_json/back.md b/docs/mkdocs/docs/api/basic_json/back.md index 685c1c84b..32066d8c7 100644 --- a/docs/mkdocs/docs/api/basic_json/back.md +++ b/docs/mkdocs/docs/api/basic_json/back.md @@ -63,4 +63,4 @@ Constant. ## Version history - Added in version 1.0.0. -- Adjusted code to return reference to binary values in version 3.8.0. +- Binary values are supported since version 3.8.0. diff --git a/docs/mkdocs/docs/api/basic_json/basic_json.md b/docs/mkdocs/docs/api/basic_json/basic_json.md index e553509ea..d3df598ad 100644 --- a/docs/mkdocs/docs/api/basic_json/basic_json.md +++ b/docs/mkdocs/docs/api/basic_json/basic_json.md @@ -482,15 +482,15 @@ basic_json(basic_json&& other) noexcept; ## Version history -1. Since version 1.0.0. -2. Since version 1.0.0. -3. Since version 2.1.0. -4. Since version 3.2.0. Explicit for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since +1. Added in version 1.0.0. +2. Added in version 1.0.0. +3. Added in version 2.1.0. +4. Added in version 3.1.2. Explicit for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since version 3.13.0. -5. Since version 1.0.0. -6. Since version 1.0.0. -7. Since version 1.0.0. Fixed in version 3.13.0 to also check the iterator range for binary values; before, a range +5. Added in version 1.0.0. +6. Added in version 1.0.0. +7. Added in version 1.0.0. Fixed in version 3.13.0 to also check the iterator range for binary values; before, a range that did not cover the whole value (such as `(end(), end())`) was accepted and the whole binary value was copied, unlike the other primitive types. -8. Since version 1.0.0. -9. Since version 1.0.0. +8. Added in version 1.0.0. +9. Added in version 1.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/contains.md b/docs/mkdocs/docs/api/basic_json/contains.md index 64a3e49a1..c6e2e7d77 100644 --- a/docs/mkdocs/docs/api/basic_json/contains.md +++ b/docs/mkdocs/docs/api/basic_json/contains.md @@ -141,9 +141,8 @@ Logarithmic in the size of the JSON object. ## Version history 1. Added in version 3.11.0. -2. Added in version 3.6.0. Extended template `KeyType` to support comparable types 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), [`at`](at.md), [`value`](value.md), and other lookup functions. +2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0. Support for keys + convertible to `std::string_view` added in version 3.13.0, consistently across the lookup functions. 3. Added in version 3.7.0. 4. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of causing undefined behavior at runtime. diff --git a/docs/mkdocs/docs/api/basic_json/count.md b/docs/mkdocs/docs/api/basic_json/count.md index 52dc0ff14..580b79884 100644 --- a/docs/mkdocs/docs/api/basic_json/count.md +++ b/docs/mkdocs/docs/api/basic_json/count.md @@ -92,8 +92,7 @@ Logarithmic in the size of the JSON object. ## Version history 1. Added in version 3.11.0. -2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` 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), - [`at`](at.md), [`value`](value.md), and other lookup functions. +2. Added in version 1.0.0. Changed to support comparable key types in version 3.11.0. Support for keys convertible to + `std::string_view` added in version 3.13.0, consistently across the lookup functions. 3. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time instead of causing undefined behavior at runtime. diff --git a/docs/mkdocs/docs/api/basic_json/emplace.md b/docs/mkdocs/docs/api/basic_json/emplace.md index 09953d347..b1fd856ae 100644 --- a/docs/mkdocs/docs/api/basic_json/emplace.md +++ b/docs/mkdocs/docs/api/basic_json/emplace.md @@ -69,6 +69,6 @@ Logarithmic in the size of the container, O(log(`size()`)). ## Version history -- Since version 2.0.8. +- Added in version 2.0.8. - Fixed in version 3.13.0: for [`ordered_json`](../ordered_json.md), the value could previously only be passed as an rvalue; it can now also be passed as an lvalue or a `#!cpp const` lvalue, matching the behavior of `json`. diff --git a/docs/mkdocs/docs/api/basic_json/emplace_back.md b/docs/mkdocs/docs/api/basic_json/emplace_back.md index 782b61f36..38a6d5c3d 100644 --- a/docs/mkdocs/docs/api/basic_json/emplace_back.md +++ b/docs/mkdocs/docs/api/basic_json/emplace_back.md @@ -67,5 +67,5 @@ Amortized constant. ## Version history -- Since version 2.0.8. +- Added in version 2.0.8. - Returns reference since 3.7.0. diff --git a/docs/mkdocs/docs/api/basic_json/erase.md b/docs/mkdocs/docs/api/basic_json/erase.md index 47531fed8..1782b6162 100644 --- a/docs/mkdocs/docs/api/basic_json/erase.md +++ b/docs/mkdocs/docs/api/basic_json/erase.md @@ -213,7 +213,6 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Added in version 1.0.0. Added support for binary types in version 3.8.0. 2. Added in version 1.0.0. Added support for binary types in version 3.8.0. 3. Added in version 1.0.0. -4. 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), [`at`](at.md), [`value`](value.md), and other lookup - functions. +4. Added in version 3.11.0. Support for keys convertible to `std::string_view` added in version 3.13.0, consistently + across the lookup functions. 5. Added in version 1.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/exception.md b/docs/mkdocs/docs/api/basic_json/exception.md index b592d62ee..88cf51b1f 100644 --- a/docs/mkdocs/docs/api/basic_json/exception.md +++ b/docs/mkdocs/docs/api/basic_json/exception.md @@ -84,4 +84,4 @@ constructor. ## Version history -- Since version 3.0.0. +- Added in version 3.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/find.md b/docs/mkdocs/docs/api/basic_json/find.md index 92ab380c1..8883dc398 100644 --- a/docs/mkdocs/docs/api/basic_json/find.md +++ b/docs/mkdocs/docs/api/basic_json/find.md @@ -99,8 +99,7 @@ Logarithmic in the size of the JSON object. ## Version history 1. Added in version 3.11.0. -2. Added in version 1.0.0. Changed to support comparable types 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), - [`at`](at.md), [`value`](value.md), and other lookup functions. +2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0. Support for keys convertible to + `std::string_view` added in version 3.13.0, consistently across the lookup functions. 3. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of causing undefined behavior at runtime. diff --git a/docs/mkdocs/docs/api/basic_json/front.md b/docs/mkdocs/docs/api/basic_json/front.md index dbe92f589..08c871b9a 100644 --- a/docs/mkdocs/docs/api/basic_json/front.md +++ b/docs/mkdocs/docs/api/basic_json/front.md @@ -57,4 +57,4 @@ Constant. ## Version history - Added in version 1.0.0. -- Adjusted code to return reference to binary values in version 3.8.0. +- Binary values are supported since version 3.8.0. diff --git a/docs/mkdocs/docs/api/basic_json/get.md b/docs/mkdocs/docs/api/basic_json/get.md index fef1be766..fcb5c595a 100644 --- a/docs/mkdocs/docs/api/basic_json/get.md +++ b/docs/mkdocs/docs/api/basic_json/get.md @@ -188,6 +188,6 @@ overload (3). ## Version history -1. Since version 2.1.0. -2. Since version 2.1.0. Extended to work with other specializations of `basic_json` in version 3.2.0. -3. Since version 1.0.0. +1. Added in version 2.1.0. +2. Added in version 2.1.0. Extended to work with other specializations of `basic_json` in version 3.1.2. +3. Added in version 1.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/get_ptr.md b/docs/mkdocs/docs/api/basic_json/get_ptr.md index 7c000f1ac..1a8d87e9d 100644 --- a/docs/mkdocs/docs/api/basic_json/get_ptr.md +++ b/docs/mkdocs/docs/api/basic_json/get_ptr.md @@ -45,7 +45,7 @@ Constant. #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/basic_json/get_to.md b/docs/mkdocs/docs/api/basic_json/get_to.md index 29a78f064..233d151b0 100644 --- a/docs/mkdocs/docs/api/basic_json/get_to.md +++ b/docs/mkdocs/docs/api/basic_json/get_to.md @@ -76,5 +76,5 @@ Depends on the `json_serializer::from_json()` implementation. ## Version history -- Since version 3.3.0. +- Added in version 3.3.0. - Added a `static_assert` with a clear message for `const` arguments in version 3.13.0. diff --git a/docs/mkdocs/docs/api/basic_json/index.md b/docs/mkdocs/docs/api/basic_json/index.md index 8d945de9b..acb67401f 100644 --- a/docs/mkdocs/docs/api/basic_json/index.md +++ b/docs/mkdocs/docs/api/basic_json/index.md @@ -44,6 +44,22 @@ are collected in [Template Parameter Requirements](../../features/types/template - [**json**](../json.md) - default specialization - [**ordered_json**](../ordered_json.md) - a specialization that maintains the insertion order of object keys +## Documents and views + +Besides `basic_json`, the library offers a second way to hold a parsed JSON text: a +[`basic_json_document`](../basic_json_document/index.md), which owns or borrows the text and holds a flat index of the +values in it, and a [`basic_json_view`](../basic_json_view/index.md), a read-only handle to one value of such a document. +Both are defined in the header ``; see +[Zero-copy JSON views](../../features/json_view.md) for the concept and the trade-offs. + +- [**json_document**](../json_document.md) / [**ordered_json_document**](../ordered_json_document.md) - read-only + documents of the `basic_json` specialization they belong to +- [**json_editable_document**](../json_editable_document.md) / + [**ordered_json_editable_document**](../ordered_json_editable_document.md) - documents that can be modified in place +- [**json_view**](../json_view.md) / [**ordered_json_view**](../ordered_json_view.md) - views of a document +- [**json_editable_view**](../json_editable_view.md) / + [**ordered_json_editable_view**](../ordered_json_editable_view.md) - views of an editable document + ## Iterator invalidation All operations that add values to an **array** ([`push_back`](push_back.md) , [`operator+=`](operator+=.md), @@ -343,6 +359,9 @@ Access to the JSON value ## See also - [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc8259) +- [basic_json_document](../basic_json_document/index.md) / [basic_json_view](../basic_json_view/index.md) - parse a JSON + text into a flat index and read it through a view +- [Zero-copy JSON views](../../features/json_view.md) - when to use a document and a view instead of a `basic_json` ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/invalid_iterator.md b/docs/mkdocs/docs/api/basic_json/invalid_iterator.md index 6c19a3e7e..d757825d8 100644 --- a/docs/mkdocs/docs/api/basic_json/invalid_iterator.md +++ b/docs/mkdocs/docs/api/basic_json/invalid_iterator.md @@ -75,4 +75,4 @@ classDiagram ## Version history -- Since version 3.0.0. +- Added in version 3.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/items.md b/docs/mkdocs/docs/api/basic_json/items.md index 06e725ddd..025002e07 100644 --- a/docs/mkdocs/docs/api/basic_json/items.md +++ b/docs/mkdocs/docs/api/basic_json/items.md @@ -104,8 +104,8 @@ When iterating over an array, `key()` will return the index of the element as st ## Version history -- Added `iterator_wrapper` in version 3.0.0. -- Added `items` and deprecated `iterator_wrapper` in version 3.1.0. +- Added in version 3.1.0 as the replacement of the static function `iterator_wrapper`, which was available since + version 1.0.0 and is deprecated since version 3.1.0. - Added structured binding support in version 3.5.0. !!! warning "Deprecation" diff --git a/docs/mkdocs/docs/api/basic_json/json_serializer.md b/docs/mkdocs/docs/api/basic_json/json_serializer.md index 20e9cfd44..011f7d113 100644 --- a/docs/mkdocs/docs/api/basic_json/json_serializer.md +++ b/docs/mkdocs/docs/api/basic_json/json_serializer.md @@ -51,4 +51,4 @@ deserializes. See [Template Parameter Requirements](../../features/types/templat ## Version history -- Since version 2.0.0. +- Added in version 2.1.0. diff --git a/docs/mkdocs/docs/api/basic_json/merge_patch.md b/docs/mkdocs/docs/api/basic_json/merge_patch.md index 00e1bc8e5..1fafb1ece 100644 --- a/docs/mkdocs/docs/api/basic_json/merge_patch.md +++ b/docs/mkdocs/docs/api/basic_json/merge_patch.md @@ -71,6 +71,6 @@ returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was ## Version history -- Added in version 3.0.0. +- Added in version 3.1.0. - Fixed use of freed or relocated memory when `apply_patch` is `#!cpp *this` or refers to a value contained in `#!cpp *this`, in version 3.13.0. diff --git a/docs/mkdocs/docs/api/basic_json/operator+=.md b/docs/mkdocs/docs/api/basic_json/operator+=.md index a3dc61b64..3aee3f22d 100644 --- a/docs/mkdocs/docs/api/basic_json/operator+=.md +++ b/docs/mkdocs/docs/api/basic_json/operator+=.md @@ -128,6 +128,6 @@ interpreted as `object_t::value_type` or `std::initializer_list`, se ## Version history -1. Since version 1.0.0. -2. Since version 1.0.0. -3. Since version 2.0.0. +1. Added in version 1.0.0. +2. Added in version 1.0.0. +3. Added in version 2.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/operator[].md b/docs/mkdocs/docs/api/basic_json/operator[].md index d8df9b73a..4356969a9 100644 --- a/docs/mkdocs/docs/api/basic_json/operator[].md +++ b/docs/mkdocs/docs/api/basic_json/operator[].md @@ -284,10 +284,10 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Added in version 1.0.0. Fixed in version 3.13.0 to throw `#!cpp std::length_error` instead of emptying the array and accessing it out of bounds when `idx` equals the maximum value of `size_type`. A missing index in the const version is guarded by a runtime assertion since version 3.13.0. -2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3) - in version 3.11.0. -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. +2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. The `T* key` overloads forward to + overload 3 since version 3.11.0. +3. Added in version 3.11.0. Support for keys convertible to `std::string_view` added in version 3.13.0, consistently + across the lookup functions. 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. 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 diff --git a/docs/mkdocs/docs/api/basic_json/operator_ValueType.md b/docs/mkdocs/docs/api/basic_json/operator_ValueType.md index 48abfb3b5..dca5ebfd1 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_ValueType.md +++ b/docs/mkdocs/docs/api/basic_json/operator_ValueType.md @@ -89,6 +89,6 @@ Linear in the size of the JSON value. ## Version history -- Since version 1.0.0. +- Added in version 1.0.0. - Macros `JSON_EXPLICIT`/[`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) added in version 3.9.0. diff --git a/docs/mkdocs/docs/api/basic_json/other_error.md b/docs/mkdocs/docs/api/basic_json/other_error.md index 7019e75c5..6149cc3fb 100644 --- a/docs/mkdocs/docs/api/basic_json/other_error.md +++ b/docs/mkdocs/docs/api/basic_json/other_error.md @@ -75,4 +75,4 @@ classDiagram ## Version history -- Since version 3.0.0. +- Added in version 3.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/out_of_range.md b/docs/mkdocs/docs/api/basic_json/out_of_range.md index e1e54ae07..be579b35d 100644 --- a/docs/mkdocs/docs/api/basic_json/out_of_range.md +++ b/docs/mkdocs/docs/api/basic_json/out_of_range.md @@ -76,4 +76,4 @@ classDiagram ## Version history -- Since version 3.0.0. +- Added in version 3.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/parse.md b/docs/mkdocs/docs/api/basic_json/parse.md index f2e7eda69..a069f2086 100644 --- a/docs/mkdocs/docs/api/basic_json/parse.md +++ b/docs/mkdocs/docs/api/basic_json/parse.md @@ -25,6 +25,10 @@ static basic_json parse(IteratorType first, SentinelType last, respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to the iterator type with `operator!=`. + Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its + low surrogate, and a code point above `U+10FFFF` all yield a + [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101). + ## Template parameters `InputType` @@ -34,9 +38,12 @@ static basic_json parse(IteratorType first, SentinelType last, - a `FILE` pointer (throws if null) - a C-style array of characters - a pointer to a null-terminated string of single byte characters (throws if null) - - a `std::string` + - a `std::string` or a `std::string_view` (C++17) - a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators (as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`) + - wide characters (`wchar_t`, `char16_t`, `char32_t`), which are converted to UTF-8: a wide string object + (`std::wstring`, `std::u16string`, `std::u32string`) or an array (e.g. a wide string literal); a pointer to a + null-terminated wide string (e.g. `std::u16string::c_str()`) is not supported `IteratorType` : a compatible iterator type, for instance. @@ -112,6 +119,13 @@ By default, a `'\0'` (NUL) byte anywhere in the input is treated as end of input outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for details and the [`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) macro to opt into rejecting it instead. +!!! tip "Parsing into a document instead of a value" + + [`basic_json_document::parse`](../basic_json_document/parse.md) accepts the same inputs and the same options, but + keeps the result as a flat index of the parsed values instead of a tree of `basic_json` values. It is faster and uses + less memory when only parts of the document are read, and it also accepts a pointer to a null-terminated wide string, + which `parse()` does not. See [Zero-copy JSON views](../../features/json_view.md). + !!! tip "Performance" Contiguous inputs (strings, string views, vectors, arrays, pointer ranges) are the fastest to parse. A @@ -250,6 +264,22 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by --8<-- "examples/trailing_commas.output" ``` +??? example "Example: parse from UTF-16, UTF-32, and wide strings" + + The encoding is deduced from the character type of the input; `std::u16string` and `std::u32string` are interpreted + as UTF-16 and UTF-32, and `std::wstring` uses the platform's `wchar_t` width. String literals like `u"..."`, + `U"..."`, and `L"..."` work as well, as do iterators over wide characters. + + ```cpp + --8<-- "examples/parse__wide_string.cpp" + ``` + + Output: + + ```json + --8<-- "examples/parse__wide_string.output" + ``` + ## See also - [accept](accept.md) - check if the input is valid JSON @@ -280,6 +310,9 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by time). - Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0 ([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) restores `std::fgetc`). +- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or + a code point above `U+10FFFF`) is rejected with `parse_error.101` in version 3.13.0; before that, some such + sequences were accepted or ended the input silently. !!! warning "Deprecation" diff --git a/docs/mkdocs/docs/api/basic_json/parse_error.md b/docs/mkdocs/docs/api/basic_json/parse_error.md index c3f49ef13..0daae8de2 100644 --- a/docs/mkdocs/docs/api/basic_json/parse_error.md +++ b/docs/mkdocs/docs/api/basic_json/parse_error.md @@ -84,4 +84,4 @@ or the end of file. This also holds true when reading a byte vector for binary f ## Version history -- Since version 3.0.0. +- Added in version 3.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/push_back.md b/docs/mkdocs/docs/api/basic_json/push_back.md index 386a589a3..f6ed6a426 100644 --- a/docs/mkdocs/docs/api/basic_json/push_back.md +++ b/docs/mkdocs/docs/api/basic_json/push_back.md @@ -125,6 +125,6 @@ throws. ## Version history -1. Since version 1.0.0. -2. Since version 1.0.0. -3. Since version 2.0.0. +1. Added in version 1.0.0. +2. Added in version 1.0.0. +3. Added in version 2.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/sax_parse.md b/docs/mkdocs/docs/api/basic_json/sax_parse.md index b13147b05..0b0f1dee2 100644 --- a/docs/mkdocs/docs/api/basic_json/sax_parse.md +++ b/docs/mkdocs/docs/api/basic_json/sax_parse.md @@ -31,6 +31,10 @@ Read from input and generate SAX events respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to the iterator type with `operator!=`. + Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its + low surrogate, and a code point above `U+10FFFF` all yield a + [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101). + The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md). ## Template parameters @@ -42,8 +46,12 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index - a `FILE` pointer - a C-style array of characters - a pointer to a null-terminated string of single byte characters + - a `std::string` or a `std::string_view` (C++17) - a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators (as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`) + - wide characters (`wchar_t`, `char16_t`, `char32_t`), which are converted to UTF-8: a wide string object + (`std::wstring`, `std::u16string`, `std::u32string`) or an array (e.g. a wide string literal); a pointer to a + null-terminated wide string (e.g. `std::u16string::c_str()`) is not supported `IteratorType` : a compatible iterator type for overload (2); a pair of character iterators whose `value_type` is an integral type @@ -160,6 +168,9 @@ use the same file with stdio functions, but it must not wait for another thread ([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)). - Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0 ([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)). +- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or + a code point above `U+10FFFF`) is rejected with `parse_error.101` in version 3.13.0; before that, some such + sequences were accepted or ended the input silently. !!! warning "Deprecation" diff --git a/docs/mkdocs/docs/api/basic_json/swap.md b/docs/mkdocs/docs/api/basic_json/swap.md index 4704852f3..4bc36b125 100644 --- a/docs/mkdocs/docs/api/basic_json/swap.md +++ b/docs/mkdocs/docs/api/basic_json/swap.md @@ -182,10 +182,10 @@ Constant. ## Version history -1. Since version 1.0.0. Exchanges the `json_base_class_t` subobject along with the value since version 3.13.0. -2. Since version 1.0.0. Exchanges the `json_base_class_t` subobject along with the value since version 3.13.0. -3. Since version 1.0.0. -4. Since version 1.0.0. -5. Since version 1.0.0. -6. Since version 3.8.0. -7. Since version 3.8.0. \ No newline at end of file +1. Added in version 1.0.0. Exchanges the `json_base_class_t` subobject along with the value since version 3.13.0. +2. Added in version 1.0.0. Exchanges the `json_base_class_t` subobject along with the value since version 3.13.0. +3. Added in version 1.0.0. +4. Added in version 1.0.0. +5. Added in version 1.0.0. +6. Added in version 3.8.0. +7. Added in version 3.8.0. \ No newline at end of file diff --git a/docs/mkdocs/docs/api/basic_json/to_bjdata.md b/docs/mkdocs/docs/api/basic_json/to_bjdata.md index aa0240a73..a85d8ff8a 100644 --- a/docs/mkdocs/docs/api/basic_json/to_bjdata.md +++ b/docs/mkdocs/docs/api/basic_json/to_bjdata.md @@ -124,5 +124,3 @@ Linear in the size of the JSON value `j`. [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`. - Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an array or object was silently skipped, producing invalid BJData. -- Writes unsigned integers wider than 64 bits as high-precision numbers since version 3.13.0; previously, they were - silently truncated to 64 bits. \ No newline at end of file diff --git a/docs/mkdocs/docs/api/basic_json/type_error.md b/docs/mkdocs/docs/api/basic_json/type_error.md index b13fdd278..dcc30b29a 100644 --- a/docs/mkdocs/docs/api/basic_json/type_error.md +++ b/docs/mkdocs/docs/api/basic_json/type_error.md @@ -76,4 +76,4 @@ classDiagram ## Version history -- Since version 3.0.0. +- Added in version 3.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/value.md b/docs/mkdocs/docs/api/basic_json/value.md index 12293fd1e..aee09a537 100644 --- a/docs/mkdocs/docs/api/basic_json/value.md +++ b/docs/mkdocs/docs/api/basic_json/value.md @@ -231,11 +231,8 @@ changes to any JSON value. 1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time instead of causing undefined behavior at runtime. -2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2. Fixed in version 3.13.0 - to consistently accept `std::string_view`-convertible keys, as already supported by - [`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 - `ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or - `default_value`, as documented). Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) +2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2. Support for keys + convertible to `std::string_view` added in version 3.13.0, consistently across the lookup functions. +3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0. 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. diff --git a/docs/mkdocs/docs/api/basic_json_document/index.md b/docs/mkdocs/docs/api/basic_json_document/index.md index d1321959a..ea5f449db 100644 --- a/docs/mkdocs/docs/api/basic_json_document/index.md +++ b/docs/mkdocs/docs/api/basic_json_document/index.md @@ -9,8 +9,8 @@ class basic_json_document; A parsed JSON text, held as a flat index of its values ([16 bytes per value](../../home/architecture.md#node-index-of-json-views)) instead of a tree of `BasicJsonType` values. -Strings and numbers stay in the source text; only strings that contain escapes are decoded, into one buffer owned by -the document. [`basic_json_view`](../basic_json_view/index.md) is a read-only handle to one value of a +Strings and floating-point numbers stay in the source text; integers are converted while parsing, and strings that +contain escapes are decoded into one buffer owned by the document. [`basic_json_view`](../basic_json_view/index.md) is a read-only handle to one value of a `basic_json_document`; [`materialize()`](../basic_json_view/materialize.md) turns a subtree into the `BasicJsonType` value that [`BasicJsonType::parse()`](../basic_json/parse.md) would have produced for it. @@ -115,6 +115,13 @@ points apply to every edit: [`source_offset()`](../basic_json_view/source_offset.md) of a value introduced by an edit is `#!cpp static_cast(-1)`, the same value it reports for a decoded string. +## See also + +- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views +- [**basic_json_view**](../basic_json_view/index.md) - the non-owning handle to a value in a document +- [**json_document**](../json_document.md) and [**json_editable_document**](../json_editable_document.md) - the default specializations +- [**materialize**](../basic_json_view/materialize.md) - build a `basic_json` value from a view + ## Version history - Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/basic_json_document/parse.md b/docs/mkdocs/docs/api/basic_json_document/parse.md index 7bc00a9f1..3f39f3485 100644 --- a/docs/mkdocs/docs/api/basic_json_document/parse.md +++ b/docs/mkdocs/docs/api/basic_json_document/parse.md @@ -21,17 +21,19 @@ static basic_json_document parse(IteratorType first, IteratorType last, Both overloads accept the same JSON text as [`BasicJsonType::parse()`](../basic_json/parse.md), with the same `ignore_comments`/`ignore_trailing_commas` options, but build a [`basic_json_document`](index.md) (a flat index into -the input) instead of a tree of `BasicJsonType` values. They take the same inputs as `BasicJsonType::parse()`. Byte -inputs that stay alive can be borrowed; all others (streams, `#!cpp FILE*`, wide characters, non-contiguous -iterators) are read into a copy that the document owns -- see the Notes. Use [`parse_copy()`](parse_copy.md) to get an -owned copy of a borrowable input as well. +the input) instead of a tree of `BasicJsonType` values. They accept every input `BasicJsonType::parse()` takes, and in +addition a pointer to a null-terminated wide string (which `BasicJsonType::parse()` rejects). Contiguous inputs of +single-byte characters that stay alive can be borrowed; all others (streams, `#!cpp FILE*`, wide characters, which +must be converted to UTF-8, and non-contiguous iterators) are read into a copy that the document owns -- see the +Notes. Use [`parse_copy()`](parse_copy.md) to get an owned copy of a borrowable input +as well. ## Template parameters `InputType` : A compatible input, for instance: - - a `#!cpp std::string`, `#!cpp std::string_view`, or a C-style array of single-byte characters + - a `#!cpp std::string`, a `#!cpp std::string_view` (C++17), or a C-style array of single-byte characters - a pointer to a null-terminated string of single-byte characters (`#!cpp char`, `#!cpp signed char`, `#!cpp unsigned char`, `#!cpp std::uint8_t`) - a container for which `#!cpp obj.data()` and `#!cpp obj.size()` give contiguous single-byte access, e.g. @@ -47,6 +49,10 @@ owned copy of a borrowable input as well. `#!cpp std::string::iterator`; the iterators of single-byte characters are borrowed or read like the byte inputs above, those of wide characters are converted to UTF-8 +Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its low +surrogate, and a code point above `U+10FFFF` all yield a +[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101). + ## Parameters `input` (in) @@ -155,3 +161,5 @@ buffer of a given length, pass a pair of pointers: `#!cpp parse(ptr, ptr + len)` ## Version history - Added in version 3.13.0. +- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or + a code point above `U+10FFFF`) is rejected with `parse_error.101`. diff --git a/docs/mkdocs/docs/api/basic_json_view/index.md b/docs/mkdocs/docs/api/basic_json_view/index.md index 4d6d7dd05..5f0d90694 100644 --- a/docs/mkdocs/docs/api/basic_json_view/index.md +++ b/docs/mkdocs/docs/api/basic_json_view/index.md @@ -143,6 +143,13 @@ still refers to it -- including ones taken before the change -- reads the new va - [**source_offset**](source_offset.md) - byte offset of this value in the document's source text +## See also + +- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views +- [**basic_json_document**](../basic_json_document/index.md) - the document that owns the parsed data +- [**json_view**](../json_view.md) and [**json_editable_view**](../json_editable_view.md) - the default specializations +- [**materialize**](materialize.md) - build a `basic_json` value from a view + ## Version history - Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md index 23d5c9beb..62b0e24fc 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md @@ -51,4 +51,4 @@ byte_container_with_subtype(container_type&& container, subtype_type subtype); ## Version history -Since version 3.8.0. +Added in version 3.8.0. diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md index 7fd8fe9a4..ec2e61760 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md @@ -39,4 +39,4 @@ Constant. ## Version history -Since version 3.8.0. +Added in version 3.8.0. diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md index 98596dab0..e22429f9c 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md @@ -42,4 +42,4 @@ Constant. ## Version history -Since version 3.8.0. +Added in version 3.8.0. diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/index.md b/docs/mkdocs/docs/api/byte_container_with_subtype/index.md index cb5a3781d..b184d8496 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/index.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/index.md @@ -29,6 +29,14 @@ specific naming scheme in order to override the binary type. - [**has_subtype**](has_subtype.md) - return whether the value has a subtype - [**clear_subtype**](clear_subtype.md) - clears the binary subtype +## See also + +- [Binary values](../../features/binary_values.md) - how binary values and subtypes are handled +- [**binary**](../basic_json/binary.md) - create a binary array value +- [**get_binary**](../basic_json/get_binary.md) - get a reference to the stored binary value +- [**binary_t**](../basic_json/binary_t.md) - type for binary arrays +- [Binary formats](../../features/binary_formats/index.md) - formats that can store binary values + ## Version history - Added in version 3.8.0. diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md index 6cd319e1d..281de5aef 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md @@ -44,4 +44,4 @@ Constant. ## Version history -Since version 3.8.0. +Added in version 3.8.0. diff --git a/docs/mkdocs/docs/api/json.md b/docs/mkdocs/docs/api/json.md index 55504ef9a..b41b691ac 100644 --- a/docs/mkdocs/docs/api/json.md +++ b/docs/mkdocs/docs/api/json.md @@ -30,4 +30,4 @@ types. ## Version history -Since version 1.0.0. +Added in version 1.0.0. diff --git a/docs/mkdocs/docs/api/json_document.md b/docs/mkdocs/docs/api/json_document.md index ea365badc..cfa22e80b 100644 --- a/docs/mkdocs/docs/api/json_document.md +++ b/docs/mkdocs/docs/api/json_document.md @@ -32,4 +32,4 @@ specialization. ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/json_editable_document.md b/docs/mkdocs/docs/api/json_editable_document.md index ab3fc3868..ec5226b78 100644 --- a/docs/mkdocs/docs/api/json_editable_document.md +++ b/docs/mkdocs/docs/api/json_editable_document.md @@ -40,4 +40,4 @@ parsing, without ever rewriting the source text -- see [Edits](basic_json_docume ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/json_editable_view.md b/docs/mkdocs/docs/api/json_editable_view.md index f5fa59e57..ddee4e372 100644 --- a/docs/mkdocs/docs/api/json_editable_view.md +++ b/docs/mkdocs/docs/api/json_editable_view.md @@ -38,4 +38,4 @@ still referring to it seeing the change, see [Edits](basic_json_document/index.m ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/json_pointer/operator_string_t.md b/docs/mkdocs/docs/api/json_pointer/operator_string_t.md index 9fd8bce94..44dde733d 100644 --- a/docs/mkdocs/docs/api/json_pointer/operator_string_t.md +++ b/docs/mkdocs/docs/api/json_pointer/operator_string_t.md @@ -51,5 +51,5 @@ operator string_t() const ## Version history -- Since version 2.0.0. +- Added in version 2.0.0. - Changed type to `string_t` and deprecated in version 3.11.0. diff --git a/docs/mkdocs/docs/api/json_pointer/to_string.md b/docs/mkdocs/docs/api/json_pointer/to_string.md index 2f700e1db..b994ff80e 100644 --- a/docs/mkdocs/docs/api/json_pointer/to_string.md +++ b/docs/mkdocs/docs/api/json_pointer/to_string.md @@ -49,5 +49,5 @@ ptr == json_pointer(ptr.to_string()); ## Version history -- Since version 2.0.0. +- Added in version 2.0.0. - Changed return type to `string_t` in version 3.11.0. diff --git a/docs/mkdocs/docs/api/json_sax/index.md b/docs/mkdocs/docs/api/json_sax/index.md index 19d0a52ac..fec1e0489 100644 --- a/docs/mkdocs/docs/api/json_sax/index.md +++ b/docs/mkdocs/docs/api/json_sax/index.md @@ -59,6 +59,13 @@ leading `-` sign. - [**start_object**](start_object.md) (_virtual_) - the beginning of an object was read - [**string**](string.md) (_virtual_) - a string value was read +## See also + +- [SAX interface](../../features/parsing/sax_interface.md) - guide to event-based parsing +- [**sax_parse**](../basic_json/sax_parse.md) - generate SAX events from an input +- [**parse**](../basic_json/parse.md) - DOM parsing, with optional [parser callbacks](../../features/parsing/parser_callbacks.md) +- [Parse exceptions](../../features/parsing/parse_exceptions.md) + ## Version history - Added in version 3.2.0. diff --git a/docs/mkdocs/docs/api/json_view.md b/docs/mkdocs/docs/api/json_view.md index 819dfe501..89229ed6f 100644 --- a/docs/mkdocs/docs/api/json_view.md +++ b/docs/mkdocs/docs/api/json_view.md @@ -31,4 +31,4 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`j ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/macros/index.md b/docs/mkdocs/docs/api/macros/index.md index 1230cd052..ae11a6084 100644 --- a/docs/mkdocs/docs/api/macros/index.md +++ b/docs/mkdocs/docs/api/macros/index.md @@ -137,4 +137,13 @@ header. See also the [macro overview page](../../features/macros.md). - [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES**](nlohmann_define_type_with_names.md) - serialize/deserialize a derived class; uses default values; uses custom names - [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES**](nlohmann_define_type_with_names.md) - serialize a derived - class; uses custom names \ No newline at end of file + class; uses custom names + +## See also + +- [Macros](../../features/macros.md) - guide to the supported macros +- [Runtime assertions](../../features/assertions.md) +- [Arbitrary type conversions](../../features/arbitrary_types.md) - using the `NLOHMANN_DEFINE_TYPE_*` macros +- [Enum conversion](../../features/enum_conversion.md) - using `NLOHMANN_JSON_SERIALIZE_ENUM` +- [The `nlohmann` namespace](../../features/namespace.md) +- [CMake integration](../../integration/cmake.md) - setting macros from the build system diff --git a/docs/mkdocs/docs/api/macros/json_assert.md b/docs/mkdocs/docs/api/macros/json_assert.md index 5cd52a091..a8f198a7a 100644 --- a/docs/mkdocs/docs/api/macros/json_assert.md +++ b/docs/mkdocs/docs/api/macros/json_assert.md @@ -38,7 +38,7 @@ Therefore, assertions can be switched off by defining `NDEBUG`. ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -64,7 +64,7 @@ Therefore, assertions can be switched off by defining `NDEBUG`. #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_brace_init_copy_semantics.md b/docs/mkdocs/docs/api/macros/json_brace_init_copy_semantics.md index a038f8435..adab0769a 100644 --- a/docs/mkdocs/docs/api/macros/json_brace_init_copy_semantics.md +++ b/docs/mkdocs/docs/api/macros/json_brace_init_copy_semantics.md @@ -79,7 +79,7 @@ The default value is `0` (disabled — existing behavior is preserved). ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -98,7 +98,7 @@ The default value is `0` (disabled — existing behavior is preserved). #define JSON_BRACE_INIT_COPY_SEMANTICS 1 #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_delete_deprecated_functions.md b/docs/mkdocs/docs/api/macros/json_delete_deprecated_functions.md index 20f0ead0c..bae89d52a 100644 --- a/docs/mkdocs/docs/api/macros/json_delete_deprecated_functions.md +++ b/docs/mkdocs/docs/api/macros/json_delete_deprecated_functions.md @@ -54,7 +54,7 @@ The default value is `0` (disabled, the deprecated functions can still be called ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -72,7 +72,7 @@ The default value is `0` (disabled, the deprecated functions can still be called #define JSON_DELETE_DEPRECATED_FUNCTIONS 1 #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md b/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md index 83c48515b..a0024d81a 100644 --- a/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md +++ b/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md @@ -39,7 +39,7 @@ The default value is `0`. #define JSON_DISABLE_ENUM_SERIALIZATION 1 #include - using json = nlohmann::json; + using nlohmann::json; enum class Choice { @@ -66,7 +66,7 @@ The default value is `0`. #define JSON_DISABLE_ENUM_SERIALIZATION 1 #include - using json = nlohmann::json; + using nlohmann::json; enum class Choice { @@ -99,7 +99,7 @@ The default value is `0`. #define JSON_DISABLE_ENUM_SERIALIZATION 1 #include - using json = nlohmann::json; + using nlohmann::json; enum class Choice { diff --git a/docs/mkdocs/docs/api/macros/json_disable_stream_bulk_scan.md b/docs/mkdocs/docs/api/macros/json_disable_stream_bulk_scan.md index 3da1ac635..6b45a0056 100644 --- a/docs/mkdocs/docs/api/macros/json_disable_stream_bulk_scan.md +++ b/docs/mkdocs/docs/api/macros/json_disable_stream_bulk_scan.md @@ -92,7 +92,7 @@ The default value is `0`: streams are scanned in bulk. #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -115,7 +115,7 @@ The default value is `0`: streams are scanned in bulk. #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_disable_tuple_reference_conversion.md b/docs/mkdocs/docs/api/macros/json_disable_tuple_reference_conversion.md index 0a2216b57..6d3aa003b 100644 --- a/docs/mkdocs/docs/api/macros/json_disable_tuple_reference_conversion.md +++ b/docs/mkdocs/docs/api/macros/json_disable_tuple_reference_conversion.md @@ -72,7 +72,7 @@ The default value is `0` (disabled — existing behavior is preserved). ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -89,7 +89,7 @@ The default value is `0` (disabled — existing behavior is preserved). #define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 1 #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_disable_unlocked_file_io.md b/docs/mkdocs/docs/api/macros/json_disable_unlocked_file_io.md index 6bb9d84a6..f0aa40b65 100644 --- a/docs/mkdocs/docs/api/macros/json_disable_unlocked_file_io.md +++ b/docs/mkdocs/docs/api/macros/json_disable_unlocked_file_io.md @@ -73,7 +73,7 @@ The default value is `0`: the file is locked once and read with the unlocked std #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -92,7 +92,7 @@ The default value is `0`: the file is locked once and read with the unlocked std #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_no_simd.md b/docs/mkdocs/docs/api/macros/json_no_simd.md index 96926c48b..9c92fcc1d 100644 --- a/docs/mkdocs/docs/api/macros/json_no_simd.md +++ b/docs/mkdocs/docs/api/macros/json_no_simd.md @@ -61,7 +61,7 @@ By default, `#!cpp JSON_NO_SIMD` is not defined, and vector instructions are use #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_precise_stream_position.md b/docs/mkdocs/docs/api/macros/json_precise_stream_position.md index 0d0114da3..f641a737a 100644 --- a/docs/mkdocs/docs/api/macros/json_precise_stream_position.md +++ b/docs/mkdocs/docs/api/macros/json_precise_stream_position.md @@ -94,7 +94,7 @@ The default value is `0` (disabled — existing behavior is preserved). #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -115,7 +115,7 @@ The default value is `0` (disabled — existing behavior is preserved). #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md b/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md index 2369f5f4d..dadcb5fbe 100644 --- a/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md +++ b/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md @@ -64,7 +64,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -81,7 +81,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i #define JSON_STRICT_BINARY_UTF8 1 #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_strict_nul_handling.md b/docs/mkdocs/docs/api/macros/json_strict_nul_handling.md index 69af3811d..806246053 100644 --- a/docs/mkdocs/docs/api/macros/json_strict_nul_handling.md +++ b/docs/mkdocs/docs/api/macros/json_strict_nul_handling.md @@ -92,7 +92,7 @@ The default value is `0` (disabled — existing behavior is preserved). ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -109,7 +109,7 @@ The default value is `0` (disabled — existing behavior is preserved). #define JSON_STRICT_NUL_HANDLING 1 #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/api/macros/json_use_objects_for_enum_keyed_maps.md b/docs/mkdocs/docs/api/macros/json_use_objects_for_enum_keyed_maps.md index ca4139c48..8fdf47982 100644 --- a/docs/mkdocs/docs/api/macros/json_use_objects_for_enum_keyed_maps.md +++ b/docs/mkdocs/docs/api/macros/json_use_objects_for_enum_keyed_maps.md @@ -77,7 +77,7 @@ The default value is `0` (disabled — existing behavior is preserved). #include #include - using json = nlohmann::json; + using nlohmann::json; enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED }; @@ -105,7 +105,7 @@ The default value is `0` (disabled — existing behavior is preserved). #include #include - using json = nlohmann::json; + using nlohmann::json; enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED }; diff --git a/docs/mkdocs/docs/api/ordered_json.md b/docs/mkdocs/docs/api/ordered_json.md index feea642cd..30f295fed 100644 --- a/docs/mkdocs/docs/api/ordered_json.md +++ b/docs/mkdocs/docs/api/ordered_json.md @@ -42,4 +42,4 @@ parsing an object of `n` keys costs O(n²) rather than O(n log n). See ## Version history -Since version 3.9.0. +Added in version 3.9.0. diff --git a/docs/mkdocs/docs/api/ordered_json_document.md b/docs/mkdocs/docs/api/ordered_json_document.md index dd6760ead..b737c2e4d 100644 --- a/docs/mkdocs/docs/api/ordered_json_document.md +++ b/docs/mkdocs/docs/api/ordered_json_document.md @@ -35,4 +35,4 @@ of object keys, instead of sorting them like [`json_document`](json_document.md) ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/ordered_json_editable_document.md b/docs/mkdocs/docs/api/ordered_json_editable_document.md index 3127eea70..cf86307e5 100644 --- a/docs/mkdocs/docs/api/ordered_json_editable_document.md +++ b/docs/mkdocs/docs/api/ordered_json_editable_document.md @@ -44,4 +44,4 @@ members [`set`](basic_json_document/set.md) added -- instead of sorting them lik ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/ordered_json_editable_view.md b/docs/mkdocs/docs/api/ordered_json_editable_view.md index 78351121e..1c9953d57 100644 --- a/docs/mkdocs/docs/api/ordered_json_editable_view.md +++ b/docs/mkdocs/docs/api/ordered_json_editable_view.md @@ -37,4 +37,4 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/ordered_json_view.md b/docs/mkdocs/docs/api/ordered_json_view.md index 9800b0752..9df72040f 100644 --- a/docs/mkdocs/docs/api/ordered_json_view.md +++ b/docs/mkdocs/docs/api/ordered_json_view.md @@ -32,4 +32,4 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an ## Version history -Since version 3.13.0. +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/community/quality_assurance.md b/docs/mkdocs/docs/community/quality_assurance.md index d55d32c16..6c8046964 100644 --- a/docs/mkdocs/docs/community/quality_assurance.md +++ b/docs/mkdocs/docs/community/quality_assurance.md @@ -188,10 +188,13 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa ``` - [x] The code is checked with the latest [Cppcheck](https://cppcheck.sourceforge.io) with all warnings enabled. -- [x] The code is checked with the latest [Clang Static Analyzer](https://clang-analyzer.llvm.org) with 89 enabled - rules. +- [x] The code is checked with the latest [Clang Static Analyzer](https://clang-analyzer.llvm.org) with 52 enabled + checkers. - [x] The code is checked with [Infer](https://fbinfer.com). - [x] The code is checked with [Codacy](https://app.codacy.com/gh/nlohmann/json/dashboard). +- [x] The code is scanned with [CodeQL](https://codeql.github.com), [Semgrep](https://semgrep.dev), and + [Flawfinder](https://dwheeler.com/flawfinder/) on every push and pull request and on a schedule; the findings are + reported to GitHub's code scanning. ## Dynamic analysis @@ -204,6 +207,8 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa - [x] The test suite is executed with [Valgrind](https://valgrind.org) (Memcheck) to detect memory leaks. - [x] The test suite is executed with [Sanitizers](https://github.com/google/sanitizers) (address sanitizer, undefined behavior sanitizer, integer overflow detection, nullability violations). +- [x] The fuzz targets are run with [CIFuzz](https://google.github.io/oss-fuzz/getting-started/continuous-integration/) + on every pull request to catch regressions before they are merged. ## Dependencies @@ -223,6 +228,8 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa No release is made while such an alert is open. - [x] Third-party code included in the repository for testing, such as [doctest](https://github.com/doctest/doctest), is updated manually. +- [x] The repository is evaluated with the [OpenSSF Scorecard](https://securityscorecards.dev) on a schedule and on + every push to the default branch, and the results are reported to GitHub's code scanning. ## Style check @@ -258,4 +265,21 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa - [x] The library is tested against relevant CMake versions: - CMake 3.5 (the earliest supported) - CMake 3.31.6 (the latest 3.x release) - - CMake 4.0.0 (a very recent release) + - CMake 4.0.0 (the first 4.x release) +!!! success "Requirement: Other build systems" + + The library can be used with build systems and package managers other than CMake. + +- [x] The library can be consumed with [Meson](https://json.nlohmann.me/integration/package_managers/) and the Swift + Package Manager; the installation with Meson and the resolution of `Package.swift` are tested in the CI. + +!!! success "Requirement: Reproducible and verifiable builds" + + The library can be built and tested without network access or a Git checkout, and its licensing is machine-readable. + +- [x] The test suite is executed from an exported source tree without the `.git` directory and with the test data + being available offline. +- [x] The test suite is executed without the tests that change installed files to check that builds are reproducible. +- [x] The repository is checked for compliance with the [REUSE specification](https://reuse.software) so that the + license and copyright of every file is machine-readable. +- [x] The documentation is built with warnings treated as errors, and its links are checked regularly. diff --git a/docs/mkdocs/docs/community/roadmap.md b/docs/mkdocs/docs/community/roadmap.md index 1b1f287a9..d294bd5f7 100644 --- a/docs/mkdocs/docs/community/roadmap.md +++ b/docs/mkdocs/docs/community/roadmap.md @@ -58,7 +58,7 @@ particular, a 3.x release does not: - remove or rename a documented macro, CMake option, CMake target, or header, or change what a documented macro does. Exceptions to these rules, for instance when fixing a bug requires changing the exception a function throws, are -documented in the [release notes](../home/releases.md). +documented in the [release notes](../home/releases/index.md). The following are **not** part of the public API and may change in any release, including patch releases: diff --git a/docs/mkdocs/docs/css/custom.css b/docs/mkdocs/docs/css/custom.css index 1fec0e57c..133609ec9 100644 --- a/docs/mkdocs/docs/css/custom.css +++ b/docs/mkdocs/docs/css/custom.css @@ -18,3 +18,32 @@ white-space: nowrap; cursor: help; } + +/* byte layout tables of the binary formats */ +.md-typeset .byte-layout td code { + white-space: pre-wrap; +} + +.md-typeset .bl { + display: inline-block; + padding: 0 .25em; + border-radius: .2em; + font-family: var(--md-code-font-family); + font-size: .85em; + white-space: nowrap; +} + +.md-typeset .bl-t { + background-color: rgba(66, 133, 244, .25); + box-shadow: inset 0 -.12em 0 rgba(66, 133, 244, .8); +} + +.md-typeset .bl-l { + background-color: rgba(251, 140, 0, .25); + box-shadow: inset 0 -.12em 0 rgba(251, 140, 0, .8); +} + +.md-typeset .bl-d { + background-color: rgba(67, 160, 71, .25); + box-shadow: inset 0 -.12em 0 rgba(67, 160, 71, .8); +} diff --git a/docs/mkdocs/docs/examples/README.cpp b/docs/mkdocs/docs/examples/README.cpp index 2d641e587..19d86a2b2 100644 --- a/docs/mkdocs/docs/examples/README.cpp +++ b/docs/mkdocs/docs/examples/README.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/accept__iterator_pair.cpp b/docs/mkdocs/docs/examples/accept__iterator_pair.cpp index dc7320de6..676ecb256 100644 --- a/docs/mkdocs/docs/examples/accept__iterator_pair.cpp +++ b/docs/mkdocs/docs/examples/accept__iterator_pair.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/accept__string.cpp b/docs/mkdocs/docs/examples/accept__string.cpp index 8eb3d9b75..dc97c9818 100644 --- a/docs/mkdocs/docs/examples/accept__string.cpp +++ b/docs/mkdocs/docs/examples/accept__string.cpp @@ -2,19 +2,19 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { // a valid JSON text - auto valid_text = R"( + const auto* valid_text = R"( { "numbers": [1, 2, 3] } )"; // an invalid JSON text - auto invalid_text = R"( + const auto* invalid_text = R"( { "strings": ["extra", "comma", ] } diff --git a/docs/mkdocs/docs/examples/accept__wide_string.cpp b/docs/mkdocs/docs/examples/accept__wide_string.cpp new file mode 100644 index 000000000..af25ae873 --- /dev/null +++ b/docs/mkdocs/docs/examples/accept__wide_string.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +using nlohmann::json; + +int main() +{ + // a valid JSON text, given as UTF-16 + const std::u16string valid_text = u"{\"name\":\"\u00e9\"}"; + + // an invalid JSON text, given as UTF-16 + const std::u16string invalid_text = u"{\"name\":\"\u00e9\",}"; + + std::cout << std::boolalpha + << json::accept(valid_text) << ' ' + << json::accept(invalid_text) << '\n'; +} diff --git a/docs/mkdocs/docs/examples/accept__wide_string.output b/docs/mkdocs/docs/examples/accept__wide_string.output new file mode 100644 index 000000000..836a5934c --- /dev/null +++ b/docs/mkdocs/docs/examples/accept__wide_string.output @@ -0,0 +1 @@ +true false diff --git a/docs/mkdocs/docs/examples/array.cpp b/docs/mkdocs/docs/examples/array.cpp index 139b5ef10..b0ee3f932 100644 --- a/docs/mkdocs/docs/examples/array.cpp +++ b/docs/mkdocs/docs/examples/array.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/array_t.cpp b/docs/mkdocs/docs/examples/array_t.cpp index 0964857b7..c1196620e 100644 --- a/docs/mkdocs/docs/examples/array_t.cpp +++ b/docs/mkdocs/docs/examples/array_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same, json::array_t>::value << std::endl; + std::cout << std::boolalpha << std::is_same, json::array_t>::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/at__json_pointer.cpp b/docs/mkdocs/docs/examples/at__json_pointer.cpp index 15e91433d..23bb917c3 100644 --- a/docs/mkdocs/docs/examples/at__json_pointer.cpp +++ b/docs/mkdocs/docs/examples/at__json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/at__json_pointer_const.cpp b/docs/mkdocs/docs/examples/at__json_pointer_const.cpp index ab026e078..6e121b75d 100644 --- a/docs/mkdocs/docs/examples/at__json_pointer_const.cpp +++ b/docs/mkdocs/docs/examples/at__json_pointer_const.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/at__keytype.c++17.cpp b/docs/mkdocs/docs/examples/at__keytype.c++17.cpp index 032506acd..271158dd7 100644 --- a/docs/mkdocs/docs/examples/at__keytype.c++17.cpp +++ b/docs/mkdocs/docs/examples/at__keytype.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/at__keytype_const.c++17.cpp b/docs/mkdocs/docs/examples/at__keytype_const.c++17.cpp index 712d456e6..948e46368 100644 --- a/docs/mkdocs/docs/examples/at__keytype_const.c++17.cpp +++ b/docs/mkdocs/docs/examples/at__keytype_const.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/at__object_t_key_type.cpp b/docs/mkdocs/docs/examples/at__object_t_key_type.cpp index e1f33ceca..6aaf1b4d0 100644 --- a/docs/mkdocs/docs/examples/at__object_t_key_type.cpp +++ b/docs/mkdocs/docs/examples/at__object_t_key_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/at__object_t_key_type_const.cpp b/docs/mkdocs/docs/examples/at__object_t_key_type_const.cpp index b37bbd489..9cbf3c2c2 100644 --- a/docs/mkdocs/docs/examples/at__object_t_key_type_const.cpp +++ b/docs/mkdocs/docs/examples/at__object_t_key_type_const.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/at__size_type.cpp b/docs/mkdocs/docs/examples/at__size_type.cpp index 6527c6b16..ce6fdefeb 100644 --- a/docs/mkdocs/docs/examples/at__size_type.cpp +++ b/docs/mkdocs/docs/examples/at__size_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/at__size_type_const.cpp b/docs/mkdocs/docs/examples/at__size_type_const.cpp index 2080387a3..78d2d5f62 100644 --- a/docs/mkdocs/docs/examples/at__size_type_const.cpp +++ b/docs/mkdocs/docs/examples/at__size_type_const.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/back.cpp b/docs/mkdocs/docs/examples/back.cpp index 45342db13..6f1526cc4 100644 --- a/docs/mkdocs/docs/examples/back.cpp +++ b/docs/mkdocs/docs/examples/back.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__BasicJsonType.cpp b/docs/mkdocs/docs/examples/basic_json__BasicJsonType.cpp index 562d45b9f..9586832db 100644 --- a/docs/mkdocs/docs/examples/basic_json__BasicJsonType.cpp +++ b/docs/mkdocs/docs/examples/basic_json__BasicJsonType.cpp @@ -1,8 +1,8 @@ #include #include -using json = nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::json; +using nlohmann::ordered_json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__CompatibleType.cpp b/docs/mkdocs/docs/examples/basic_json__CompatibleType.cpp index 130ef6aec..f1301b8d9 100644 --- a/docs/mkdocs/docs/examples/basic_json__CompatibleType.cpp +++ b/docs/mkdocs/docs/examples/basic_json__CompatibleType.cpp @@ -8,7 +8,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.cpp b/docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.cpp index dec693c80..ece5a7fe4 100644 --- a/docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.cpp +++ b/docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__basic_json.cpp b/docs/mkdocs/docs/examples/basic_json__basic_json.cpp index 17136f437..481d43458 100644 --- a/docs/mkdocs/docs/examples/basic_json__basic_json.cpp +++ b/docs/mkdocs/docs/examples/basic_json__basic_json.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__copyassignment.cpp b/docs/mkdocs/docs/examples/basic_json__copyassignment.cpp index 2d8657400..e2b684e13 100644 --- a/docs/mkdocs/docs/examples/basic_json__copyassignment.cpp +++ b/docs/mkdocs/docs/examples/basic_json__copyassignment.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__list_init_t.cpp b/docs/mkdocs/docs/examples/basic_json__list_init_t.cpp index 78611e1a3..c678318dc 100644 --- a/docs/mkdocs/docs/examples/basic_json__list_init_t.cpp +++ b/docs/mkdocs/docs/examples/basic_json__list_init_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__moveconstructor.cpp b/docs/mkdocs/docs/examples/basic_json__moveconstructor.cpp index 48b68f61e..f8a84d091 100644 --- a/docs/mkdocs/docs/examples/basic_json__moveconstructor.cpp +++ b/docs/mkdocs/docs/examples/basic_json__moveconstructor.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__nullptr_t.cpp b/docs/mkdocs/docs/examples/basic_json__nullptr_t.cpp index 7a4366650..ed69b5707 100644 --- a/docs/mkdocs/docs/examples/basic_json__nullptr_t.cpp +++ b/docs/mkdocs/docs/examples/basic_json__nullptr_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__size_type_basic_json.cpp b/docs/mkdocs/docs/examples/basic_json__size_type_basic_json.cpp index 9ec767727..526ed5457 100644 --- a/docs/mkdocs/docs/examples/basic_json__size_type_basic_json.cpp +++ b/docs/mkdocs/docs/examples/basic_json__size_type_basic_json.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json__value_t.cpp b/docs/mkdocs/docs/examples/basic_json__value_t.cpp index c306731ac..b52b3a442 100644 --- a/docs/mkdocs/docs/examples/basic_json__value_t.cpp +++ b/docs/mkdocs/docs/examples/basic_json__value_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__accept.cpp b/docs/mkdocs/docs/examples/basic_json_document__accept.cpp index 675fa823f..e7273bab6 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__accept.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__accept.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__basic_json_document.cpp b/docs/mkdocs/docs/examples/basic_json_document__basic_json_document.cpp index 0c185f7b8..3d6741155 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__basic_json_document.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__basic_json_document.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__erase.cpp b/docs/mkdocs/docs/examples/basic_json_document__erase.cpp index 4b3197698..116eba8a9 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__erase.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__erase.cpp @@ -1,9 +1,9 @@ #include #include -using json = nlohmann::json; -using json_editable_document = nlohmann::json_editable_document; -using json_editable_view = nlohmann::json_editable_view; +using nlohmann::json; +using nlohmann::json_editable_document; +using nlohmann::json_editable_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__insert.cpp b/docs/mkdocs/docs/examples/basic_json_document__insert.cpp index 1c04bd6cc..714c4b98e 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__insert.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__insert.cpp @@ -1,9 +1,9 @@ #include #include -using json = nlohmann::json; -using json_editable_document = nlohmann::json_editable_document; -using json_editable_view = nlohmann::json_editable_view; +using nlohmann::json; +using nlohmann::json_editable_document; +using nlohmann::json_editable_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__is_discarded.cpp b/docs/mkdocs/docs/examples/basic_json_document__is_discarded.cpp index d64d37d23..62ce5b29a 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__is_discarded.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__is_discarded.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__load.cpp b/docs/mkdocs/docs/examples/basic_json_document__load.cpp index 52a085a10..03861deb6 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__load.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__load.cpp @@ -4,8 +4,8 @@ #include #include -using json = nlohmann::json; -using json_document = nlohmann::json_document; +using nlohmann::json; +using nlohmann::json_document; using image_check = json_document::image_check; int main() diff --git a/docs/mkdocs/docs/examples/basic_json_document__memory_usage.cpp b/docs/mkdocs/docs/examples/basic_json_document__memory_usage.cpp index efa235310..7e9e9136f 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__memory_usage.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__memory_usage.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__node_count.cpp b/docs/mkdocs/docs/examples/basic_json_document__node_count.cpp index d6ae5f89b..bb685bb51 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__node_count.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__node_count.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__owns_source.cpp b/docs/mkdocs/docs/examples/basic_json_document__owns_source.cpp index 8f442ea13..05bca7856 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__owns_source.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__owns_source.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__parse.cpp b/docs/mkdocs/docs/examples/basic_json_document__parse.cpp index 4eba7350d..b103bac85 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__parse.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__parse.cpp @@ -3,7 +3,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__parse_copy.cpp b/docs/mkdocs/docs/examples/basic_json_document__parse_copy.cpp index 16d3ce939..4770a4bfb 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__parse_copy.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__parse_copy.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; json_document parse_from_temporary_buffer() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__parse_iterator_pair.cpp b/docs/mkdocs/docs/examples/basic_json_document__parse_iterator_pair.cpp index 6d446bd67..ea0376254 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__parse_iterator_pair.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__parse_iterator_pair.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__push_back.cpp b/docs/mkdocs/docs/examples/basic_json_document__push_back.cpp index 16385ea99..e7a33a3e0 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__push_back.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__push_back.cpp @@ -1,8 +1,8 @@ #include #include -using json = nlohmann::json; -using json_editable_document = nlohmann::json_editable_document; +using nlohmann::json; +using nlohmann::json_editable_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__read.cpp b/docs/mkdocs/docs/examples/basic_json_document__read.cpp index 4858a7db7..7b9208da9 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__read.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__read.cpp @@ -3,7 +3,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__root.cpp b/docs/mkdocs/docs/examples/basic_json_document__root.cpp index 9789f2566..5b06ec6da 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__root.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__root.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__save.cpp b/docs/mkdocs/docs/examples/basic_json_document__save.cpp index 6a3cc978b..76eac5efe 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__save.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__save.cpp @@ -4,7 +4,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__set.cpp b/docs/mkdocs/docs/examples/basic_json_document__set.cpp index 7631a7b1b..ece572ee2 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__set.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__set.cpp @@ -1,9 +1,9 @@ #include #include -using json = nlohmann::json; -using json_editable_document = nlohmann::json_editable_document; -using json_editable_view = nlohmann::json_editable_view; +using nlohmann::json; +using nlohmann::json_editable_document; +using nlohmann::json_editable_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__shrink_to_fit.cpp b/docs/mkdocs/docs/examples/basic_json_document__shrink_to_fit.cpp index f2560078a..6ab9b6503 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__shrink_to_fit.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__shrink_to_fit.cpp @@ -3,7 +3,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_document__source.cpp b/docs/mkdocs/docs/examples/basic_json_document__source.cpp index 722cdffaf..12d40ee72 100644 --- a/docs/mkdocs/docs/examples/basic_json_document__source.cpp +++ b/docs/mkdocs/docs/examples/basic_json_document__source.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__at.cpp b/docs/mkdocs/docs/examples/basic_json_view__at.cpp index 1e984c55f..2cd390e36 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__at.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__at.cpp @@ -1,8 +1,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__at_json_pointer.cpp b/docs/mkdocs/docs/examples/basic_json_view__at_json_pointer.cpp index 47ed9419c..8f6088d84 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__at_json_pointer.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__at_json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; using json_pointer = nlohmann::json::json_pointer; int main() diff --git a/docs/mkdocs/docs/examples/basic_json_view__back.cpp b/docs/mkdocs/docs/examples/basic_json_view__back.cpp index 606cb8966..6fa64cbc0 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__back.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__back.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__begin.cpp b/docs/mkdocs/docs/examples/basic_json_view__begin.cpp index 5989ae404..71a119602 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__begin.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__begin.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__cbegin.cpp b/docs/mkdocs/docs/examples/basic_json_view__cbegin.cpp index 4e67a8041..70b532077 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__cbegin.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__cbegin.cpp @@ -2,8 +2,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__cend.cpp b/docs/mkdocs/docs/examples/basic_json_view__cend.cpp index c72990673..d396f43bf 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__cend.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__cend.cpp @@ -2,8 +2,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__contains.cpp b/docs/mkdocs/docs/examples/basic_json_view__contains.cpp index e339ee79a..603bef166 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__contains.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__contains.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__contains_json_pointer.cpp b/docs/mkdocs/docs/examples/basic_json_view__contains_json_pointer.cpp index 260bac5eb..cb86f14bd 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__contains_json_pointer.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__contains_json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; using json_pointer = nlohmann::json::json_pointer; int main() diff --git a/docs/mkdocs/docs/examples/basic_json_view__count.cpp b/docs/mkdocs/docs/examples/basic_json_view__count.cpp index 75d82579f..414ff816f 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__count.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__count.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__dump.cpp b/docs/mkdocs/docs/examples/basic_json_view__dump.cpp index c9bd8198f..617e4307b 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__dump.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__dump.cpp @@ -1,8 +1,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__end.cpp b/docs/mkdocs/docs/examples/basic_json_view__end.cpp index 6808a6c95..cf5f6f421 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__end.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__end.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__find.cpp b/docs/mkdocs/docs/examples/basic_json_view__find.cpp index 06141b65a..47512fb27 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__find.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__find.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__front.cpp b/docs/mkdocs/docs/examples/basic_json_view__front.cpp index ed743f77e..079813c48 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__front.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__front.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__get.cpp b/docs/mkdocs/docs/examples/basic_json_view__get.cpp index 43e262035..e5ede47b9 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__get.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__get.cpp @@ -3,8 +3,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; // address has no direct conversion in get(), so get
() falls back // to materialize().get
() -- a real nlohmann::json value is built diff --git a/docs/mkdocs/docs/examples/basic_json_view__get_string.cpp b/docs/mkdocs/docs/examples/basic_json_view__get_string.cpp index 66ae1bbb3..1c459286b 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__get_string.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__get_string.cpp @@ -2,8 +2,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__get_to.cpp b/docs/mkdocs/docs/examples/basic_json_view__get_to.cpp index 748270759..ccab7e309 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__get_to.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__get_to.cpp @@ -2,8 +2,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__items.cpp b/docs/mkdocs/docs/examples/basic_json_view__items.cpp index 3f7779c19..aaaaafba5 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__items.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__items.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__materialize.cpp b/docs/mkdocs/docs/examples/basic_json_view__materialize.cpp index 38160b461..9d11cee95 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__materialize.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__materialize.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__number_format.cpp b/docs/mkdocs/docs/examples/basic_json_view__number_format.cpp index f67ea4e84..5ba989d40 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__number_format.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__number_format.cpp @@ -1,8 +1,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__number_token.cpp b/docs/mkdocs/docs/examples/basic_json_view__number_token.cpp index dec63371c..4ccd8d089 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__number_token.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__number_token.cpp @@ -1,8 +1,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__operator[].cpp b/docs/mkdocs/docs/examples/basic_json_view__operator[].cpp index 1919e06be..a454bf7c5 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__operator[].cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__operator[].cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__operator[]_json_pointer.cpp b/docs/mkdocs/docs/examples/basic_json_view__operator[]_json_pointer.cpp index 2ea8130a6..3010cb2b5 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__operator[]_json_pointer.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__operator[]_json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; using json_pointer = nlohmann::json::json_pointer; int main() diff --git a/docs/mkdocs/docs/examples/basic_json_view__operator_eq.cpp b/docs/mkdocs/docs/examples/basic_json_view__operator_eq.cpp index 846d40fc0..abea9086d 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__operator_eq.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__operator_eq.cpp @@ -1,8 +1,8 @@ #include #include -using json_document = nlohmann::json_document; -using json = nlohmann::json; +using nlohmann::json_document; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__operator_ltlt.cpp b/docs/mkdocs/docs/examples/basic_json_view__operator_ltlt.cpp index 97100d536..09afb9f6f 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__operator_ltlt.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__operator_ltlt.cpp @@ -2,8 +2,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__operator_ne.cpp b/docs/mkdocs/docs/examples/basic_json_view__operator_ne.cpp index faa1d61cf..24fe8927c 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__operator_ne.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__operator_ne.cpp @@ -1,9 +1,9 @@ #include #include -using json_document = nlohmann::json_document; -using ordered_json_document = nlohmann::ordered_json_document; -using json = nlohmann::json; +using nlohmann::json_document; +using nlohmann::ordered_json_document; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__size_empty.cpp b/docs/mkdocs/docs/examples/basic_json_view__size_empty.cpp index 36700ef9d..64a16c589 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__size_empty.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__size_empty.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__source_offset.cpp b/docs/mkdocs/docs/examples/basic_json_view__source_offset.cpp index 651964f0f..9ec340f1f 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__source_offset.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__source_offset.cpp @@ -2,7 +2,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__type_name.cpp b/docs/mkdocs/docs/examples/basic_json_view__type_name.cpp index 30661c0fd..7e7740877 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__type_name.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__type_name.cpp @@ -1,8 +1,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__type_predicates.cpp b/docs/mkdocs/docs/examples/basic_json_view__type_predicates.cpp index edc37ff52..210dbe230 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__type_predicates.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__type_predicates.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__value.cpp b/docs/mkdocs/docs/examples/basic_json_view__value.cpp index 1829730ef..252c80c57 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__value.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__value.cpp @@ -2,8 +2,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; int main() { diff --git a/docs/mkdocs/docs/examples/basic_json_view__value_json_pointer.cpp b/docs/mkdocs/docs/examples/basic_json_view__value_json_pointer.cpp index 605f389a7..e5b4448fa 100644 --- a/docs/mkdocs/docs/examples/basic_json_view__value_json_pointer.cpp +++ b/docs/mkdocs/docs/examples/basic_json_view__value_json_pointer.cpp @@ -1,8 +1,8 @@ #include #include -using json_document = nlohmann::json_document; -using json_view = nlohmann::json_view; +using nlohmann::json_document; +using nlohmann::json_view; using json_pointer = nlohmann::json::json_pointer; int main() diff --git a/docs/mkdocs/docs/examples/begin.cpp b/docs/mkdocs/docs/examples/begin.cpp index 654835b02..6be31d569 100644 --- a/docs/mkdocs/docs/examples/begin.cpp +++ b/docs/mkdocs/docs/examples/begin.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/binary.cpp b/docs/mkdocs/docs/examples/binary.cpp index 617ce6096..6fff997bb 100644 --- a/docs/mkdocs/docs/examples/binary.cpp +++ b/docs/mkdocs/docs/examples/binary.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -12,5 +12,5 @@ int main() json j = json::binary(vec, 42); // output type and subtype - std::cout << "type: " << j.type_name() << ", subtype: " << j.get_binary().subtype() << std::endl; + std::cout << "type: " << j.type_name() << ", subtype: " << j.get_binary().subtype() << '\n'; } diff --git a/docs/mkdocs/docs/examples/binary_t.cpp b/docs/mkdocs/docs/examples/binary_t.cpp index bfaee5ca8..ed6196634 100644 --- a/docs/mkdocs/docs/examples/binary_t.cpp +++ b/docs/mkdocs/docs/examples/binary_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same>, json::binary_t>::value << std::endl; + std::cout << std::boolalpha << std::is_same>, json::binary_t>::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/boolean_t.cpp b/docs/mkdocs/docs/examples/boolean_t.cpp index 75b8c99f9..45d6aa385 100644 --- a/docs/mkdocs/docs/examples/boolean_t.cpp +++ b/docs/mkdocs/docs/examples/boolean_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same::value << std::endl; + std::cout << std::boolalpha << std::is_same::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp index 1c10be5c2..bcb8d664a 100644 --- a/docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp +++ b/docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp @@ -4,7 +4,7 @@ // define a byte container based on std::vector using byte_container_with_subtype = nlohmann::byte_container_with_subtype>; -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -19,5 +19,5 @@ int main() // (3) create container with subtype auto c3 = byte_container_with_subtype(bytes, 42); - std::cout << json(c1) << "\n" << json(c2) << "\n" << json(c3) << std::endl; + std::cout << json(c1) << "\n" << json(c2) << "\n" << json(c3) << '\n'; } diff --git a/docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.cpp index f9ce6842b..68a6767f7 100644 --- a/docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.cpp +++ b/docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.cpp @@ -4,7 +4,7 @@ // define a byte container based on std::vector using byte_container_with_subtype = nlohmann::byte_container_with_subtype>; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.cpp index 61c21eaae..b393be891 100644 --- a/docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.cpp +++ b/docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.cpp @@ -15,5 +15,5 @@ int main() auto c2 = byte_container_with_subtype(bytes, 42); std::cout << std::boolalpha << "c1.has_subtype() = " << c1.has_subtype() - << "\nc2.has_subtype() = " << c2.has_subtype() << std::endl; + << "\nc2.has_subtype() = " << c2.has_subtype() << '\n'; } diff --git a/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__equal.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__equal.cpp index ef2a9cdcb..6b7f079a8 100644 --- a/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__equal.cpp +++ b/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__equal.cpp @@ -19,5 +19,5 @@ int main() << "c1 == c2: " << (c1 == c2) << '\n' << "c1 == c3: " << (c1 == c3) << '\n' << "c3 == c4: " << (c3 == c4) << '\n' - << "c3 == c5: " << (c3 == c5) << std::endl; + << "c3 == c5: " << (c3 == c5) << '\n'; } diff --git a/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__notequal.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__notequal.cpp index c7c104342..a3f6f9578 100644 --- a/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__notequal.cpp +++ b/docs/mkdocs/docs/examples/byte_container_with_subtype__operator__notequal.cpp @@ -19,5 +19,5 @@ int main() << "c1 != c2: " << (c1 != c2) << '\n' << "c1 != c3: " << (c1 != c3) << '\n' << "c3 != c4: " << (c3 != c4) << '\n' - << "c3 != c5: " << (c3 != c5) << std::endl; + << "c3 != c5: " << (c3 != c5) << '\n'; } diff --git a/docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.cpp index b2694c54d..3b8123aec 100644 --- a/docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.cpp +++ b/docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.cpp @@ -4,7 +4,7 @@ // define a byte container based on std::vector using byte_container_with_subtype = nlohmann::byte_container_with_subtype>; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.cpp index cd230ade1..b60e93e8b 100644 --- a/docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.cpp +++ b/docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.cpp @@ -15,7 +15,7 @@ int main() auto c2 = byte_container_with_subtype(bytes, 42); std::cout << "c1.subtype() = " << c1.subtype() - << "\nc2.subtype() = " << c2.subtype() << std::endl; + << "\nc2.subtype() = " << c2.subtype() << '\n'; // in case no subtype is set, return special value assert(c1.subtype() == static_cast(-1)); diff --git a/docs/mkdocs/docs/examples/cbegin.cpp b/docs/mkdocs/docs/examples/cbegin.cpp index bed2b3725..c0c66a36d 100644 --- a/docs/mkdocs/docs/examples/cbegin.cpp +++ b/docs/mkdocs/docs/examples/cbegin.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/cbor_tag_handler_t.cpp b/docs/mkdocs/docs/examples/cbor_tag_handler_t.cpp index 38d168ca8..78c03c282 100644 --- a/docs/mkdocs/docs/examples/cbor_tag_handler_t.cpp +++ b/docs/mkdocs/docs/examples/cbor_tag_handler_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,14 +15,14 @@ int main() } catch (const json::parse_error& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << '\n'; } // cbor_tag_handler_t::ignore ignores the tag auto b_ignore_tag = json::from_cbor(vec, true, true, json::cbor_tag_handler_t::ignore); - std::cout << b_ignore_tag << std::endl; + std::cout << b_ignore_tag << '\n'; // cbor_tag_handler_t::store stores the tag as binary subtype auto b_store_tag = json::from_cbor(vec, true, true, json::cbor_tag_handler_t::store); - std::cout << b_store_tag << std::endl; + std::cout << b_store_tag << '\n'; } diff --git a/docs/mkdocs/docs/examples/cend.cpp b/docs/mkdocs/docs/examples/cend.cpp index 3050f5001..374bb3d17 100644 --- a/docs/mkdocs/docs/examples/cend.cpp +++ b/docs/mkdocs/docs/examples/cend.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/clear.cpp b/docs/mkdocs/docs/examples/clear.cpp index f081e7eda..b4a73dbcb 100644 --- a/docs/mkdocs/docs/examples/clear.cpp +++ b/docs/mkdocs/docs/examples/clear.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/comments.cpp b/docs/mkdocs/docs/examples/comments.cpp index fc8a0ac34..1e02a071d 100644 --- a/docs/mkdocs/docs/examples/comments.cpp +++ b/docs/mkdocs/docs/examples/comments.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -20,7 +20,7 @@ int main() } catch (json::exception& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << '\n'; } json j = json::parse(s, diff --git a/docs/mkdocs/docs/examples/contains__json_pointer.cpp b/docs/mkdocs/docs/examples/contains__json_pointer.cpp index 985ad9ec5..1ace92a93 100644 --- a/docs/mkdocs/docs/examples/contains__json_pointer.cpp +++ b/docs/mkdocs/docs/examples/contains__json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -23,5 +23,5 @@ int main() // an array index with a leading '0' is not found << j.contains("/array/01"_json_pointer) << '\n' // an array index that is not a number is not found - << j.contains("/array/one"_json_pointer) << std::endl; + << j.contains("/array/one"_json_pointer) << '\n'; } diff --git a/docs/mkdocs/docs/examples/contains__keytype.c++17.cpp b/docs/mkdocs/docs/examples/contains__keytype.c++17.cpp index 43b62fab1..6b98b99b6 100644 --- a/docs/mkdocs/docs/examples/contains__keytype.c++17.cpp +++ b/docs/mkdocs/docs/examples/contains__keytype.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -16,5 +16,5 @@ int main() std::cout << std::boolalpha << "j_object contains 'key': " << j_object.contains("key"sv) << '\n' << "j_object contains 'another': " << j_object.contains("another"sv) << '\n' << - "j_array contains 'key': " << j_array.contains("key"sv) << std::endl; + "j_array contains 'key': " << j_array.contains("key"sv) << '\n'; } diff --git a/docs/mkdocs/docs/examples/contains__object_t_key_type.cpp b/docs/mkdocs/docs/examples/contains__object_t_key_type.cpp index a8bc8143d..f72d81b6a 100644 --- a/docs/mkdocs/docs/examples/contains__object_t_key_type.cpp +++ b/docs/mkdocs/docs/examples/contains__object_t_key_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -14,5 +14,5 @@ int main() std::cout << std::boolalpha << "j_object contains 'key': " << j_object.contains("key") << '\n' << "j_object contains 'another': " << j_object.contains("another") << '\n' << - "j_array contains 'key': " << j_array.contains("key") << std::endl; + "j_array contains 'key': " << j_array.contains("key") << '\n'; } diff --git a/docs/mkdocs/docs/examples/count__keytype.c++17.cpp b/docs/mkdocs/docs/examples/count__keytype.c++17.cpp index ec6de0607..0310e17e1 100644 --- a/docs/mkdocs/docs/examples/count__keytype.c++17.cpp +++ b/docs/mkdocs/docs/examples/count__keytype.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/count__object_t_key_type.cpp b/docs/mkdocs/docs/examples/count__object_t_key_type.cpp index a8d54b9d1..9680f94c3 100644 --- a/docs/mkdocs/docs/examples/count__object_t_key_type.cpp +++ b/docs/mkdocs/docs/examples/count__object_t_key_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/crbegin.cpp b/docs/mkdocs/docs/examples/crbegin.cpp index dc3209cf8..2ff1b41f8 100644 --- a/docs/mkdocs/docs/examples/crbegin.cpp +++ b/docs/mkdocs/docs/examples/crbegin.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/crend.cpp b/docs/mkdocs/docs/examples/crend.cpp index dff260931..298a69cb8 100644 --- a/docs/mkdocs/docs/examples/crend.cpp +++ b/docs/mkdocs/docs/examples/crend.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/custom_array_type.cpp b/docs/mkdocs/docs/examples/custom_array_type.cpp index 9765c51e8..fe60d575c 100644 --- a/docs/mkdocs/docs/examples/custom_array_type.cpp +++ b/docs/mkdocs/docs/examples/custom_array_type.cpp @@ -13,6 +13,6 @@ int main() j.push_back(2); j.push_back(3); - std::cout << j.dump() << std::endl; - std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << std::endl; + std::cout << j.dump() << '\n'; + std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << '\n'; } diff --git a/docs/mkdocs/docs/examples/custom_binary_type.cpp b/docs/mkdocs/docs/examples/custom_binary_type.cpp index 0a835d475..38356af6d 100644 --- a/docs/mkdocs/docs/examples/custom_binary_type.cpp +++ b/docs/mkdocs/docs/examples/custom_binary_type.cpp @@ -10,6 +10,6 @@ int main() { const auto j = custom_json::binary({0x01, 0x02, 0x03}); - std::cout << j.dump() << std::endl; - std::cout << std::boolalpha << (custom_json::from_cbor(custom_json::to_cbor(j)) == j) << std::endl; + std::cout << j.dump() << '\n'; + std::cout << std::boolalpha << (custom_json::from_cbor(custom_json::to_cbor(j)) == j) << '\n'; } diff --git a/docs/mkdocs/docs/examples/custom_object_type.cpp b/docs/mkdocs/docs/examples/custom_object_type.cpp index 9d4b43daf..d37363fc5 100644 --- a/docs/mkdocs/docs/examples/custom_object_type.cpp +++ b/docs/mkdocs/docs/examples/custom_object_type.cpp @@ -14,12 +14,12 @@ int main() j["happy"] = true; j["list"] = {1, 2, 3}; - std::cout << j.dump(2) << std::endl; - std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << std::endl; + std::cout << j.dump(2) << '\n'; + std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << '\n'; // custom_object_type has no key_compare member, so object_comparator_t // falls back to its default std::cout << std::boolalpha << std::is_same::value - << std::endl; + << '\n'; } diff --git a/docs/mkdocs/docs/examples/custom_string_type.cpp b/docs/mkdocs/docs/examples/custom_string_type.cpp index 2e71c9378..691848aca 100644 --- a/docs/mkdocs/docs/examples/custom_string_type.cpp +++ b/docs/mkdocs/docs/examples/custom_string_type.cpp @@ -13,6 +13,6 @@ int main() j["happy"] = true; j["list"] = {1, 2, 3}; - std::cout << j.dump(2) << std::endl; - std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << std::endl; + std::cout << j.dump(2) << '\n'; + std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << '\n'; } diff --git a/docs/mkdocs/docs/examples/default_object_comparator_t.cpp b/docs/mkdocs/docs/examples/default_object_comparator_t.cpp index 9f200fe6b..6cb4d4c22 100644 --- a/docs/mkdocs/docs/examples/default_object_comparator_t.cpp +++ b/docs/mkdocs/docs/examples/default_object_comparator_t.cpp @@ -1,11 +1,11 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { std::cout << std::boolalpha << "one < two : " << json::default_object_comparator_t{}("one", "two") << "\n" - << "three < four : " << json::default_object_comparator_t{}("three", "four") << std::endl; + << "three < four : " << json::default_object_comparator_t{}("three", "four") << '\n'; } diff --git a/docs/mkdocs/docs/examples/diagnostic_positions.cpp b/docs/mkdocs/docs/examples/diagnostic_positions.cpp index 259344cd0..5675a1297 100644 --- a/docs/mkdocs/docs/examples/diagnostic_positions.cpp +++ b/docs/mkdocs/docs/examples/diagnostic_positions.cpp @@ -3,7 +3,7 @@ #define JSON_DIAGNOSTIC_POSITIONS 1 #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/diagnostic_positions_exception.cpp b/docs/mkdocs/docs/examples/diagnostic_positions_exception.cpp index 085a25c19..599176364 100644 --- a/docs/mkdocs/docs/examples/diagnostic_positions_exception.cpp +++ b/docs/mkdocs/docs/examples/diagnostic_positions_exception.cpp @@ -3,7 +3,7 @@ #define JSON_DIAGNOSTIC_POSITIONS 1 #include -using json = nlohmann::json; +using nlohmann::json; /* Demonstration of type error exception with diagnostic positions support enabled */ int main() diff --git a/docs/mkdocs/docs/examples/diagnostics_extended.cpp b/docs/mkdocs/docs/examples/diagnostics_extended.cpp index 3b9f484b7..bac1a5363 100644 --- a/docs/mkdocs/docs/examples/diagnostics_extended.cpp +++ b/docs/mkdocs/docs/examples/diagnostics_extended.cpp @@ -3,7 +3,7 @@ # define JSON_DIAGNOSTICS 1 #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/diagnostics_extended_positions.cpp b/docs/mkdocs/docs/examples/diagnostics_extended_positions.cpp index 9a31ac356..22c8685a7 100644 --- a/docs/mkdocs/docs/examples/diagnostics_extended_positions.cpp +++ b/docs/mkdocs/docs/examples/diagnostics_extended_positions.cpp @@ -4,7 +4,7 @@ #define JSON_DIAGNOSTIC_POSITIONS 1 #include -using json = nlohmann::json; +using nlohmann::json; /* Demonstration of type error exception with diagnostic positions support enabled */ int main() diff --git a/docs/mkdocs/docs/examples/diagnostics_standard.cpp b/docs/mkdocs/docs/examples/diagnostics_standard.cpp index eae61a4a7..2c3537d2e 100644 --- a/docs/mkdocs/docs/examples/diagnostics_standard.cpp +++ b/docs/mkdocs/docs/examples/diagnostics_standard.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/diff.cpp b/docs/mkdocs/docs/examples/diff.cpp index ef01332a1..3b089e9a8 100644 --- a/docs/mkdocs/docs/examples/diff.cpp +++ b/docs/mkdocs/docs/examples/diff.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -33,5 +33,5 @@ int main() // output patch and roundtrip result std::cout << std::setw(4) << patch << "\n\n" - << std::setw(4) << patched_source << std::endl; + << std::setw(4) << patched_source << '\n'; } diff --git a/docs/mkdocs/docs/examples/dump.cpp b/docs/mkdocs/docs/examples/dump.cpp index 009c95fd7..c95381f9e 100644 --- a/docs/mkdocs/docs/examples/dump.cpp +++ b/docs/mkdocs/docs/examples/dump.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -33,11 +33,11 @@ int main() json j_invalid = "ä\xA9ü"; try { - std::cout << j_invalid.dump() << std::endl; + std::cout << j_invalid.dump() << '\n'; } catch (const json::type_error& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << '\n'; } std::cout << "string with replaced invalid characters: " diff --git a/docs/mkdocs/docs/examples/emplace.cpp b/docs/mkdocs/docs/examples/emplace.cpp index a5314910f..93f665f39 100644 --- a/docs/mkdocs/docs/examples/emplace.cpp +++ b/docs/mkdocs/docs/examples/emplace.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/emplace_back.cpp b/docs/mkdocs/docs/examples/emplace_back.cpp index e979a945c..a9b055434 100644 --- a/docs/mkdocs/docs/examples/emplace_back.cpp +++ b/docs/mkdocs/docs/examples/emplace_back.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/empty.cpp b/docs/mkdocs/docs/examples/empty.cpp index 6ef6e40ed..7d92ebfed 100644 --- a/docs/mkdocs/docs/examples/empty.cpp +++ b/docs/mkdocs/docs/examples/empty.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/end.cpp b/docs/mkdocs/docs/examples/end.cpp index 47beedb77..01d4700bf 100644 --- a/docs/mkdocs/docs/examples/end.cpp +++ b/docs/mkdocs/docs/examples/end.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/erase__IteratorType.cpp b/docs/mkdocs/docs/examples/erase__IteratorType.cpp index f0d4ec6fb..1b1578ba9 100644 --- a/docs/mkdocs/docs/examples/erase__IteratorType.cpp +++ b/docs/mkdocs/docs/examples/erase__IteratorType.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.cpp b/docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.cpp index 392511ffe..f2b06d690 100644 --- a/docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.cpp +++ b/docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/erase__keytype.c++17.cpp b/docs/mkdocs/docs/examples/erase__keytype.c++17.cpp index c5e4bed5d..f1339941e 100644 --- a/docs/mkdocs/docs/examples/erase__keytype.c++17.cpp +++ b/docs/mkdocs/docs/examples/erase__keytype.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/erase__object_t_key_type.cpp b/docs/mkdocs/docs/examples/erase__object_t_key_type.cpp index 2fd84c86e..140e77ef6 100644 --- a/docs/mkdocs/docs/examples/erase__object_t_key_type.cpp +++ b/docs/mkdocs/docs/examples/erase__object_t_key_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/erase__size_type.cpp b/docs/mkdocs/docs/examples/erase__size_type.cpp index 810062387..b92fed88a 100644 --- a/docs/mkdocs/docs/examples/erase__size_type.cpp +++ b/docs/mkdocs/docs/examples/erase__size_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/error_handler_t.cpp b/docs/mkdocs/docs/examples/error_handler_t.cpp index bf035cea3..1b6945b47 100644 --- a/docs/mkdocs/docs/examples/error_handler_t.cpp +++ b/docs/mkdocs/docs/examples/error_handler_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -9,11 +9,11 @@ int main() json j_invalid = "ä\xA9ü"; try { - std::cout << j_invalid.dump() << std::endl; + std::cout << j_invalid.dump() << '\n'; } catch (const json::type_error& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << '\n'; } std::cout << "string with replaced invalid characters: " diff --git a/docs/mkdocs/docs/examples/exception.cpp b/docs/mkdocs/docs/examples/exception.cpp index 3e5a23b3c..17ed0584b 100644 --- a/docs/mkdocs/docs/examples/exception.cpp +++ b/docs/mkdocs/docs/examples/exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,6 +15,6 @@ int main() { // output exception information std::cout << "message: " << e.what() << '\n' - << "exception id: " << e.id << std::endl; + << "exception id: " << e.id << '\n'; } } diff --git a/docs/mkdocs/docs/examples/find__keytype.c++17.cpp b/docs/mkdocs/docs/examples/find__keytype.c++17.cpp index da94cf0ad..f7efb5d6f 100644 --- a/docs/mkdocs/docs/examples/find__keytype.c++17.cpp +++ b/docs/mkdocs/docs/examples/find__keytype.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/find__object_t_key_type.cpp b/docs/mkdocs/docs/examples/find__object_t_key_type.cpp index 685ba7763..e1e226cb4 100644 --- a/docs/mkdocs/docs/examples/find__object_t_key_type.cpp +++ b/docs/mkdocs/docs/examples/find__object_t_key_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/flatten.cpp b/docs/mkdocs/docs/examples/flatten.cpp index 83f3ff6c3..a0ae57ec6 100644 --- a/docs/mkdocs/docs/examples/flatten.cpp +++ b/docs/mkdocs/docs/examples/flatten.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/flatten__empty.cpp b/docs/mkdocs/docs/examples/flatten__empty.cpp index 1dd52d551..b802c060d 100644 --- a/docs/mkdocs/docs/examples/flatten__empty.cpp +++ b/docs/mkdocs/docs/examples/flatten__empty.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/format_as.cpp b/docs/mkdocs/docs/examples/format_as.cpp index 0ea3a5ede..e392a6251 100644 --- a/docs/mkdocs/docs/examples/format_as.cpp +++ b/docs/mkdocs/docs/examples/format_as.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -12,5 +12,5 @@ int main() // fmt::format/fmt::print would find it auto j_str = format_as(j); - std::cout << j_str << std::endl; + std::cout << j_str << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_bjdata.cpp b/docs/mkdocs/docs/examples/from_bjdata.cpp index 961164c29..3de031c27 100644 --- a/docs/mkdocs/docs/examples/from_bjdata.cpp +++ b/docs/mkdocs/docs/examples/from_bjdata.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -16,5 +16,5 @@ int main() json j = json::from_bjdata(v); // print the deserialized JSON value - std::cout << std::setw(2) << j << std::endl; + std::cout << std::setw(2) << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_bon8.cpp b/docs/mkdocs/docs/examples/from_bon8.cpp index 83ab82060..320f5b3a0 100644 --- a/docs/mkdocs/docs/examples/from_bon8.cpp +++ b/docs/mkdocs/docs/examples/from_bon8.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -17,5 +17,5 @@ int main() json j = json::from_bon8(v); // print the deserialized JSON value - std::cout << std::setw(2) << j << std::endl; + std::cout << std::setw(2) << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_bson.cpp b/docs/mkdocs/docs/examples/from_bson.cpp index c9d9fdfa6..8d7d2f951 100644 --- a/docs/mkdocs/docs/examples/from_bson.cpp +++ b/docs/mkdocs/docs/examples/from_bson.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -17,5 +17,5 @@ int main() json j = json::from_bson(v); // print the deserialized JSON value - std::cout << std::setw(2) << j << std::endl; + std::cout << std::setw(2) << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_cbor.cpp b/docs/mkdocs/docs/examples/from_cbor.cpp index e685329ef..4ef881acb 100644 --- a/docs/mkdocs/docs/examples/from_cbor.cpp +++ b/docs/mkdocs/docs/examples/from_cbor.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -16,5 +16,5 @@ int main() json j = json::from_cbor(v); // print the deserialized JSON value - std::cout << std::setw(2) << j << std::endl; + std::cout << std::setw(2) << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_json__default_constructible.cpp b/docs/mkdocs/docs/examples/from_json__default_constructible.cpp index 17c0551c8..33c75f95a 100644 --- a/docs/mkdocs/docs/examples/from_json__default_constructible.cpp +++ b/docs/mkdocs/docs/examples/from_json__default_constructible.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -33,5 +33,5 @@ int main() auto p = j.get(); - std::cout << p.name << " (" << p.age << ") lives in " << p.address << std::endl; + std::cout << p.name << " (" << p.age << ") lives in " << p.address << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_json__non_default_constructible.cpp b/docs/mkdocs/docs/examples/from_json__non_default_constructible.cpp index 6cb86153c..73eba7584 100644 --- a/docs/mkdocs/docs/examples/from_json__non_default_constructible.cpp +++ b/docs/mkdocs/docs/examples/from_json__non_default_constructible.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -49,5 +49,5 @@ int main() auto p = j.get(); - std::cout << p.name << " (" << p.age << ") lives in " << p.address << std::endl; + std::cout << p.name << " (" << p.age << ") lives in " << p.address << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_msgpack.cpp b/docs/mkdocs/docs/examples/from_msgpack.cpp index 5c2183f1e..f2a1db51e 100644 --- a/docs/mkdocs/docs/examples/from_msgpack.cpp +++ b/docs/mkdocs/docs/examples/from_msgpack.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -16,5 +16,5 @@ int main() json j = json::from_msgpack(v); // print the deserialized JSON value - std::cout << std::setw(2) << j << std::endl; + std::cout << std::setw(2) << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/from_ubjson.cpp b/docs/mkdocs/docs/examples/from_ubjson.cpp index 1e85e4e36..9046ac406 100644 --- a/docs/mkdocs/docs/examples/from_ubjson.cpp +++ b/docs/mkdocs/docs/examples/from_ubjson.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -16,5 +16,5 @@ int main() json j = json::from_ubjson(v); // print the deserialized JSON value - std::cout << std::setw(2) << j << std::endl; + std::cout << std::setw(2) << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/front.cpp b/docs/mkdocs/docs/examples/front.cpp index a0f630689..4f53be276 100644 --- a/docs/mkdocs/docs/examples/front.cpp +++ b/docs/mkdocs/docs/examples/front.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/get__BasicJsonType.cpp b/docs/mkdocs/docs/examples/get__BasicJsonType.cpp index d63458c0b..fb70d402e 100644 --- a/docs/mkdocs/docs/examples/get__BasicJsonType.cpp +++ b/docs/mkdocs/docs/examples/get__BasicJsonType.cpp @@ -1,8 +1,8 @@ #include #include -using json = nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::json; +using nlohmann::ordered_json; int main() { diff --git a/docs/mkdocs/docs/examples/get__PointerType.cpp b/docs/mkdocs/docs/examples/get__PointerType.cpp index 2f32ed7af..aa7440482 100644 --- a/docs/mkdocs/docs/examples/get__PointerType.cpp +++ b/docs/mkdocs/docs/examples/get__PointerType.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -9,11 +9,11 @@ int main() json value = 17; // explicitly getting pointers - auto p1 = value.get(); - auto p2 = value.get(); - auto p3 = value.get(); - auto p4 = value.get(); - auto p5 = value.get(); + const auto* p1 = value.get(); + auto* p2 = value.get(); + auto* p3 = value.get(); + const auto* p4 = value.get(); + auto* p5 = value.get(); // print the pointees std::cout << *p1 << ' ' << *p2 << ' ' << *p3 << ' ' << *p4 << '\n'; diff --git a/docs/mkdocs/docs/examples/get__ValueType_const.cpp b/docs/mkdocs/docs/examples/get__ValueType_const.cpp index 7b0ca47bb..19d9b8d9f 100644 --- a/docs/mkdocs/docs/examples/get__ValueType_const.cpp +++ b/docs/mkdocs/docs/examples/get__ValueType_const.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -43,7 +43,7 @@ int main() } std::cout << "\n\n"; - for (auto i : v8) + for (const auto& i : v8) { std::cout << i.first << ": " << i.second << '\n'; } diff --git a/docs/mkdocs/docs/examples/get_allocator.cpp b/docs/mkdocs/docs/examples/get_allocator.cpp index 35079a10c..186b102a8 100644 --- a/docs/mkdocs/docs/examples/get_allocator.cpp +++ b/docs/mkdocs/docs/examples/get_allocator.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -11,7 +11,7 @@ int main() json* j = traits_t::allocate(alloc, 1); traits_t::construct(alloc, j, "Hello, world!"); - std::cout << *j << std::endl; + std::cout << *j << '\n'; traits_t::destroy(alloc, j); traits_t::deallocate(alloc, j, 1); diff --git a/docs/mkdocs/docs/examples/get_binary.cpp b/docs/mkdocs/docs/examples/get_binary.cpp index 617ce6096..6fff997bb 100644 --- a/docs/mkdocs/docs/examples/get_binary.cpp +++ b/docs/mkdocs/docs/examples/get_binary.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -12,5 +12,5 @@ int main() json j = json::binary(vec, 42); // output type and subtype - std::cout << "type: " << j.type_name() << ", subtype: " << j.get_binary().subtype() << std::endl; + std::cout << "type: " << j.type_name() << ", subtype: " << j.get_binary().subtype() << '\n'; } diff --git a/docs/mkdocs/docs/examples/get_ptr.cpp b/docs/mkdocs/docs/examples/get_ptr.cpp index 564ce0f4e..0826523b9 100644 --- a/docs/mkdocs/docs/examples/get_ptr.cpp +++ b/docs/mkdocs/docs/examples/get_ptr.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -9,11 +9,11 @@ int main() json value = 17; // explicitly getting pointers - auto p1 = value.get_ptr(); - auto p2 = value.get_ptr(); - auto p3 = value.get_ptr(); - auto p4 = value.get_ptr(); - auto p5 = value.get_ptr(); + const auto* p1 = value.get_ptr(); + auto* p2 = value.get_ptr(); + auto* p3 = value.get_ptr(); + const auto* p4 = value.get_ptr(); + auto* p5 = value.get_ptr(); // print the pointees std::cout << *p1 << ' ' << *p2 << ' ' << *p3 << ' ' << *p4 << '\n'; diff --git a/docs/mkdocs/docs/examples/get_ref.cpp b/docs/mkdocs/docs/examples/get_ref.cpp index 0183a6537..706b45708 100644 --- a/docs/mkdocs/docs/examples/get_ref.cpp +++ b/docs/mkdocs/docs/examples/get_ref.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/get_to.cpp b/docs/mkdocs/docs/examples/get_to.cpp index cdbe347c0..553c80c06 100644 --- a/docs/mkdocs/docs/examples/get_to.cpp +++ b/docs/mkdocs/docs/examples/get_to.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -52,7 +52,7 @@ int main() } std::cout << "\n\n"; - for (auto i : v8) + for (const auto& i : v8) { std::cout << i.first << ": " << i.second << '\n'; } diff --git a/docs/mkdocs/docs/examples/insert.cpp b/docs/mkdocs/docs/examples/insert.cpp index 4ee609876..de6b03d61 100644 --- a/docs/mkdocs/docs/examples/insert.cpp +++ b/docs/mkdocs/docs/examples/insert.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/insert__count.cpp b/docs/mkdocs/docs/examples/insert__count.cpp index ce33b93e4..43e1e3164 100644 --- a/docs/mkdocs/docs/examples/insert__count.cpp +++ b/docs/mkdocs/docs/examples/insert__count.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/insert__ilist.cpp b/docs/mkdocs/docs/examples/insert__ilist.cpp index a20766a14..9415b6887 100644 --- a/docs/mkdocs/docs/examples/insert__ilist.cpp +++ b/docs/mkdocs/docs/examples/insert__ilist.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/insert__range.cpp b/docs/mkdocs/docs/examples/insert__range.cpp index 92fe63b0d..69bf25069 100644 --- a/docs/mkdocs/docs/examples/insert__range.cpp +++ b/docs/mkdocs/docs/examples/insert__range.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/insert__range_object.cpp b/docs/mkdocs/docs/examples/insert__range_object.cpp index 97373d3b4..f2cc9627c 100644 --- a/docs/mkdocs/docs/examples/insert__range_object.cpp +++ b/docs/mkdocs/docs/examples/insert__range_object.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/invalid_iterator.cpp b/docs/mkdocs/docs/examples/invalid_iterator.cpp index ecde12e62..138fdbc7e 100644 --- a/docs/mkdocs/docs/examples/invalid_iterator.cpp +++ b/docs/mkdocs/docs/examples/invalid_iterator.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -16,6 +16,6 @@ int main() { // output exception information std::cout << "message: " << e.what() << '\n' - << "exception id: " << e.id << std::endl; + << "exception id: " << e.id << '\n'; } } diff --git a/docs/mkdocs/docs/examples/is_array.cpp b/docs/mkdocs/docs/examples/is_array.cpp index 8ecc45035..c1ef39b8e 100644 --- a/docs/mkdocs/docs/examples/is_array.cpp +++ b/docs/mkdocs/docs/examples/is_array.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_binary.cpp b/docs/mkdocs/docs/examples/is_binary.cpp index d7f049ec4..6fe4d5006 100644 --- a/docs/mkdocs/docs/examples/is_binary.cpp +++ b/docs/mkdocs/docs/examples/is_binary.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_boolean.cpp b/docs/mkdocs/docs/examples/is_boolean.cpp index 0b7981955..e48d1ef5c 100644 --- a/docs/mkdocs/docs/examples/is_boolean.cpp +++ b/docs/mkdocs/docs/examples/is_boolean.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_discarded.cpp b/docs/mkdocs/docs/examples/is_discarded.cpp index 09016655e..9bfcba039 100644 --- a/docs/mkdocs/docs/examples/is_discarded.cpp +++ b/docs/mkdocs/docs/examples/is_discarded.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_discarded__parse.cpp b/docs/mkdocs/docs/examples/is_discarded__parse.cpp index feaca2363..2286c15e6 100644 --- a/docs/mkdocs/docs/examples/is_discarded__parse.cpp +++ b/docs/mkdocs/docs/examples/is_discarded__parse.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_null.cpp b/docs/mkdocs/docs/examples/is_null.cpp index 8a8433260..891acb1be 100644 --- a/docs/mkdocs/docs/examples/is_null.cpp +++ b/docs/mkdocs/docs/examples/is_null.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_number.cpp b/docs/mkdocs/docs/examples/is_number.cpp index f107a048a..0384891c1 100644 --- a/docs/mkdocs/docs/examples/is_number.cpp +++ b/docs/mkdocs/docs/examples/is_number.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_number_float.cpp b/docs/mkdocs/docs/examples/is_number_float.cpp index bba2b446f..f3804cf9d 100644 --- a/docs/mkdocs/docs/examples/is_number_float.cpp +++ b/docs/mkdocs/docs/examples/is_number_float.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_number_integer.cpp b/docs/mkdocs/docs/examples/is_number_integer.cpp index 8d6a5ae16..141190b47 100644 --- a/docs/mkdocs/docs/examples/is_number_integer.cpp +++ b/docs/mkdocs/docs/examples/is_number_integer.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_number_unsigned.cpp b/docs/mkdocs/docs/examples/is_number_unsigned.cpp index b52ac6b31..332aff2f0 100644 --- a/docs/mkdocs/docs/examples/is_number_unsigned.cpp +++ b/docs/mkdocs/docs/examples/is_number_unsigned.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_object.cpp b/docs/mkdocs/docs/examples/is_object.cpp index a0216fd21..fa0b16a32 100644 --- a/docs/mkdocs/docs/examples/is_object.cpp +++ b/docs/mkdocs/docs/examples/is_object.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_primitive.cpp b/docs/mkdocs/docs/examples/is_primitive.cpp index af3968e83..1aaa871d1 100644 --- a/docs/mkdocs/docs/examples/is_primitive.cpp +++ b/docs/mkdocs/docs/examples/is_primitive.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_string.cpp b/docs/mkdocs/docs/examples/is_string.cpp index c89f550b0..6aaae09dd 100644 --- a/docs/mkdocs/docs/examples/is_string.cpp +++ b/docs/mkdocs/docs/examples/is_string.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/is_structured.cpp b/docs/mkdocs/docs/examples/is_structured.cpp index 41947b1be..2b8939d2b 100644 --- a/docs/mkdocs/docs/examples/is_structured.cpp +++ b/docs/mkdocs/docs/examples/is_structured.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/items.cpp b/docs/mkdocs/docs/examples/items.cpp index 9cd2b51b1..5d3786c5f 100644 --- a/docs/mkdocs/docs/examples/items.cpp +++ b/docs/mkdocs/docs/examples/items.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -10,13 +10,13 @@ int main() json j_array = {1, 2, 4, 8, 16}; // example for an object - for (auto& x : j_object.items()) + for (const auto& x : j_object.items()) { std::cout << "key: " << x.key() << ", value: " << x.value() << '\n'; } // example for an array - for (auto& x : j_array.items()) + for (const auto& x : j_array.items()) { std::cout << "key: " << x.key() << ", value: " << x.value() << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_base_class_t.cpp b/docs/mkdocs/docs/examples/json_base_class_t.cpp index 1dafd417f..678e22dd4 100644 --- a/docs/mkdocs/docs/examples/json_base_class_t.cpp +++ b/docs/mkdocs/docs/examples/json_base_class_t.cpp @@ -24,7 +24,7 @@ void visitor_adaptor_with_metadata::visit(const Fnc& fnc) const template void visitor_adaptor_with_metadata::do_visit(const Ptr& ptr, const Fnc& fnc) const { - using value_t = nlohmann::detail::value_t; + using nlohmann::detail::value_t; const json& j = *static_cast(this); switch (j.type()) { diff --git a/docs/mkdocs/docs/examples/json_document.cpp b/docs/mkdocs/docs/examples/json_document.cpp index 01f2847e2..1613526ac 100644 --- a/docs/mkdocs/docs/examples/json_document.cpp +++ b/docs/mkdocs/docs/examples/json_document.cpp @@ -1,7 +1,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/json_editable_document.cpp b/docs/mkdocs/docs/examples/json_editable_document.cpp index bf08c0d9b..1ae760183 100644 --- a/docs/mkdocs/docs/examples/json_editable_document.cpp +++ b/docs/mkdocs/docs/examples/json_editable_document.cpp @@ -1,9 +1,9 @@ #include #include -using json = nlohmann::json; -using json_editable_document = nlohmann::json_editable_document; -using json_editable_view = nlohmann::json_editable_view; +using nlohmann::json; +using nlohmann::json_editable_document; +using nlohmann::json_editable_view; int main() { diff --git a/docs/mkdocs/docs/examples/json_lines.cpp b/docs/mkdocs/docs/examples/json_lines.cpp index 233c81a4a..a8da39d80 100644 --- a/docs/mkdocs/docs/examples/json_lines.cpp +++ b/docs/mkdocs/docs/examples/json_lines.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -17,6 +17,6 @@ int main() std::string line; while (std::getline(input, line)) { - std::cout << json::parse(line) << std::endl; + std::cout << json::parse(line) << '\n'; } } diff --git a/docs/mkdocs/docs/examples/json_pointer.cpp b/docs/mkdocs/docs/examples/json_pointer.cpp index 8705cf498..d8a43601b 100644 --- a/docs/mkdocs/docs/examples/json_pointer.cpp +++ b/docs/mkdocs/docs/examples/json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/json_pointer__back.cpp b/docs/mkdocs/docs/examples/json_pointer__back.cpp index dd3b210bf..04f663818 100644 --- a/docs/mkdocs/docs/examples/json_pointer__back.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__back.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -11,5 +11,5 @@ int main() // call empty() std::cout << "last reference token of \"" << ptr1 << "\" is \"" << ptr1.back() << "\"\n" - << "last reference token of \"" << ptr2 << "\" is \"" << ptr2.back() << "\"" << std::endl; + << "last reference token of \"" << ptr2 << "\" is \"" << ptr2.back() << "\"" << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__empty.cpp b/docs/mkdocs/docs/examples/json_pointer__empty.cpp index 57257e8b1..f59955533 100644 --- a/docs/mkdocs/docs/examples/json_pointer__empty.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__empty.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -16,5 +16,5 @@ int main() << "\"" << ptr0 << "\": " << ptr0.empty() << '\n' << "\"" << ptr1 << "\": " << ptr1.empty() << '\n' << "\"" << ptr2 << "\": " << ptr2.empty() << '\n' - << "\"" << ptr3 << "\": " << ptr3.empty() << std::endl; + << "\"" << ptr3 << "\": " << ptr3.empty() << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__front.cpp b/docs/mkdocs/docs/examples/json_pointer__front.cpp index 42c76ffec..6d0fd400d 100644 --- a/docs/mkdocs/docs/examples/json_pointer__front.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__front.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -11,5 +11,5 @@ int main() // call empty() std::cout << "first reference token of \"" << ptr1 << "\" is \"" << ptr1.front() << "\"\n" - << "first reference token of \"" << ptr2 << "\" is \"" << ptr2.front() << "\"" << std::endl; + << "first reference token of \"" << ptr2 << "\" is \"" << ptr2.front() << "\"" << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__operator__equal.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__equal.cpp index dce6df03c..e59bc453c 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator__equal.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator__equal.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,5 +15,5 @@ int main() << "\"" << ptr0 << "\" == \"" << ptr0 << "\": " << (ptr0 == ptr0) << '\n' << "\"" << ptr0 << "\" == \"" << ptr1 << "\": " << (ptr0 == ptr1) << '\n' << "\"" << ptr1 << "\" == \"" << ptr2 << "\": " << (ptr1 == ptr2) << '\n' - << "\"" << ptr2 << "\" == \"" << ptr2 << "\": " << (ptr2 == ptr2) << std::endl; + << "\"" << ptr2 << "\" == \"" << ptr2 << "\": " << (ptr2 == ptr2) << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.cpp index af8ec5a29..28197c3db 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -20,14 +20,14 @@ int main() std::cout << std::boolalpha << "\"" << ptr0 << "\" == \"" << str0 << "\": " << (ptr0 == str0) << '\n' << "\"" << str0 << "\" == \"" << ptr1 << "\": " << (str0 == ptr1) << '\n' - << "\"" << ptr2 << "\" == \"" << str1 << "\": " << (ptr2 == str1) << std::endl; + << "\"" << ptr2 << "\" == \"" << str1 << "\": " << (ptr2 == str1) << '\n'; try { - std::cout << "\"" << str2 << "\" == \"" << ptr2 << "\": " << (str2 == ptr2) << std::endl; + std::cout << "\"" << str2 << "\" == \"" << ptr2 << "\": " << (str2 == ptr2) << '\n'; } catch (const json::parse_error& ex) { - std::cout << ex.what() << std::endl; + std::cout << ex.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/json_pointer__operator__notequal.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__notequal.cpp index 9bbdd5310..624b4e053 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator__notequal.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator__notequal.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,5 +15,5 @@ int main() << "\"" << ptr0 << "\" != \"" << ptr0 << "\": " << (ptr0 != ptr0) << '\n' << "\"" << ptr0 << "\" != \"" << ptr1 << "\": " << (ptr0 != ptr1) << '\n' << "\"" << ptr1 << "\" != \"" << ptr2 << "\": " << (ptr1 != ptr2) << '\n' - << "\"" << ptr2 << "\" != \"" << ptr2 << "\": " << (ptr2 != ptr2) << std::endl; + << "\"" << ptr2 << "\" != \"" << ptr2 << "\": " << (ptr2 != ptr2) << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.cpp index b9b898728..abf92adc3 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -19,14 +19,14 @@ int main() std::cout << std::boolalpha << "\"" << ptr0 << "\" != \"" << str0 << "\": " << (ptr0 != str0) << '\n' << "\"" << str0 << "\" != \"" << ptr1 << "\": " << (str0 != ptr1) << '\n' - << "\"" << ptr2 << "\" != \"" << str1 << "\": " << (ptr2 != str1) << std::endl; + << "\"" << ptr2 << "\" != \"" << str1 << "\": " << (ptr2 != str1) << '\n'; try { - std::cout << "\"" << str2 << "\" != \"" << ptr2 << "\": " << (str2 != ptr2) << std::endl; + std::cout << "\"" << str2 << "\" != \"" << ptr2 << "\": " << (str2 != ptr2) << '\n'; } catch (const json::parse_error& ex) { - std::cout << ex.what() << std::endl; + std::cout << ex.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/json_pointer__operator_add.cpp b/docs/mkdocs/docs/examples/json_pointer__operator_add.cpp index 14bd74561..9526cc690 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator_add.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator_add.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -19,5 +19,5 @@ int main() // append an array index ptr /= 42; - std::cout << "\"" << ptr << "\"" << std::endl; + std::cout << "\"" << ptr << "\"" << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__operator_add_binary.cpp b/docs/mkdocs/docs/examples/json_pointer__operator_add_binary.cpp index d26a0d171..55941da25 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator_add_binary.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator_add_binary.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,5 +15,5 @@ int main() std::cout << "\"" << ptr / "fob" << "\"\n"; // append an array index - std::cout << "\"" << ptr / 42 << "\"" << std::endl; + std::cout << "\"" << ptr / 42 << "\"" << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__operator_spaceship.c++20.cpp b/docs/mkdocs/docs/examples/json_pointer__operator_spaceship.c++20.cpp index a2354da73..374c07dd6 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator_spaceship.c++20.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator_spaceship.c++20.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; const char* to_string(const std::strong_ordering& so) { diff --git a/docs/mkdocs/docs/examples/json_pointer__operator_string_t.cpp b/docs/mkdocs/docs/examples/json_pointer__operator_string_t.cpp index 56f213020..964797230 100644 --- a/docs/mkdocs/docs/examples/json_pointer__operator_string_t.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__operator_string_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,5 +15,5 @@ int main() s += "\n"; s += ptr2; - std::cout << s << std::endl; + std::cout << s << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__parent_pointer.cpp b/docs/mkdocs/docs/examples/json_pointer__parent_pointer.cpp index ef9df4534..f321050dd 100644 --- a/docs/mkdocs/docs/examples/json_pointer__parent_pointer.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__parent_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -14,5 +14,5 @@ int main() std::cout << std::boolalpha << "parent of \"" << ptr1 << "\" is \"" << ptr1.parent_pointer() << "\"\n" << "parent of \"" << ptr2 << "\" is \"" << ptr2.parent_pointer() << "\"\n" - << "parent of \"" << ptr3 << "\" is \"" << ptr3.parent_pointer() << "\"" << std::endl; + << "parent of \"" << ptr3 << "\" is \"" << ptr3.parent_pointer() << "\"" << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__pop_back.cpp b/docs/mkdocs/docs/examples/json_pointer__pop_back.cpp index fd077b7e9..ebd43b23d 100644 --- a/docs/mkdocs/docs/examples/json_pointer__pop_back.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__pop_back.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/json_pointer__pop_front.cpp b/docs/mkdocs/docs/examples/json_pointer__pop_front.cpp index 59403668c..dfadd63c6 100644 --- a/docs/mkdocs/docs/examples/json_pointer__pop_front.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__pop_front.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/json_pointer__push_back.cpp b/docs/mkdocs/docs/examples/json_pointer__push_back.cpp index e6b59a125..76b27ebc4 100644 --- a/docs/mkdocs/docs/examples/json_pointer__push_back.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__push_back.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/json_pointer__push_front.cpp b/docs/mkdocs/docs/examples/json_pointer__push_front.cpp index 7bcdccb45..723edb7c8 100644 --- a/docs/mkdocs/docs/examples/json_pointer__push_front.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__push_front.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/json_pointer__string_t.cpp b/docs/mkdocs/docs/examples/json_pointer__string_t.cpp index fbe0f179e..48e9d22aa 100644 --- a/docs/mkdocs/docs/examples/json_pointer__string_t.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__string_t.cpp @@ -1,13 +1,13 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { json::json_pointer::string_t s = "This is a string."; - std::cout << s << std::endl; + std::cout << s << '\n'; - std::cout << std::boolalpha << std::is_same::value << std::endl; + std::cout << std::boolalpha << std::is_same::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_pointer__to_string.cpp b/docs/mkdocs/docs/examples/json_pointer__to_string.cpp index 31d35a724..af52355a5 100644 --- a/docs/mkdocs/docs/examples/json_pointer__to_string.cpp +++ b/docs/mkdocs/docs/examples/json_pointer__to_string.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -30,5 +30,5 @@ int main() << "\"" << ptr9.to_string() << "\"\n" << "\"" << ptr10.to_string() << "\"\n" << "\"" << ptr11.to_string() << "\"\n" - << "\"" << ptr12.to_string() << "\"" << std::endl; + << "\"" << ptr12.to_string() << "\"" << '\n'; } diff --git a/docs/mkdocs/docs/examples/json_view_ownership.cpp b/docs/mkdocs/docs/examples/json_view_ownership.cpp index fa0e8262a..7fa2afdf3 100644 --- a/docs/mkdocs/docs/examples/json_view_ownership.cpp +++ b/docs/mkdocs/docs/examples/json_view_ownership.cpp @@ -3,7 +3,7 @@ #include #include -using json_document = nlohmann::json_document; +using nlohmann::json_document; int main() { diff --git a/docs/mkdocs/docs/examples/max_size.cpp b/docs/mkdocs/docs/examples/max_size.cpp index c2ffc5468..7477acab7 100644 --- a/docs/mkdocs/docs/examples/max_size.cpp +++ b/docs/mkdocs/docs/examples/max_size.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/merge_diff.cpp b/docs/mkdocs/docs/examples/merge_diff.cpp index 22ae9b087..906b5786f 100644 --- a/docs/mkdocs/docs/examples/merge_diff.cpp +++ b/docs/mkdocs/docs/examples/merge_diff.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -47,5 +47,5 @@ int main() // output patch and roundtrip result std::cout << std::setw(4) << patch << "\n\n" - << std::setw(4) << patched_source << std::endl; + << std::setw(4) << patched_source << '\n'; } diff --git a/docs/mkdocs/docs/examples/merge_patch.cpp b/docs/mkdocs/docs/examples/merge_patch.cpp index f3fee1ed1..aaed7d923 100644 --- a/docs/mkdocs/docs/examples/merge_patch.cpp +++ b/docs/mkdocs/docs/examples/merge_patch.cpp @@ -2,7 +2,7 @@ #include #include // for std::setw -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -37,5 +37,5 @@ int main() document.merge_patch(patch); // output original and patched document - std::cout << std::setw(4) << document << std::endl; + std::cout << std::setw(4) << document << '\n'; } diff --git a/docs/mkdocs/docs/examples/meta.cpp b/docs/mkdocs/docs/examples/meta.cpp index a051575ba..f0de003b4 100644 --- a/docs/mkdocs/docs/examples/meta.cpp +++ b/docs/mkdocs/docs/examples/meta.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.cpp index 0dca78162..30f792b86 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.cpp @@ -33,5 +33,5 @@ int main() B example(23, 42, 3.142, 1.777); json example_json = example; - std::cout << std::setw(4) << example_json << std::endl; + std::cout << std::setw(4) << example_json << '\n'; } \ No newline at end of file diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.cpp index ce13ff114..832f2cbdd 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -43,7 +43,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -57,6 +57,6 @@ int main() } catch (const json::exception& e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.cpp index ab8657801..3a0392717 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -29,7 +29,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -43,6 +43,6 @@ int main() } catch (const json::exception& e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp index dd6a9a679..a61d57e93 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -35,5 +35,5 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp index 8213f9063..ca2dcaa22 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -29,5 +29,5 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp index 2713444b6..a06cdff0c 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -44,7 +44,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -53,5 +53,5 @@ int main() // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; auto p3 = j3.get(); - std::cout << "roundtrip: " << json(p3) << std::endl; + std::cout << "roundtrip: " << json(p3) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp index 851a3582f..764c8dd81 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -29,7 +29,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -38,5 +38,5 @@ int main() // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; auto p3 = j3.get(); - std::cout << "roundtrip: " << json(p3) << std::endl; + std::cout << "roundtrip: " << json(p3) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp index dfbdde4ac..d6089f165 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -36,7 +36,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -50,6 +50,6 @@ int main() } catch (const json::exception& e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp index cdf90dc2c..fea57ea24 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -22,7 +22,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -36,6 +36,6 @@ int main() } catch (const json::exception& e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp index ead75b74e..6f20b1067 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -28,5 +28,5 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp index 7aef23af6..978d14bde 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -22,5 +22,5 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp index 23384b70c..a6bcc624a 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -42,7 +42,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -51,5 +51,5 @@ int main() // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; auto p3 = j3.get(); - std::cout << "roundtrip: " << json(p3) << std::endl; + std::cout << "roundtrip: " << json(p3) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp index aa9bc53b4..d33d5355d 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -27,7 +27,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; @@ -36,5 +36,5 @@ int main() // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; auto p3 = j3.get(); - std::cout << "roundtrip: " << json(p3) << std::endl; + std::cout << "roundtrip: " << json(p3) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp index d92f13c42..f022cffd8 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -36,7 +36,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"json_address": "742 Evergreen Terrace", "json_age": 40, "json_name": "Homer Simpson"})"_json; @@ -50,6 +50,6 @@ int main() } catch (const json::exception& e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp index 36114e724..bb3712486 100644 --- a/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; namespace ns @@ -22,7 +22,7 @@ int main() // serialization: person -> json json j = p; - std::cout << "serialization: " << j << std::endl; + std::cout << "serialization: " << j << '\n'; // deserialization: json -> person json j2 = R"({"json_address": "742 Evergreen Terrace", "json_age": 40, "json_name": "Homer Simpson"})"_json; @@ -36,6 +36,6 @@ int main() } catch (const json::exception& e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_namespace.cpp b/docs/mkdocs/docs/examples/nlohmann_json_namespace.cpp index 4bad91f4b..6cb07f584 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_namespace.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_namespace.cpp @@ -2,7 +2,7 @@ #include // possible use case: use NLOHMANN_JSON_NAMESPACE instead of nlohmann -using json = NLOHMANN_JSON_NAMESPACE::json; +using NLOHMANN_JSON_NAMESPACE::json; // macro needed to output the NLOHMANN_JSON_NAMESPACE as string literal #define Q(x) #x @@ -10,5 +10,5 @@ using json = NLOHMANN_JSON_NAMESPACE::json; int main() { - std::cout << QUOTE(NLOHMANN_JSON_NAMESPACE) << std::endl; + std::cout << QUOTE(NLOHMANN_JSON_NAMESPACE) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.cpp b/docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.cpp index 9385d593d..924bcb239 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.cpp @@ -29,5 +29,5 @@ int main() NLOHMANN_JSON_NAMESPACE::json j; j.push_back(o1); j.push_back(o2); - std::cout << j << std::endl; + std::cout << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.cpp b/docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.cpp index 97948dd7e..9f1bd9ad6 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.cpp @@ -9,5 +9,5 @@ int main() { - std::cout << QUOTE(NLOHMANN_JSON_NAMESPACE) << std::endl; + std::cout << QUOTE(NLOHMANN_JSON_NAMESPACE) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.cpp index f9e472c64..5a59e826a 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -39,7 +39,7 @@ int main() json j_stopped = ns::TS_STOPPED; json j_red = ns::Color::red; std::cout << "ns::TS_STOPPED -> " << j_stopped - << ", ns::Color::red -> " << j_red << std::endl; + << ", ns::Color::red -> " << j_red << '\n'; // deserialization json j_running = "running"; @@ -47,7 +47,7 @@ int main() auto running = j_running.get(); auto blue = j_blue.get(); std::cout << j_running << " -> " << running - << ", " << j_blue << " -> " << static_cast(blue) << std::endl; + << ", " << j_blue << " -> " << static_cast(blue) << '\n'; // deserializing undefined JSON value to enum // (where the first map entry above is the default) @@ -55,5 +55,5 @@ int main() auto invalid = j_pi.get(); auto unknown = j_pi.get(); std::cout << j_pi << " -> " << invalid << ", " - << j_pi << " -> " << static_cast(unknown) << std::endl; + << j_pi << " -> " << static_cast(unknown) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.cpp index fd27226ca..e7b0fa966 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -22,12 +22,12 @@ int main() { // serialization json j_red = ns::Color::red; - std::cout << static_cast(ns::Color::red) << " -> " << j_red << std::endl; + std::cout << static_cast(ns::Color::red) << " -> " << j_red << '\n'; // deserialization json j_rot = "rot"; auto rot = j_rot.get(); auto red = j_red.get(); - std::cout << j_rot << " -> " << static_cast(rot) << std::endl; - std::cout << j_red << " -> " << static_cast(red) << std::endl; + std::cout << j_rot << " -> " << static_cast(rot) << '\n'; + std::cout << j_red << " -> " << static_cast(red) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp index fa2572dec..1017a729f 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -39,7 +39,7 @@ int main() json j_stopped = ns::TS_STOPPED; json j_red = ns::Color::red; std::cout << "ns::TS_STOPPED -> " << j_stopped - << ", ns::Color::red -> " << j_red << std::endl; + << ", ns::Color::red -> " << j_red << '\n'; // deserialization json j_running = "running"; @@ -47,6 +47,6 @@ int main() auto running = j_running.get(); auto blue = j_blue.get(); std::cout << j_running << " -> " << running - << ", " << j_blue << " -> " << static_cast(blue) << std::endl; + << ", " << j_blue << " -> " << static_cast(blue) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp index 7f766b7a9..263fe30a6 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -22,12 +22,12 @@ int main() { // serialization json j_red = ns::Color::red; - std::cout << static_cast(ns::Color::red) << " -> " << j_red << std::endl; + std::cout << static_cast(ns::Color::red) << " -> " << j_red << '\n'; // deserialization json j_rot = "rot"; auto rot = j_rot.get(); auto red = j_red.get(); - std::cout << j_rot << " -> " << static_cast(rot) << std::endl; - std::cout << j_red << " -> " << static_cast(red) << std::endl; + std::cout << j_rot << " -> " << static_cast(rot) << '\n'; + std::cout << j_red << " -> " << static_cast(red) << '\n'; } diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp index aeb901122..4589edc8a 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -34,7 +34,7 @@ int main() } catch (const json::exception e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } // invalid deserialization @@ -46,7 +46,7 @@ int main() } catch (const json::exception e) { - std::cout << "deserialization failed: " << e.what() << std::endl; + std::cout << "deserialization failed: " << e.what() << '\n'; } return 0; diff --git a/docs/mkdocs/docs/examples/nlohmann_json_version.cpp b/docs/mkdocs/docs/examples/nlohmann_json_version.cpp index ca5f53728..e83eb9227 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_version.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_version.cpp @@ -1,12 +1,12 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { std::cout << "JSON for Modern C++ version " << NLOHMANN_JSON_VERSION_MAJOR << "." << NLOHMANN_JSON_VERSION_MINOR << "." - << NLOHMANN_JSON_VERSION_PATCH << std::endl; + << NLOHMANN_JSON_VERSION_PATCH << '\n'; } diff --git a/docs/mkdocs/docs/examples/no_exceptions_access.cpp b/docs/mkdocs/docs/examples/no_exceptions_access.cpp new file mode 100644 index 000000000..2395ac21e --- /dev/null +++ b/docs/mkdocs/docs/examples/no_exceptions_access.cpp @@ -0,0 +1,43 @@ +#include +#include + +using json = nlohmann::json; + +int main() +{ + json j = json::parse(R"({"name": "Mary", "age": 42})"); + + // look up the key, then test the type: neither step throws + const auto keys = {"name", "age", "city"}; + for (const char* key : keys) + { + const auto it = j.find(key); + if (it == j.end()) + { + std::cout << key << ": missing\n"; + } + else if (it->is_string()) + { + std::cout << key << ": string \"" << it->get_ref() << "\"\n"; + } + else + { + std::cout << key << ": not a string, but " << it->type_name() << '\n'; + } + } + + // get_ptr returns a null pointer if the value has another type + const std::string* name = j["name"].get_ptr(); + const std::string* age = j["age"].get_ptr(); + std::cout << "get_ptr: name = " << (name != nullptr) << ", age = " << (age != nullptr) << '\n'; + + // value() is NOT exception-free: it throws if the key exists with a different type + try + { + std::cout << j.value("name", 0) << '\n'; + } + catch (const json::type_error& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/no_exceptions_access.output b/docs/mkdocs/docs/examples/no_exceptions_access.output new file mode 100644 index 000000000..38b700296 --- /dev/null +++ b/docs/mkdocs/docs/examples/no_exceptions_access.output @@ -0,0 +1,5 @@ +name: string "Mary" +age: not a string, but number +city: missing +get_ptr: name = 1, age = 0 +[json.exception.type_error.302] type must be number, but is string diff --git a/docs/mkdocs/docs/examples/no_exceptions_convert.c++17.cpp b/docs/mkdocs/docs/examples/no_exceptions_convert.c++17.cpp new file mode 100644 index 000000000..a48dfdb06 --- /dev/null +++ b/docs/mkdocs/docs/examples/no_exceptions_convert.c++17.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +using json = nlohmann::json; + +struct person +{ + std::string name; + int age; +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, age) + +// Compile this function with exceptions enabled and call it from code that does not use them. +std::optional to_person(const json& j) +{ + try + { + return j.get(); + } + catch (const json::exception&) + { + return std::nullopt; + } +} + +int main() +{ + const json good = {{"name", "Mary"}, {"age", 42}}; + const json bad = {{"name", "Mary"}, {"age", "forty-two"}}; + const json incomplete = {{"name", "Mary"}}; + + const auto inputs = {good, bad, incomplete}; + + for (const json& j : inputs) + { + if (const auto p = to_person(j)) + { + std::cout << "person: " << p->name << ", " << p->age << '\n'; + } + else + { + std::cout << "not a person: " << j << '\n'; + } + } +} diff --git a/docs/mkdocs/docs/examples/no_exceptions_convert.c++17.output b/docs/mkdocs/docs/examples/no_exceptions_convert.c++17.output new file mode 100644 index 000000000..1409aa105 --- /dev/null +++ b/docs/mkdocs/docs/examples/no_exceptions_convert.c++17.output @@ -0,0 +1,3 @@ +person: Mary, 42 +not a person: {"age":"forty-two","name":"Mary"} +not a person: {"name":"Mary"} diff --git a/docs/mkdocs/docs/examples/no_exceptions_parse.cpp b/docs/mkdocs/docs/examples/no_exceptions_parse.cpp new file mode 100644 index 000000000..d65d5c829 --- /dev/null +++ b/docs/mkdocs/docs/examples/no_exceptions_parse.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using json = nlohmann::json; + +// None of the calls below throws, so this example also works with -fno-exceptions. +int main() +{ + const std::string invalid = R"({"name": "Mary", "age": )"; + + // parse() with allow_exceptions = false returns a discarded value + json j = json::parse(invalid, nullptr, false); + std::cout << std::boolalpha << "parse: discarded = " << j.is_discarded() << '\n'; + + // accept() only tells whether the input is valid JSON + std::cout << "accept: " << json::accept(invalid) << '\n'; + + // the binary formats have the same parameter + const std::vector truncated_cbor = {0x83, 0x01}; + json c = json::from_cbor(truncated_cbor, true, false); + std::cout << "from_cbor: discarded = " << c.is_discarded() << '\n'; + + // valid input is parsed as usual + json valid = json::parse(R"({"name": "Mary"})", nullptr, false); + std::cout << "valid input: " << valid << '\n'; +} diff --git a/docs/mkdocs/docs/examples/no_exceptions_parse.output b/docs/mkdocs/docs/examples/no_exceptions_parse.output new file mode 100644 index 000000000..d571e3cbb --- /dev/null +++ b/docs/mkdocs/docs/examples/no_exceptions_parse.output @@ -0,0 +1,4 @@ +parse: discarded = true +accept: false +from_cbor: discarded = true +valid input: {"name":"Mary"} diff --git a/docs/mkdocs/docs/examples/number_float_t.cpp b/docs/mkdocs/docs/examples/number_float_t.cpp index 21211dc51..4eb91369e 100644 --- a/docs/mkdocs/docs/examples/number_float_t.cpp +++ b/docs/mkdocs/docs/examples/number_float_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same::value << std::endl; + std::cout << std::boolalpha << std::is_same::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/number_integer_t.cpp b/docs/mkdocs/docs/examples/number_integer_t.cpp index 75ee57b63..db6954847 100644 --- a/docs/mkdocs/docs/examples/number_integer_t.cpp +++ b/docs/mkdocs/docs/examples/number_integer_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same::value << std::endl; + std::cout << std::boolalpha << std::is_same::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/number_unsigned_t.cpp b/docs/mkdocs/docs/examples/number_unsigned_t.cpp index ff3b86df8..b1a3d12b1 100644 --- a/docs/mkdocs/docs/examples/number_unsigned_t.cpp +++ b/docs/mkdocs/docs/examples/number_unsigned_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same::value << std::endl; + std::cout << std::boolalpha << std::is_same::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/object.cpp b/docs/mkdocs/docs/examples/object.cpp index ad167d4af..bed0d7124 100644 --- a/docs/mkdocs/docs/examples/object.cpp +++ b/docs/mkdocs/docs/examples/object.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/object_comparator_t.cpp b/docs/mkdocs/docs/examples/object_comparator_t.cpp index 6b82c7ca6..6526a09c1 100644 --- a/docs/mkdocs/docs/examples/object_comparator_t.cpp +++ b/docs/mkdocs/docs/examples/object_comparator_t.cpp @@ -1,11 +1,11 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { std::cout << std::boolalpha << "json::object_comparator_t(\"one\", \"two\") = " << json::object_comparator_t{}("one", "two") << "\n" - << "json::object_comparator_t(\"three\", \"four\") = " << json::object_comparator_t{}("three", "four") << std::endl; + << "json::object_comparator_t(\"three\", \"four\") = " << json::object_comparator_t{}("three", "four") << '\n'; } diff --git a/docs/mkdocs/docs/examples/object_t.cpp b/docs/mkdocs/docs/examples/object_t.cpp index 85cfa3e33..275898b76 100644 --- a/docs/mkdocs/docs/examples/object_t.cpp +++ b/docs/mkdocs/docs/examples/object_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same, json::object_t>::value << std::endl; + std::cout << std::boolalpha << std::is_same, json::object_t>::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/operator__ValueType.cpp b/docs/mkdocs/docs/examples/operator__ValueType.cpp index 7ab29ec71..0363c79cc 100644 --- a/docs/mkdocs/docs/examples/operator__ValueType.cpp +++ b/docs/mkdocs/docs/examples/operator__ValueType.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -43,7 +43,7 @@ int main() } std::cout << "\n\n"; - for (auto i : v8) + for (const auto& i : v8) { std::cout << i.first << ": " << i.second << '\n'; } diff --git a/docs/mkdocs/docs/examples/operator__equal.cpp b/docs/mkdocs/docs/examples/operator__equal.cpp index 1426f489a..2a9a89e54 100644 --- a/docs/mkdocs/docs/examples/operator__equal.cpp +++ b/docs/mkdocs/docs/examples/operator__equal.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__equal__nullptr_t.cpp b/docs/mkdocs/docs/examples/operator__equal__nullptr_t.cpp index dbb210372..7b1810a4a 100644 --- a/docs/mkdocs/docs/examples/operator__equal__nullptr_t.cpp +++ b/docs/mkdocs/docs/examples/operator__equal__nullptr_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__equal__specializations.cpp b/docs/mkdocs/docs/examples/operator__equal__specializations.cpp index 97d5ecead..a053af6df 100644 --- a/docs/mkdocs/docs/examples/operator__equal__specializations.cpp +++ b/docs/mkdocs/docs/examples/operator__equal__specializations.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -12,5 +12,5 @@ int main() nlohmann::ordered_json oj1 = {{"version", 1}, {"type", "integer"}}; nlohmann::ordered_json oj2 = {{"type", "integer"}, {"version", 1}}; - std::cout << std::boolalpha << (uj1 == uj2) << '\n' << (oj1 == oj2) << std::endl; + std::cout << std::boolalpha << (uj1 == uj2) << '\n' << (oj1 == oj2) << '\n'; } diff --git a/docs/mkdocs/docs/examples/operator__greater.cpp b/docs/mkdocs/docs/examples/operator__greater.cpp index 65bb9c049..878eba66f 100644 --- a/docs/mkdocs/docs/examples/operator__greater.cpp +++ b/docs/mkdocs/docs/examples/operator__greater.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__greaterequal.cpp b/docs/mkdocs/docs/examples/operator__greaterequal.cpp index f8659ee07..2d36c1d4e 100644 --- a/docs/mkdocs/docs/examples/operator__greaterequal.cpp +++ b/docs/mkdocs/docs/examples/operator__greaterequal.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__less.cpp b/docs/mkdocs/docs/examples/operator__less.cpp index 64209a22a..949982102 100644 --- a/docs/mkdocs/docs/examples/operator__less.cpp +++ b/docs/mkdocs/docs/examples/operator__less.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__lessequal.cpp b/docs/mkdocs/docs/examples/operator__lessequal.cpp index 543b9543f..e044e5544 100644 --- a/docs/mkdocs/docs/examples/operator__lessequal.cpp +++ b/docs/mkdocs/docs/examples/operator__lessequal.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__notequal.cpp b/docs/mkdocs/docs/examples/operator__notequal.cpp index 43e89509c..0f9f85869 100644 --- a/docs/mkdocs/docs/examples/operator__notequal.cpp +++ b/docs/mkdocs/docs/examples/operator__notequal.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__notequal__nullptr_t.cpp b/docs/mkdocs/docs/examples/operator__notequal__nullptr_t.cpp index dc7e6ac4c..30ef0d1bd 100644 --- a/docs/mkdocs/docs/examples/operator__notequal__nullptr_t.cpp +++ b/docs/mkdocs/docs/examples/operator__notequal__nullptr_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator__value_t.cpp b/docs/mkdocs/docs/examples/operator__value_t.cpp index d9aac7978..c2340c68a 100644 --- a/docs/mkdocs/docs/examples/operator__value_t.cpp +++ b/docs/mkdocs/docs/examples/operator__value_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_array__json_pointer.cpp b/docs/mkdocs/docs/examples/operator_array__json_pointer.cpp index 0fa207f02..388011f45 100644 --- a/docs/mkdocs/docs/examples/operator_array__json_pointer.cpp +++ b/docs/mkdocs/docs/examples/operator_array__json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/operator_array__json_pointer_const.cpp b/docs/mkdocs/docs/examples/operator_array__json_pointer_const.cpp index f40e2494a..1a327cea8 100644 --- a/docs/mkdocs/docs/examples/operator_array__json_pointer_const.cpp +++ b/docs/mkdocs/docs/examples/operator_array__json_pointer_const.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/operator_array__keytype.c++17.cpp b/docs/mkdocs/docs/examples/operator_array__keytype.c++17.cpp index 7f2b41dd8..69e5b340e 100644 --- a/docs/mkdocs/docs/examples/operator_array__keytype.c++17.cpp +++ b/docs/mkdocs/docs/examples/operator_array__keytype.c++17.cpp @@ -4,7 +4,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.cpp b/docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.cpp index 2cf94f40f..99343575a 100644 --- a/docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.cpp +++ b/docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_array__object_t_key_type.cpp b/docs/mkdocs/docs/examples/operator_array__object_t_key_type.cpp index f9b7f7318..209b08d61 100644 --- a/docs/mkdocs/docs/examples/operator_array__object_t_key_type.cpp +++ b/docs/mkdocs/docs/examples/operator_array__object_t_key_type.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.cpp b/docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.cpp index 1bdb34024..1f774cc6f 100644 --- a/docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.cpp +++ b/docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_array__size_type.cpp b/docs/mkdocs/docs/examples/operator_array__size_type.cpp index d6f3e153c..5a7f735d7 100644 --- a/docs/mkdocs/docs/examples/operator_array__size_type.cpp +++ b/docs/mkdocs/docs/examples/operator_array__size_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_array__size_type_const.cpp b/docs/mkdocs/docs/examples/operator_array__size_type_const.cpp index d56fa0a43..4a41c4529 100644 --- a/docs/mkdocs/docs/examples/operator_array__size_type_const.cpp +++ b/docs/mkdocs/docs/examples/operator_array__size_type_const.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_deserialize.cpp b/docs/mkdocs/docs/examples/operator_deserialize.cpp index 8e3d8bd83..b42f40428 100644 --- a/docs/mkdocs/docs/examples/operator_deserialize.cpp +++ b/docs/mkdocs/docs/examples/operator_deserialize.cpp @@ -3,7 +3,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_literal_json.cpp b/docs/mkdocs/docs/examples/operator_literal_json.cpp index 84ca6297d..6b8a7e18c 100644 --- a/docs/mkdocs/docs/examples/operator_literal_json.cpp +++ b/docs/mkdocs/docs/examples/operator_literal_json.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/operator_literal_json_pointer.cpp b/docs/mkdocs/docs/examples/operator_literal_json_pointer.cpp index aba93e88e..c7e5460b1 100644 --- a/docs/mkdocs/docs/examples/operator_literal_json_pointer.cpp +++ b/docs/mkdocs/docs/examples/operator_literal_json_pointer.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/operator_ltlt__basic_json.cpp b/docs/mkdocs/docs/examples/operator_ltlt__basic_json.cpp index 3bd4ad577..148d60276 100644 --- a/docs/mkdocs/docs/examples/operator_ltlt__basic_json.cpp +++ b/docs/mkdocs/docs/examples/operator_ltlt__basic_json.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/operator_ltlt__json_pointer.cpp b/docs/mkdocs/docs/examples/operator_ltlt__json_pointer.cpp index 044318fa5..156a07794 100644 --- a/docs/mkdocs/docs/examples/operator_ltlt__json_pointer.cpp +++ b/docs/mkdocs/docs/examples/operator_ltlt__json_pointer.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -9,5 +9,5 @@ int main() json::json_pointer ptr("/foo/bar/baz"); // write string representation to stream - std::cout << ptr << std::endl; + std::cout << ptr << '\n'; } diff --git a/docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.cpp b/docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.cpp index 9e7c9e9be..2c260adc8 100644 --- a/docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.cpp +++ b/docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; const char* to_string(const std::partial_ordering& po) { diff --git a/docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.cpp b/docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.cpp index ebb5b4349..8628dfb0b 100644 --- a/docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.cpp +++ b/docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; const char* to_string(const std::partial_ordering& po) { diff --git a/docs/mkdocs/docs/examples/ordered_json.cpp b/docs/mkdocs/docs/examples/ordered_json.cpp index effad530c..a427e635e 100644 --- a/docs/mkdocs/docs/examples/ordered_json.cpp +++ b/docs/mkdocs/docs/examples/ordered_json.cpp @@ -1,7 +1,7 @@ #include #include -using ordered_json = nlohmann::ordered_json; +using nlohmann::ordered_json; int main() { diff --git a/docs/mkdocs/docs/examples/ordered_json_document.cpp b/docs/mkdocs/docs/examples/ordered_json_document.cpp index 2cae413bf..389055115 100644 --- a/docs/mkdocs/docs/examples/ordered_json_document.cpp +++ b/docs/mkdocs/docs/examples/ordered_json_document.cpp @@ -1,7 +1,7 @@ #include #include -using ordered_json_document = nlohmann::ordered_json_document; +using nlohmann::ordered_json_document; int main() { diff --git a/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp b/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp index c6e7fa08e..9d7821066 100644 --- a/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp +++ b/docs/mkdocs/docs/examples/ordered_json_editable_document.cpp @@ -1,7 +1,7 @@ #include #include -using ordered_json_editable_document = nlohmann::ordered_json_editable_document; +using nlohmann::ordered_json_editable_document; int main() { diff --git a/docs/mkdocs/docs/examples/ordered_map.cpp b/docs/mkdocs/docs/examples/ordered_map.cpp index dcc60cb52..7c507a564 100644 --- a/docs/mkdocs/docs/examples/ordered_map.cpp +++ b/docs/mkdocs/docs/examples/ordered_map.cpp @@ -10,7 +10,7 @@ void output(const char* prefix, const Map& m) { std::cout << element.first << ":" << element.second << ' '; } - std::cout << "}" << std::endl; + std::cout << "}" << '\n'; } int main() diff --git a/docs/mkdocs/docs/examples/other_error.cpp b/docs/mkdocs/docs/examples/other_error.cpp index 56aa8ae6b..2fa152b8b 100644 --- a/docs/mkdocs/docs/examples/other_error.cpp +++ b/docs/mkdocs/docs/examples/other_error.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -25,6 +25,6 @@ int main() { // output exception information std::cout << "message: " << e.what() << '\n' - << "exception id: " << e.id << std::endl; + << "exception id: " << e.id << '\n'; } } diff --git a/docs/mkdocs/docs/examples/out_of_range.cpp b/docs/mkdocs/docs/examples/out_of_range.cpp index 03282082d..cb87d1a33 100644 --- a/docs/mkdocs/docs/examples/out_of_range.cpp +++ b/docs/mkdocs/docs/examples/out_of_range.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,6 +15,6 @@ int main() { // output exception information std::cout << "message: " << e.what() << '\n' - << "exception id: " << e.id << std::endl; + << "exception id: " << e.id << '\n'; } } diff --git a/docs/mkdocs/docs/examples/parse__allow_exceptions.cpp b/docs/mkdocs/docs/examples/parse__allow_exceptions.cpp index f396c347e..6e9385a86 100644 --- a/docs/mkdocs/docs/examples/parse__allow_exceptions.cpp +++ b/docs/mkdocs/docs/examples/parse__allow_exceptions.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -19,7 +19,7 @@ int main() } catch (const json::parse_error& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << '\n'; } // parse without exceptions @@ -27,10 +27,10 @@ int main() if (j.is_discarded()) { - std::cout << "the input is invalid JSON" << std::endl; + std::cout << "the input is invalid JSON" << '\n'; } else { - std::cout << "the input is valid JSON: " << j << std::endl; + std::cout << "the input is valid JSON: " << j << '\n'; } } diff --git a/docs/mkdocs/docs/examples/parse__array__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__array__parser_callback_t.cpp index 63f0a0e4d..747df8599 100644 --- a/docs/mkdocs/docs/examples/parse__array__parser_callback_t.cpp +++ b/docs/mkdocs/docs/examples/parse__array__parser_callback_t.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.cpp index 6eb409bba..50eeee437 100644 --- a/docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.cpp +++ b/docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/parse__istream__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__istream__parser_callback_t.cpp index 2ef14dab2..4a7b60873 100644 --- a/docs/mkdocs/docs/examples/parse__istream__parser_callback_t.cpp +++ b/docs/mkdocs/docs/examples/parse__istream__parser_callback_t.cpp @@ -3,12 +3,12 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { // a JSON text - auto text = R"( + const auto* text = R"( { "Image": { "Width": 800, diff --git a/docs/mkdocs/docs/examples/parse__iterator_pair.cpp b/docs/mkdocs/docs/examples/parse__iterator_pair.cpp index d0c30c1a5..751f29d17 100644 --- a/docs/mkdocs/docs/examples/parse__iterator_pair.cpp +++ b/docs/mkdocs/docs/examples/parse__iterator_pair.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/parse__pointers.cpp b/docs/mkdocs/docs/examples/parse__pointers.cpp index a5a16eea9..9152588c8 100644 --- a/docs/mkdocs/docs/examples/parse__pointers.cpp +++ b/docs/mkdocs/docs/examples/parse__pointers.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/parse__string__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__string__parser_callback_t.cpp index 19c6c448a..24a284e62 100644 --- a/docs/mkdocs/docs/examples/parse__string__parser_callback_t.cpp +++ b/docs/mkdocs/docs/examples/parse__string__parser_callback_t.cpp @@ -2,12 +2,12 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { // a JSON text - auto text = R"( + const auto* text = R"( { "Image": { "Width": 800, diff --git a/docs/mkdocs/docs/examples/parse__wide_string.cpp b/docs/mkdocs/docs/examples/parse__wide_string.cpp new file mode 100644 index 000000000..33b251d8b --- /dev/null +++ b/docs/mkdocs/docs/examples/parse__wide_string.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +using nlohmann::json; + +int main() +{ + // the same JSON text, given in different encodings + const std::string utf8 = "{\"name\":\"\u00e9\"}"; + const std::u16string utf16 = u"{\"name\":\"\u00e9\"}"; + const std::u32string utf32 = U"{\"name\":\"\u00e9\"}"; + const std::wstring wide = L"{\"name\":\"\u00e9\"}"; + + // the encoding is deduced from the character type of the input, and all + // of them yield the same value + std::cout << json::parse(utf8) << '\n' + << json::parse(utf16) << '\n' + << json::parse(utf32) << '\n' + << json::parse(wide) << '\n'; + + // a wide string literal works as well: u"..." is UTF-16, U"..." is UTF-32 + std::cout << json::parse(u"[1, 2, 3]") << '\n'; + + // iterators over wide characters work as well + std::cout << json::parse(utf16.begin(), utf16.end()) << '\n'; +} diff --git a/docs/mkdocs/docs/examples/parse__wide_string.output b/docs/mkdocs/docs/examples/parse__wide_string.output new file mode 100644 index 000000000..cc75f0381 --- /dev/null +++ b/docs/mkdocs/docs/examples/parse__wide_string.output @@ -0,0 +1,6 @@ +{"name":"é"} +{"name":"é"} +{"name":"é"} +{"name":"é"} +[1,2,3] +{"name":"é"} diff --git a/docs/mkdocs/docs/examples/parse_error.cpp b/docs/mkdocs/docs/examples/parse_error.cpp index 2f73ac65f..73367dada 100644 --- a/docs/mkdocs/docs/examples/parse_error.cpp +++ b/docs/mkdocs/docs/examples/parse_error.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,6 +15,6 @@ int main() // output exception information std::cout << "message: " << e.what() << '\n' << "exception id: " << e.id << '\n' - << "byte position of error: " << e.byte << std::endl; + << "byte position of error: " << e.byte << '\n'; } } diff --git a/docs/mkdocs/docs/examples/parse_error_position.cpp b/docs/mkdocs/docs/examples/parse_error_position.cpp new file mode 100644 index 000000000..ed3c42e79 --- /dev/null +++ b/docs/mkdocs/docs/examples/parse_error_position.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +using json = nlohmann::json; + +// line and column (both starting at 1) of a byte index in a text +std::pair line_column(const std::string& input, std::size_t byte) +{ + std::size_t line = 1; + std::size_t column = 1; + for (std::size_t i = 0; i + 1 < byte && i < input.size(); ++i) + { + if (input[i] == '\n') + { + ++line; + column = 1; + } + else + { + ++column; + } + } + return {line, column}; +} + +int main() +{ + const std::string input = "{\n \"a\": 1,\n \"b\": ]\n}"; + + try + { + const json j = json::parse(input); + std::cout << j << '\n'; + } + catch (const json::parse_error& e) + { + const auto position = line_column(input, e.byte); + std::cout << e.what() << '\n'; + std::cout << "byte " << e.byte << " = line " << position.first << ", column " << position.second << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/parse_error_position.output b/docs/mkdocs/docs/examples/parse_error_position.output new file mode 100644 index 000000000..77730d4db --- /dev/null +++ b/docs/mkdocs/docs/examples/parse_error_position.output @@ -0,0 +1,2 @@ +[json.exception.parse_error.101] parse error at line 3, column 8: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal +byte 20 = line 3, column 8 diff --git a/docs/mkdocs/docs/examples/parse_error_position_diagnostic.cpp b/docs/mkdocs/docs/examples/parse_error_position_diagnostic.cpp new file mode 100644 index 000000000..1630b2a59 --- /dev/null +++ b/docs/mkdocs/docs/examples/parse_error_position_diagnostic.cpp @@ -0,0 +1,46 @@ +#define JSON_DIAGNOSTIC_POSITIONS 1 +#include +#include +#include + +using json = nlohmann::json; + +// line and column (both starting at 1) of a byte index in a text +std::pair line_column(const std::string& input, std::size_t byte) +{ + std::size_t line = 1; + std::size_t column = 1; + for (std::size_t i = 0; i + 1 < byte && i < input.size(); ++i) + { + if (input[i] == '\n') + { + ++line; + column = 1; + } + else + { + ++column; + } + } + return {line, column}; +} + +int main() +{ + const std::string input = "{\n \"name\": \"Mary\",\n \"age\": \"forty-two\"\n}"; + const json j = json::parse(input); // the input is valid JSON ... + + try + { + const int age = j["age"]; // ... but the value has the wrong type + std::cout << age << '\n'; + } + catch (const json::exception& e) + { + std::cout << e.what() << '\n'; + + // start_pos() is a 0-based index into the input; the helper expects a 1-based byte index + const auto position = line_column(input, j["age"].start_pos() + 1); + std::cout << "the value starts at line " << position.first << ", column " << position.second << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/parse_error_position_diagnostic.output b/docs/mkdocs/docs/examples/parse_error_position_diagnostic.output new file mode 100644 index 000000000..2f2f0862d --- /dev/null +++ b/docs/mkdocs/docs/examples/parse_error_position_diagnostic.output @@ -0,0 +1,2 @@ +[json.exception.type_error.302] (bytes 29-40) type must be number, but is string +the value starts at line 3, column 10 diff --git a/docs/mkdocs/docs/examples/parse_event_t.cpp b/docs/mkdocs/docs/examples/parse_event_t.cpp index 3a56b22a9..ff1e2c557 100644 --- a/docs/mkdocs/docs/examples/parse_event_t.cpp +++ b/docs/mkdocs/docs/examples/parse_event_t.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; // translate a parse_event_t to a human-readable name std::string event_name(json::parse_event_t event) @@ -29,7 +29,7 @@ std::string event_name(json::parse_event_t event) int main() { // a small JSON text - auto text = R"({"pi": 3.141, "numbers": [1, 2]})"; + const auto* text = R"({"pi": 3.141, "numbers": [1, 2]})"; // parse the text and report every event together with its depth; // returning true keeps every value unchanged diff --git a/docs/mkdocs/docs/examples/parser_callback_t.cpp b/docs/mkdocs/docs/examples/parser_callback_t.cpp index 99c4298e8..16ce0f2f5 100644 --- a/docs/mkdocs/docs/examples/parser_callback_t.cpp +++ b/docs/mkdocs/docs/examples/parser_callback_t.cpp @@ -1,12 +1,12 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { // a JSON text with an array and a number inside an object - auto text = R"({"IDs": [116, 943], "Width": 800})"; + const auto* text = R"({"IDs": [116, 943], "Width": 800})"; // discard the array when the parser reads its opening bracket json j_array_start = json::parse(text, [](int /*depth*/, json::parse_event_t event, json& /*parsed*/) diff --git a/docs/mkdocs/docs/examples/partial_input_buffering.cpp b/docs/mkdocs/docs/examples/partial_input_buffering.cpp new file mode 100644 index 000000000..f8c0f49e0 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_buffering.cpp @@ -0,0 +1,47 @@ +#include +#include + +using json = nlohmann::json; + +std::string buffer; + +// call for every chunk; returns true and sets `value` once the buffer holds one complete JSON value +bool push(const std::string& chunk, json& value) +{ + buffer += chunk; + try + { + value = json::parse(buffer); + buffer.clear(); + return true; + } + catch (const json::parse_error& e) + { + if (e.byte == buffer.size() + 1) + { + return false; // the input ends prematurely: wait for more data + } + throw; // a real syntax error + } +} + +int main() +{ + json value; + const auto chunks = {"{\"na", "me\": \"Ma", "ry\", \"l", "ist\": [1, 2", ", 3]}"}; + for (const std::string chunk : chunks) + { + std::cout << "chunk " << chunk << " -> " << (push(chunk, value) ? "complete" : "incomplete") << '\n'; + } + std::cout << value << '\n'; + + // a syntax error in the middle is not mistaken for missing data + try + { + push("[1, ]", value); + } + catch (const json::parse_error& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/partial_input_buffering.output b/docs/mkdocs/docs/examples/partial_input_buffering.output new file mode 100644 index 000000000..b1d5a6526 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_buffering.output @@ -0,0 +1,7 @@ +chunk {"na -> incomplete +chunk me": "Ma -> incomplete +chunk ry", "l -> incomplete +chunk ist": [1, 2 -> incomplete +chunk , 3]} -> complete +{"list":[1,2,3],"name":"Mary"} +[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal diff --git a/docs/mkdocs/docs/examples/partial_input_cbor.cpp b/docs/mkdocs/docs/examples/partial_input_cbor.cpp new file mode 100644 index 000000000..8e2497e26 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_cbor.cpp @@ -0,0 +1,23 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + // a CBOR value followed by other data + const std::vector cbor = json::to_cbor(json{1, 2, 3}); + std::string data(cbor.begin(), cbor.end()); + data += "TRAILING"; + + std::istringstream input(data); + json j = json::from_cbor(input, false); // strict = false: stop after the first value + + std::cout << "value: " << j << '\n'; + std::cout << "bytes used: " << input.tellg() << '\n'; + + std::string rest; + input >> rest; + std::cout << "remaining input: " << rest << '\n'; +} diff --git a/docs/mkdocs/docs/examples/partial_input_cbor.output b/docs/mkdocs/docs/examples/partial_input_cbor.output new file mode 100644 index 000000000..beaf841d1 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_cbor.output @@ -0,0 +1,3 @@ +value: [1,2,3] +bytes used: 4 +remaining input: TRAILING diff --git a/docs/mkdocs/docs/examples/partial_input_concatenated.cpp b/docs/mkdocs/docs/examples/partial_input_concatenated.cpp new file mode 100644 index 000000000..0f7079ee4 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_concatenated.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::istringstream input(R"({"a":1} [2,3] 4 "s")"); + + // check for the end of the stream before each read, because a read at the end throws + json j; + while (input >> std::ws && input.peek() != std::char_traits::eof()) + { + input >> j; + std::cout << j << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/partial_input_concatenated.output b/docs/mkdocs/docs/examples/partial_input_concatenated.output new file mode 100644 index 000000000..e51277135 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_concatenated.output @@ -0,0 +1,4 @@ +{"a":1} +[2,3] +4 +"s" diff --git a/docs/mkdocs/docs/examples/partial_input_discard_records.cpp b/docs/mkdocs/docs/examples/partial_input_discard_records.cpp new file mode 100644 index 000000000..3aebda1f7 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_discard_records.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +using json = nlohmann::json; + +void handle(const json& record) +{ + std::cout << "handling " << record << '\n'; +} + +int main() +{ + std::istringstream file(R"([{"id": 1}, {"id": 2}, {"id": 3}])"); + + json result = json::parse(file, [](int depth, json::parse_event_t event, json & record) + { + if (depth == 1 && event == json::parse_event_t::object_end) + { + handle(record); + return false; // discard the record + } + return true; + }); + + std::cout << "result: " << result << '\n'; +} diff --git a/docs/mkdocs/docs/examples/partial_input_discard_records.output b/docs/mkdocs/docs/examples/partial_input_discard_records.output new file mode 100644 index 000000000..7eed01747 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_discard_records.output @@ -0,0 +1,4 @@ +handling {"id":1} +handling {"id":2} +handling {"id":3} +result: [] diff --git a/docs/mkdocs/docs/examples/partial_input_sax_stop.cpp b/docs/mkdocs/docs/examples/partial_input_sax_stop.cpp new file mode 100644 index 000000000..6dbeb0945 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_sax_stop.cpp @@ -0,0 +1,81 @@ +#include +#include + +using json = nlohmann::json; + +struct find_id : nlohmann::json_sax +{ + bool next_is_id = false; + json::number_unsigned_t id = 0; + + bool key(std::string& name) override + { + next_is_id = (name == "id"); + return true; + } + + bool number_unsigned(json::number_unsigned_t value) override + { + if (next_is_id) + { + id = value; + return false; // found: stop parsing + } + return true; + } + + bool null() override + { + return true; + } + bool boolean(bool /*val*/) override + { + return true; + } + bool number_integer(json::number_integer_t /*val*/) override + { + return true; + } + bool number_float(json::number_float_t /*val*/, const std::string& /*s*/) override + { + return true; + } + bool string(std::string& /*val*/) override + { + return true; + } + bool binary(json::binary_t& /*val*/) override + { + return true; + } + bool start_object(std::size_t /*elements*/) override + { + return true; + } + bool end_object() override + { + return true; + } + bool start_array(std::size_t /*elements*/) override + { + return true; + } + bool end_array() override + { + return true; + } + bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& /*ex*/) override + { + return false; + } +}; + +int main() +{ + find_id handler; + const bool completed = json::sax_parse(R"({"x": [1, 2, 3], "id": 42, "rest": [4, 5, 6]})", &handler); + + std::cout << std::boolalpha; + std::cout << "parse completed: " << completed << '\n'; + std::cout << "id: " << handler.id << '\n'; +} diff --git a/docs/mkdocs/docs/examples/partial_input_sax_stop.output b/docs/mkdocs/docs/examples/partial_input_sax_stop.output new file mode 100644 index 000000000..947e23ee9 --- /dev/null +++ b/docs/mkdocs/docs/examples/partial_input_sax_stop.output @@ -0,0 +1,2 @@ +parse completed: false +id: 42 diff --git a/docs/mkdocs/docs/examples/patch.cpp b/docs/mkdocs/docs/examples/patch.cpp index b7ecb8eee..53688a364 100644 --- a/docs/mkdocs/docs/examples/patch.cpp +++ b/docs/mkdocs/docs/examples/patch.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -29,5 +29,5 @@ int main() // output original and patched document std::cout << std::setw(4) << doc << "\n\n" - << std::setw(4) << patched_doc << std::endl; + << std::setw(4) << patched_doc << '\n'; } diff --git a/docs/mkdocs/docs/examples/patch__exception.cpp b/docs/mkdocs/docs/examples/patch__exception.cpp index 9e0a27cab..98b8bebc7 100644 --- a/docs/mkdocs/docs/examples/patch__exception.cpp +++ b/docs/mkdocs/docs/examples/patch__exception.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -32,5 +32,5 @@ int main() } // the original document is unchanged - std::cout << std::setw(4) << doc << std::endl; + std::cout << std::setw(4) << doc << '\n'; } diff --git a/docs/mkdocs/docs/examples/patch_inplace.cpp b/docs/mkdocs/docs/examples/patch_inplace.cpp index 061708a2d..82e837549 100644 --- a/docs/mkdocs/docs/examples/patch_inplace.cpp +++ b/docs/mkdocs/docs/examples/patch_inplace.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -25,11 +25,11 @@ int main() )"_json; // output original document - std::cout << "Before\n" << std::setw(4) << doc << std::endl; + std::cout << "Before\n" << std::setw(4) << doc << '\n'; // apply the patch doc.patch_inplace(patch); // output patched document - std::cout << "\nAfter\n" << std::setw(4) << doc << std::endl; + std::cout << "\nAfter\n" << std::setw(4) << doc << '\n'; } diff --git a/docs/mkdocs/docs/examples/patch_inplace__exception.cpp b/docs/mkdocs/docs/examples/patch_inplace__exception.cpp index 02ebe133a..bb89e08e6 100644 --- a/docs/mkdocs/docs/examples/patch_inplace__exception.cpp +++ b/docs/mkdocs/docs/examples/patch_inplace__exception.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -34,5 +34,5 @@ int main() } // the first operation has already been applied to doc - std::cout << std::setw(4) << doc << std::endl; + std::cout << std::setw(4) << doc << '\n'; } diff --git a/docs/mkdocs/docs/examples/push_back.cpp b/docs/mkdocs/docs/examples/push_back.cpp index bbddf4f51..3120d452b 100644 --- a/docs/mkdocs/docs/examples/push_back.cpp +++ b/docs/mkdocs/docs/examples/push_back.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/push_back__initializer_list.cpp b/docs/mkdocs/docs/examples/push_back__initializer_list.cpp index e96645f16..e0cf4e6de 100644 --- a/docs/mkdocs/docs/examples/push_back__initializer_list.cpp +++ b/docs/mkdocs/docs/examples/push_back__initializer_list.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/push_back__object_t__value.cpp b/docs/mkdocs/docs/examples/push_back__object_t__value.cpp index 5d694e938..b63e8f7e1 100644 --- a/docs/mkdocs/docs/examples/push_back__object_t__value.cpp +++ b/docs/mkdocs/docs/examples/push_back__object_t__value.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/rbegin.cpp b/docs/mkdocs/docs/examples/rbegin.cpp index 239f7a689..867a9c952 100644 --- a/docs/mkdocs/docs/examples/rbegin.cpp +++ b/docs/mkdocs/docs/examples/rbegin.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/reject_duplicate_keys.cpp b/docs/mkdocs/docs/examples/reject_duplicate_keys.cpp index 0e5e78925..3e8259763 100644 --- a/docs/mkdocs/docs/examples/reject_duplicate_keys.cpp +++ b/docs/mkdocs/docs/examples/reject_duplicate_keys.cpp @@ -5,7 +5,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; json parse_strict(const std::string& input) { diff --git a/docs/mkdocs/docs/examples/rend.cpp b/docs/mkdocs/docs/examples/rend.cpp index adadbbdc2..86d70c7f9 100644 --- a/docs/mkdocs/docs/examples/rend.cpp +++ b/docs/mkdocs/docs/examples/rend.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/sax_no_exception.cpp b/docs/mkdocs/docs/examples/sax_no_exception.cpp index 1eeff466e..580c0cb9d 100644 --- a/docs/mkdocs/docs/examples/sax_no_exception.cpp +++ b/docs/mkdocs/docs/examples/sax_no_exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; // a DOM parser that reports parse errors instead of throwing class sax_no_exception : public nlohmann::detail::json_sax_dom_parser @@ -18,7 +18,7 @@ class sax_no_exception : public nlohmann::detail::json_sax_dom_parser #include -using json = nlohmann::json; +using nlohmann::json; // a simple event consumer that collects string representations of the passed // values; note inheriting from json::json_sax_t is not required, but can @@ -95,7 +95,7 @@ class sax_event_consumer : public json::json_sax_t int main() { // a JSON text - auto text = R"( + const auto* text = R"( { "Image": { "Width": 800, @@ -127,5 +127,5 @@ int main() } // output the result of sax_parse - std::cout << "\nresult: " << std::boolalpha << result << std::endl; + std::cout << "\nresult: " << std::boolalpha << result << '\n'; } diff --git a/docs/mkdocs/docs/examples/sax_parse__binary.cpp b/docs/mkdocs/docs/examples/sax_parse__binary.cpp index 08bc85df6..300bbda66 100644 --- a/docs/mkdocs/docs/examples/sax_parse__binary.cpp +++ b/docs/mkdocs/docs/examples/sax_parse__binary.cpp @@ -3,7 +3,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; // a simple event consumer that collects string representations of the passed // values; note inheriting from json::json_sax_t is not required, but can @@ -110,5 +110,5 @@ int main() } // output the result of sax_parse - std::cout << "\nresult: " << std::boolalpha << result << std::endl; + std::cout << "\nresult: " << std::boolalpha << result << '\n'; } diff --git a/docs/mkdocs/docs/examples/size.cpp b/docs/mkdocs/docs/examples/size.cpp index 237548377..00ed8cd15 100644 --- a/docs/mkdocs/docs/examples/size.cpp +++ b/docs/mkdocs/docs/examples/size.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/std_formatter.c++20.cpp b/docs/mkdocs/docs/examples/std_formatter.c++20.cpp index d249415d7..3401ad5e6 100644 --- a/docs/mkdocs/docs/examples/std_formatter.c++20.cpp +++ b/docs/mkdocs/docs/examples/std_formatter.c++20.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/std_hash.cpp b/docs/mkdocs/docs/examples/std_hash.cpp index 184ddbbb3..0fcabdcf1 100644 --- a/docs/mkdocs/docs/examples/std_hash.cpp +++ b/docs/mkdocs/docs/examples/std_hash.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -16,5 +16,5 @@ int main() << "hash({}) = " << std::hash {}(json::object()) << '\n' << "hash([]) = " << std::hash {}(json::array()) << '\n' << "hash({\"hello\": \"world\"}) = " << std::hash {}("{\"hello\": \"world\"}"_json) - << std::endl; + << '\n'; } diff --git a/docs/mkdocs/docs/examples/std_swap.cpp b/docs/mkdocs/docs/examples/std_swap.cpp index 36ab3ce6b..f829c9a5d 100644 --- a/docs/mkdocs/docs/examples/std_swap.cpp +++ b/docs/mkdocs/docs/examples/std_swap.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,5 +15,5 @@ int main() // swap values std::swap(j1, j2); - std::cout << "j1 = " << j1 << " | j2 = " << j2 << std::endl; + std::cout << "j1 = " << j1 << " | j2 = " << j2 << '\n'; } diff --git a/docs/mkdocs/docs/examples/string_t.cpp b/docs/mkdocs/docs/examples/string_t.cpp index 77a9ea480..118c42d26 100644 --- a/docs/mkdocs/docs/examples/string_t.cpp +++ b/docs/mkdocs/docs/examples/string_t.cpp @@ -2,9 +2,9 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { - std::cout << std::boolalpha << std::is_same::value << std::endl; + std::cout << std::boolalpha << std::is_same::value << '\n'; } diff --git a/docs/mkdocs/docs/examples/swap__array_t.cpp b/docs/mkdocs/docs/examples/swap__array_t.cpp index 2119dd552..e551f1fcc 100644 --- a/docs/mkdocs/docs/examples/swap__array_t.cpp +++ b/docs/mkdocs/docs/examples/swap__array_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/swap__binary_t.cpp b/docs/mkdocs/docs/examples/swap__binary_t.cpp index 4b8fc3db7..a9c7b9d03 100644 --- a/docs/mkdocs/docs/examples/swap__binary_t.cpp +++ b/docs/mkdocs/docs/examples/swap__binary_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/swap__object_t.cpp b/docs/mkdocs/docs/examples/swap__object_t.cpp index 301b558d3..9aff7667e 100644 --- a/docs/mkdocs/docs/examples/swap__object_t.cpp +++ b/docs/mkdocs/docs/examples/swap__object_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/swap__reference.cpp b/docs/mkdocs/docs/examples/swap__reference.cpp index 34182ad24..d2ffbedf2 100644 --- a/docs/mkdocs/docs/examples/swap__reference.cpp +++ b/docs/mkdocs/docs/examples/swap__reference.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/swap__string_t.cpp b/docs/mkdocs/docs/examples/swap__string_t.cpp index b5d58316f..14d7552ef 100644 --- a/docs/mkdocs/docs/examples/swap__string_t.cpp +++ b/docs/mkdocs/docs/examples/swap__string_t.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/to_bjdata.cpp b/docs/mkdocs/docs/examples/to_bjdata.cpp index 9b7abac4e..ddebccb2f 100644 --- a/docs/mkdocs/docs/examples/to_bjdata.cpp +++ b/docs/mkdocs/docs/examples/to_bjdata.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; // function to print BJData's diagnostic format @@ -10,11 +10,11 @@ void print_byte(uint8_t byte) { if (32 < byte and byte < 128) { - std::cout << (char)byte; + std::cout << static_cast(byte); } else { - std::cout << (int)byte; + std::cout << static_cast(byte); } } @@ -31,7 +31,7 @@ int main() { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; // create an array of numbers json array = {1, 2, 3, 4, 5, 6, 7, 8}; @@ -48,17 +48,17 @@ int main() { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; for (auto& byte : v_array_size) { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; for (auto& byte : v_array_size_and_type) { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_bjdata__exception.cpp b/docs/mkdocs/docs/examples/to_bjdata__exception.cpp index 560f3343b..8e3dda347 100644 --- a/docs/mkdocs/docs/examples/to_bjdata__exception.cpp +++ b/docs/mkdocs/docs/examples/to_bjdata__exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/to_bon8.cpp b/docs/mkdocs/docs/examples/to_bon8.cpp index a40130903..2dc36fc6a 100644 --- a/docs/mkdocs/docs/examples/to_bon8.cpp +++ b/docs/mkdocs/docs/examples/to_bon8.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -16,7 +16,7 @@ int main() // print the vector content for (auto& byte : v) { - std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " "; + std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte) << " "; } - std::cout << std::endl; + std::cout << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_bon8__exception.cpp b/docs/mkdocs/docs/examples/to_bon8__exception.cpp index 48992413b..cefea2809 100644 --- a/docs/mkdocs/docs/examples/to_bon8__exception.cpp +++ b/docs/mkdocs/docs/examples/to_bon8__exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/to_bson.cpp b/docs/mkdocs/docs/examples/to_bson.cpp index 3484b0b76..bfbb3677b 100644 --- a/docs/mkdocs/docs/examples/to_bson.cpp +++ b/docs/mkdocs/docs/examples/to_bson.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -16,7 +16,7 @@ int main() // print the vector content for (auto& byte : v) { - std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " "; + std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte) << " "; } - std::cout << std::endl; + std::cout << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_bson__exception.cpp b/docs/mkdocs/docs/examples/to_bson__exception.cpp index fe12c202f..387bba3e8 100644 --- a/docs/mkdocs/docs/examples/to_bson__exception.cpp +++ b/docs/mkdocs/docs/examples/to_bson__exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/to_cbor.cpp b/docs/mkdocs/docs/examples/to_cbor.cpp index 3d5e04150..46a2cd042 100644 --- a/docs/mkdocs/docs/examples/to_cbor.cpp +++ b/docs/mkdocs/docs/examples/to_cbor.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -16,7 +16,7 @@ int main() // print the vector content for (auto& byte : v) { - std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " "; + std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte) << " "; } - std::cout << std::endl; + std::cout << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_json.cpp b/docs/mkdocs/docs/examples/to_json.cpp index 1f82a4de4..9ac74116a 100644 --- a/docs/mkdocs/docs/examples/to_json.cpp +++ b/docs/mkdocs/docs/examples/to_json.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; namespace ns { @@ -28,5 +28,5 @@ int main() json j = p; - std::cout << j << std::endl; + std::cout << j << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_msgpack.cpp b/docs/mkdocs/docs/examples/to_msgpack.cpp index b29ae8c7c..958a13767 100644 --- a/docs/mkdocs/docs/examples/to_msgpack.cpp +++ b/docs/mkdocs/docs/examples/to_msgpack.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() @@ -16,7 +16,7 @@ int main() // print the vector content for (auto& byte : v) { - std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " "; + std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte) << " "; } - std::cout << std::endl; + std::cout << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_msgpack__exception.cpp b/docs/mkdocs/docs/examples/to_msgpack__exception.cpp index fb20d0ad8..d932ffac4 100644 --- a/docs/mkdocs/docs/examples/to_msgpack__exception.cpp +++ b/docs/mkdocs/docs/examples/to_msgpack__exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/to_string.cpp b/docs/mkdocs/docs/examples/to_string.cpp index ee44283f3..2e9ec0d3b 100644 --- a/docs/mkdocs/docs/examples/to_string.cpp +++ b/docs/mkdocs/docs/examples/to_string.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using std::to_string; int main() @@ -16,5 +16,5 @@ int main() // serialize without indentation std::cout << j_str << "\n\n" - << i_str << std::endl; + << i_str << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_ubjson.cpp b/docs/mkdocs/docs/examples/to_ubjson.cpp index fd267a85a..e951378e2 100644 --- a/docs/mkdocs/docs/examples/to_ubjson.cpp +++ b/docs/mkdocs/docs/examples/to_ubjson.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; // function to print UBJSON's diagnostic format @@ -10,11 +10,11 @@ void print_byte(uint8_t byte) { if (32 < byte and byte < 128) { - std::cout << (char)byte; + std::cout << static_cast(byte); } else { - std::cout << (int)byte; + std::cout << static_cast(byte); } } @@ -31,7 +31,7 @@ int main() { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; // create an array of numbers json array = {1, 2, 3, 4, 5, 6, 7, 8}; @@ -48,17 +48,17 @@ int main() { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; for (auto& byte : v_array_size) { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; for (auto& byte : v_array_size_and_type) { print_byte(byte); } - std::cout << std::endl; + std::cout << '\n'; } diff --git a/docs/mkdocs/docs/examples/to_ubjson__exception.cpp b/docs/mkdocs/docs/examples/to_ubjson__exception.cpp index 8fe481d84..bfed7abfe 100644 --- a/docs/mkdocs/docs/examples/to_ubjson__exception.cpp +++ b/docs/mkdocs/docs/examples/to_ubjson__exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/trailing_commas.cpp b/docs/mkdocs/docs/examples/trailing_commas.cpp index 32bf6874e..4a2285e6b 100644 --- a/docs/mkdocs/docs/examples/trailing_commas.cpp +++ b/docs/mkdocs/docs/examples/trailing_commas.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -25,7 +25,7 @@ int main() } catch (json::exception& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << '\n'; } json j = json::parse(s, diff --git a/docs/mkdocs/docs/examples/type.cpp b/docs/mkdocs/docs/examples/type.cpp index 68fba3a91..3b51b143f 100644 --- a/docs/mkdocs/docs/examples/type.cpp +++ b/docs/mkdocs/docs/examples/type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/type_error.cpp b/docs/mkdocs/docs/examples/type_error.cpp index f520f4013..44c3e0279 100644 --- a/docs/mkdocs/docs/examples/type_error.cpp +++ b/docs/mkdocs/docs/examples/type_error.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -15,6 +15,6 @@ int main() { // output exception information std::cout << "message: " << e.what() << '\n' - << "exception id: " << e.id << std::endl; + << "exception id: " << e.id << '\n'; } } diff --git a/docs/mkdocs/docs/examples/type_name.cpp b/docs/mkdocs/docs/examples/type_name.cpp index 32d3590cc..b3a46712f 100644 --- a/docs/mkdocs/docs/examples/type_name.cpp +++ b/docs/mkdocs/docs/examples/type_name.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/type_recipes_chrono.cpp b/docs/mkdocs/docs/examples/type_recipes_chrono.cpp new file mode 100644 index 000000000..a3bd357ff --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_chrono.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +// the name of a period; extend the list for the periods you use +template const char* unit_name(); +template <> const char* unit_name() +{ + return "ns"; +} +template <> const char* unit_name() +{ + return "us"; +} +template <> const char* unit_name() +{ + return "ms"; +} +template <> const char* unit_name>() +{ + return "s"; +} +template <> const char* unit_name>() +{ + return "min"; +} +template <> const char* unit_name>() +{ + return "h"; +} + +namespace nlohmann +{ +template +struct adl_serializer> +{ + static void to_json(json& j, const std::chrono::duration& d) + { + j = {{"unit", unit_name()}, {"value", d.count()}}; + } + + static void from_json(const json& j, std::chrono::duration& d) + { + if (j.at("unit").get() != unit_name()) + { + throw std::invalid_argument("unexpected unit " + j.at("unit").dump()); + } + d = std::chrono::duration(j.at("value").get()); + } +}; + +template +struct adl_serializer> +{ + static void to_json(json& j, const std::chrono::time_point& t) + { + j = t.time_since_epoch(); + } + + static void from_json(const json& j, std::chrono::time_point& t) + { + t = std::chrono::time_point(j.get()); + } +}; +} // namespace nlohmann + +int main() +{ + using namespace std::chrono; + + std::cout << std::boolalpha; + + // the unit is part of the JSON value + const json timeout = milliseconds(1500); + std::cout << "duration: " << timeout << '\n'; + std::cout << "as milliseconds: " << timeout.get().count() << '\n'; + + // reading with a different unit is rejected + try + { + timeout.get(); + } + catch (const std::invalid_argument& e) + { + std::cout << "error: " << e.what() << '\n'; + } + + // a system_clock time point is the time since the Unix epoch + const time_point t(milliseconds(1700000000123)); + const json stamp = t; + std::cout << "time point: " << stamp << '\n'; + + const auto back = stamp.get>(); + std::cout << "round trip: " << (back == t) << '\n'; +} diff --git a/docs/mkdocs/docs/examples/type_recipes_chrono.output b/docs/mkdocs/docs/examples/type_recipes_chrono.output new file mode 100644 index 000000000..6b40a2bc2 --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_chrono.output @@ -0,0 +1,5 @@ +duration: {"unit":"ms","value":1500} +as milliseconds: 1500 +error: unexpected unit "ms" +time point: {"unit":"ms","value":1700000000123} +round trip: true diff --git a/docs/mkdocs/docs/examples/type_recipes_class_hierarchy.c++14.cpp b/docs/mkdocs/docs/examples/type_recipes_class_hierarchy.c++14.cpp new file mode 100644 index 000000000..a7ac926f3 --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_class_hierarchy.c++14.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +struct shape +{ + virtual ~shape() = default; + virtual json serialize() const = 0; +}; + +struct circle : shape +{ + double radius; + + explicit circle(double r) : radius(r) {} + + json serialize() const override + { + return {{"type", "circle"}, {"radius", radius}}; + } +}; + +struct rectangle : shape +{ + double width; + double height; + + rectangle(double w, double h) : width(w), height(h) {} + + json serialize() const override + { + return {{"type", "rectangle"}, {"width", width}, {"height", height}}; + } +}; + +namespace nlohmann +{ +template <> +struct adl_serializer> +{ + static void to_json(json& j, const std::unique_ptr& p) + { + j = p ? p->serialize() : json(nullptr); + } + + static void from_json(const json& j, std::unique_ptr& p) + { + if (j.is_null()) + { + p.reset(); + return; + } + + const auto& type = j.at("type").get_ref(); + if (type == "circle") + { + p = std::make_unique(j.at("radius").get()); + } + else if (type == "rectangle") + { + p = std::make_unique(j.at("width").get(), j.at("height").get()); + } + else + { + throw std::invalid_argument("unknown shape type: " + type); + } + } +}; +} // namespace nlohmann + +int main() +{ + std::vector> shapes; + shapes.push_back(std::make_unique(1.5)); + shapes.push_back(std::make_unique(2, 3)); + + const json j = shapes; + std::cout << j << '\n'; + + // reading creates objects of the derived types + const auto back = j.get>>(); + std::cout << std::boolalpha; + std::cout << "first is a circle: " << (dynamic_cast(back[0].get()) != nullptr) << '\n'; + std::cout << "second is a rectangle: " << (dynamic_cast(back[1].get()) != nullptr) << '\n'; + + // an unknown type tag is reported + try + { + json{{"type", "star"}}.get>(); + } + catch (const std::invalid_argument& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/type_recipes_class_hierarchy.c++14.output b/docs/mkdocs/docs/examples/type_recipes_class_hierarchy.c++14.output new file mode 100644 index 000000000..b04cea385 --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_class_hierarchy.c++14.output @@ -0,0 +1,4 @@ +[{"radius":1.5,"type":"circle"},{"height":3.0,"type":"rectangle","width":2.0}] +first is a circle: true +second is a rectangle: true +unknown shape type: star diff --git a/docs/mkdocs/docs/examples/type_recipes_smart_pointers.c++14.cpp b/docs/mkdocs/docs/examples/type_recipes_smart_pointers.c++14.cpp new file mode 100644 index 000000000..d06557df2 --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_smart_pointers.c++14.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace nlohmann +{ +template +struct adl_serializer> +{ + static void to_json(json& j, const std::shared_ptr& p) + { + if (p) + { + j = *p; + } + else + { + j = nullptr; + } + } + + static void from_json(const json& j, std::shared_ptr& p) + { + if (j.is_null()) + { + p = nullptr; + } + else + { + p = std::make_shared(j.get::type>()); + } + } +}; + +template +struct adl_serializer> +{ + static void to_json(json& j, const std::unique_ptr& p) + { + if (p) + { + j = *p; + } + else + { + j = nullptr; + } + } + + static void from_json(const json& j, std::unique_ptr& p) + { + if (j.is_null()) + { + p = nullptr; + } + else + { + p = std::make_unique(j.get()); + } + } +}; +} // namespace nlohmann + +struct point +{ + int x; + int y; +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(point, x, y) + +int main() +{ + // an empty pointer is written as null + const std::vector> points = {std::make_shared(point{1, 2}), nullptr}; + const json j = points; + std::cout << j << '\n'; + + // reading allocates new objects + const auto back = j.get>>(); + std::cout << back[0]->x << ", " << back[0]->y << ", " << (back[1] == nullptr) << '\n'; + + // pointers to const and unique_ptr work as well + const auto const_ptr = j[0].get>(); + const auto unique = j[0].get>(); + std::cout << const_ptr->y << ", " << unique->x << '\n'; +} diff --git a/docs/mkdocs/docs/examples/type_recipes_smart_pointers.c++14.output b/docs/mkdocs/docs/examples/type_recipes_smart_pointers.c++14.output new file mode 100644 index 000000000..083b00774 --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_smart_pointers.c++14.output @@ -0,0 +1,3 @@ +[{"x":1,"y":2},null] +1, 2, 1 +2, 1 diff --git a/docs/mkdocs/docs/examples/type_recipes_variant.c++17.cpp b/docs/mkdocs/docs/examples/type_recipes_variant.c++17.cpp new file mode 100644 index 000000000..e774f8a2b --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_variant.c++17.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace geo +{ +struct circ +{ + double radius; +}; + +struct rect +{ + double width; + double height; +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(circ, radius) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(rect, width, height) + +using figure = std::variant; + +void to_json(json& j, const figure& f) +{ + std::visit([&j](const auto & alternative) + { + j = alternative; + }, f); + j["type"] = std::holds_alternative(f) ? "circ" : "rect"; +} + +void from_json(const json& j, figure& f) +{ + const auto& type = j.at("type").get_ref(); + if (type == "circ") + { + f = j.get(); + } + else if (type == "rect") + { + f = j.get(); + } + else + { + throw std::invalid_argument("unknown figure type: " + type); + } +} +} // namespace geo + +int main() +{ + const std::vector figures = {geo::circ{2}, geo::rect{3, 4}}; + + const json j = figures; + std::cout << j << '\n'; + + const auto back = j.get>(); + std::cout << "alternatives read back: " << back[0].index() << ", " << back[1].index() << '\n'; +} diff --git a/docs/mkdocs/docs/examples/type_recipes_variant.c++17.output b/docs/mkdocs/docs/examples/type_recipes_variant.c++17.output new file mode 100644 index 000000000..062554cb5 --- /dev/null +++ b/docs/mkdocs/docs/examples/type_recipes_variant.c++17.output @@ -0,0 +1,2 @@ +[{"radius":2.0,"type":"circ"},{"height":4.0,"type":"rect","width":3.0}] +alternatives read back: 0, 1 diff --git a/docs/mkdocs/docs/examples/unflatten.cpp b/docs/mkdocs/docs/examples/unflatten.cpp index 75fb02d03..ac665e2da 100644 --- a/docs/mkdocs/docs/examples/unflatten.cpp +++ b/docs/mkdocs/docs/examples/unflatten.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/update.cpp b/docs/mkdocs/docs/examples/update.cpp index ff94b67fa..413b12779 100644 --- a/docs/mkdocs/docs/examples/update.cpp +++ b/docs/mkdocs/docs/examples/update.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/update__range.cpp b/docs/mkdocs/docs/examples/update__range.cpp index 5b4385046..e38b5c19d 100644 --- a/docs/mkdocs/docs/examples/update__range.cpp +++ b/docs/mkdocs/docs/examples/update__range.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/value__exception.cpp b/docs/mkdocs/docs/examples/value__exception.cpp index 1d611d4c9..79cbc53b2 100644 --- a/docs/mkdocs/docs/examples/value__exception.cpp +++ b/docs/mkdocs/docs/examples/value__exception.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/value__json_ptr.cpp b/docs/mkdocs/docs/examples/value__json_ptr.cpp index d866ef076..8f8bb37e3 100644 --- a/docs/mkdocs/docs/examples/value__json_ptr.cpp +++ b/docs/mkdocs/docs/examples/value__json_ptr.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; using namespace nlohmann::literals; int main() diff --git a/docs/mkdocs/docs/examples/value__keytype.c++17.cpp b/docs/mkdocs/docs/examples/value__keytype.c++17.cpp index 1f6ff5c30..b1a034a55 100644 --- a/docs/mkdocs/docs/examples/value__keytype.c++17.cpp +++ b/docs/mkdocs/docs/examples/value__keytype.c++17.cpp @@ -3,7 +3,7 @@ #include using namespace std::string_view_literals; -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/value__object_t_key_type.cpp b/docs/mkdocs/docs/examples/value__object_t_key_type.cpp index 9488d30ef..df2e48f52 100644 --- a/docs/mkdocs/docs/examples/value__object_t_key_type.cpp +++ b/docs/mkdocs/docs/examples/value__object_t_key_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/examples/value__return_type.cpp b/docs/mkdocs/docs/examples/value__return_type.cpp index 7947e1d93..2818e2271 100644 --- a/docs/mkdocs/docs/examples/value__return_type.cpp +++ b/docs/mkdocs/docs/examples/value__return_type.cpp @@ -1,7 +1,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -9,6 +9,6 @@ int main() std::cout << "operator[]: " << j["uint64"] << '\n' << "default value (int): " << j.value("uint64", 0) << '\n' - << "default value (uint64_t): " << j.value("uint64", std::uint64_t(0)) << '\n' + << "default value (uint64_t): " << j.value("uint64", static_cast(0)) << '\n' << "explicit return value type: " << j.value("uint64", 0) << '\n'; } diff --git a/docs/mkdocs/docs/examples/with_t.cpp b/docs/mkdocs/docs/examples/with_t.cpp index 665efde3e..2be58f944 100644 --- a/docs/mkdocs/docs/examples/with_t.cpp +++ b/docs/mkdocs/docs/examples/with_t.cpp @@ -14,5 +14,5 @@ int main() j["b"] = 3; // keys are sorted, because std::map is used to store the object - std::cout << j.dump() << std::endl; + std::cout << j.dump() << '\n'; } diff --git a/docs/mkdocs/docs/features/arbitrary_types.md b/docs/mkdocs/docs/features/arbitrary_types.md index 0ca165902..9d918506f 100644 --- a/docs/mkdocs/docs/features/arbitrary_types.md +++ b/docs/mkdocs/docs/features/arbitrary_types.md @@ -54,7 +54,7 @@ assert(p == p2); To make this work with one of your types, you only need to provide two functions: ```cpp -using json = nlohmann::json; +using nlohmann::json; namespace ns { void to_json(json& j, const person& p) { @@ -409,6 +409,32 @@ namespace nlohmann { } ``` +## Why isn't my `to_json` or `from_json` found? + +The library calls `to_json` and `from_json` without qualification, so the compiler finds them by +[argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) in the namespace of the type, or in the +namespaces of its template arguments. The usual reasons for a function that is "not found" are: + +- **The type is an alias of a standard type.** After `#!cpp using Hash = std::array;` the type + *is* `std::array`, which lives in namespace `std`. A `to_json(json&, const Hash&)` in your namespace + is never selected. Either give the type its own identity, for example `#!cpp struct Hash : std::array {};` + (the function is then found in `Hash`'s namespace), or specialize + [`adl_serializer`](#how-do-i-convert-third-party-types). +- **The function is in the wrong namespace.** It must be in the namespace of the type (a global type needs a global + function), not in the namespace of the code that calls the conversion. Specializations of `adl_serializer` belong in + namespace `nlohmann`. +- **It is declared too late.** A conversion can only use what has been declared when it is instantiated. If + `#!cpp json j = std::vector{...};` appears before the `adl_serializer` specialization, the + compiler reports an error such as "explicit specialization of `adl_serializer<...>` after instantiation". Put the + specialization in a small header that every file includes **before** it uses the type with `json`. +- **It is a template.** For `template struct box`, write `template void to_json(json&, const box&)`, + not an overload for one `T`. +- **It worked by accident.** Some versions found `to_json` overloads in `nlohmann::detail` through argument-dependent + lookup of the base class of `basic_json`. Do not rely on this; it is not part of the interface. + +For smart pointers, class hierarchies, `std::variant`, and `std::chrono`, see +[Recipes for common types](type_recipes.md). + ## Why can't I convert to/from `std::any`? `std::any` is intentionally excluded from `get()`/generic conversion support, so `get()` and @@ -505,6 +531,7 @@ struct bad_serializer ## See also +- [Recipes for common types](type_recipes.md) - smart pointers, class hierarchies, `std::variant`, and `std::chrono` - [Converting values](conversions.md) - the general overview of `get`/`get_to` and implicit conversions - [Specializing enum conversion](enum_conversion.md) - map enums to JSON strings instead of integers - [Supported macros](macros.md) - reference for `NLOHMANN_DEFINE_TYPE_*` and related macros diff --git a/docs/mkdocs/docs/features/assertions.md b/docs/mkdocs/docs/features/assertions.md index dadb0bd87..c551fef30 100644 --- a/docs/mkdocs/docs/features/assertions.md +++ b/docs/mkdocs/docs/features/assertions.md @@ -7,7 +7,9 @@ detect bugs in client code. ## Switch off runtime assertions Runtime assertions can be switched off by defining the preprocessor macro `NDEBUG` (see the -[documentation of assert](https://en.cppreference.com/w/cpp/error/assert)) which is the default for release builds. +[documentation of assert](https://en.cppreference.com/w/cpp/error/assert)) which is the default for release builds. Without `NDEBUG`, the assertions are compiled in and contain the name of the source +file (`__FILE__`); see [notes for builds and distribution](../integration/index.md#notes-for-builds-and-distribution) +if that path must not end up in your binaries. ## Change assertion behavior @@ -35,7 +37,7 @@ See also the documentation on [element access](element_access/index.md). ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -57,7 +59,7 @@ See also the documentation on [element access](element_access/index.md). ```cpp #include - using json = nlohmann::json; + using nlohmann::json; using namespace nlohmann::literals; int main() @@ -85,7 +87,7 @@ uninitialized iterator is undefined behavior and yields a runtime assertion. ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -112,7 +114,7 @@ behavior and yields a runtime assertion. ```cpp #include - using json = nlohmann::json; + using nlohmann::json; int main() { @@ -144,7 +146,7 @@ result in undefined behavior. Since version 3.12.0, this library checks for `nul #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/features/binary_formats/bjdata.md b/docs/mkdocs/docs/features/binary_formats/bjdata.md index 12ef19d59..3a7a1798a 100644 --- a/docs/mkdocs/docs/features/binary_formats/bjdata.md +++ b/docs/mkdocs/docs/features/binary_formats/bjdata.md @@ -17,6 +17,39 @@ simple processing. - [BJData Specification](https://neurojson.org/bjdata/draft2) +## Byte layout + +BJData follows the structure of UBJSON: a one-byte, human-readable ASCII *type marker* followed by the payload of that type, with strings prefixed by a length that is itself an integer value. In contrast to UBJSON, numbers are stored in little-endian byte order, and additional markers cover unsigned 16/32/64-bit integers, half-precision floats, and bytes. Containers can be written unoptimized (opened with `[` or `{`, closed with `]` or `}`) or optimized with a declared element type (`$`) and/or count (`#`). BJData extends the optimized form: the count can be a list of dimensions, which stores an N-dimensional packed array as a flat sequence of values without any per-element markers. + +Consider the following JSON value (shown formatted for readability): + +```json +{ + "id": 300, + "name": "json", + "pi": 3.14, + "tags": [ + "a", + "b" + ] +} +``` + +`to_bjdata` encodes it to 51 bytes (shown in hexadecimal; the encoding contains no whitespace). The colors mark the role of each byte: type/marker length/count/size data. + +
+ + + + + + + + + + +
JSONBytesMeaning
{7Bobject start ({)
"id": 300,69 02 69 64 49 2C 01key: length 2 as int8 (i), then UTF-8 bytes; value: int16 (I), little-endian
"name": "json",69 04 6E 61 6D 65 53 69 04 6A 73 6F 6Ekey: length 4; value: string (S), length 4 as int8, UTF-8 bytes
"pi": 3.14,69 02 70 69 44 1F 85 EB 51 B8 1E 09 40key: length 2; value: float64 (D), little-endian
"tags": [69 04 74 61 67 73 5Bkey: length 4; value: array start ([)
"a",53 69 01 61string, length 1
"b"53 69 01 62string, length 1
]5Darray end (])
}7Dobject end (})
+ ## Serialization The library uses the following mapping from JSON values types to BJData types according to the BJData specification: diff --git a/docs/mkdocs/docs/features/binary_formats/bon8.md b/docs/mkdocs/docs/features/binary_formats/bon8.md index d2d20da19..b972dc035 100644 --- a/docs/mkdocs/docs/features/binary_formats/bon8.md +++ b/docs/mkdocs/docs/features/binary_formats/bon8.md @@ -10,6 +10,39 @@ values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to - [BON8 specification](https://github.com/hikoworks/hikogui/blob/main/docs/BON8.md) - [Reference implementation](https://github.com/hikoworks/hikogui/blob/main/src/hikogui/codec/BON8.hpp) in HikoGUI +## Byte layout + +BON8 exploits the fact that some byte values can never start a UTF-8 character. These bytes serve as *type markers*, so strings can be stored as plain UTF-8 without a length prefix: a string ends at the first byte that cannot continue it (often the marker of the next value). Small integers, `true`, `false`, `null`, and a few common floats are a single byte. Arrays and objects are introduced by a marker; those with up to four elements encode their size in the marker and need no terminator, while larger ones are closed by the end marker `0xFE`. + +Consider the following JSON value (shown formatted for readability): + +```json +{ + "id": 300, + "name": "json", + "pi": 3.14, + "tags": [ + "a", + "b" + ] +} +``` + +`to_bon8` encodes it to 35 bytes (shown in hexadecimal; the encoding contains no whitespace). The colors mark the role of each byte: type/marker length/count/size data. + +
+ + + + + + + + + + +
JSONBytesMeaning
{8Aobject with 4 members
"id": 300,69 64 C4 04key: UTF-8 bytes; the string ends at the next byte, which cannot continue a UTF-8 character; value: 2-byte positive integer (lead byte, then a byte below 0x80)
"name": "json",6E 61 6D 65 FF 6A 73 6F 6E FFkey: UTF-8 bytes; 0xFF terminates the string as another string follows; value: UTF-8 bytes; terminated as a string key follows
"pi": 3.14,70 69 8F 40 09 1E B8 51 EB 85 1Fkey: UTF-8 bytes; value: binary64 (IEEE 754, big-endian); its marker also ends "pi"
"tags": [74 61 67 73 82key: UTF-8 bytes; value: array with 2 elements; its marker also ends "tags"
"a",61 FFUTF-8 bytes; terminated as a string follows
"b"62 FFUTF-8 bytes; terminated as nothing follows
]no end marker: the element count was given up front
}no end marker: the member count was given up front
+ ## Serialization The library uses the following mapping from JSON values types to BON8 types according to the BON8 specification: diff --git a/docs/mkdocs/docs/features/binary_formats/bson.md b/docs/mkdocs/docs/features/binary_formats/bson.md index f205cba17..9c82594dd 100644 --- a/docs/mkdocs/docs/features/binary_formats/bson.md +++ b/docs/mkdocs/docs/features/binary_formats/bson.md @@ -10,6 +10,39 @@ representation of data types that are not part of the JSON spec. For example, BS - [BSON Specification](http://bsonspec.org/spec.html) - the specification +## Byte layout + +A BSON document is a length-prefixed sequence of *elements*. The first four bytes hold the total size of the document (little-endian int32), followed by the elements, and a final `0x00` byte ends the document. Each element starts with a one-byte type, followed by its key as a NUL-terminated string and the value, whose encoding depends on the type. Arrays are documents whose keys are the decimal indices `"0"`, `"1"`, ...; nesting is expressed by embedding documents. Because every document announces its size, a reader can skip over it without parsing its content. The top level of a BSON value is always a document, which is why only JSON objects can be serialized. + +Consider the following JSON value (shown formatted for readability): + +```json +{ + "id": 300, + "name": "json", + "pi": 3.14, + "tags": [ + "a", + "b" + ] +} +``` + +`to_bson` encodes it to 69 bytes (shown in hexadecimal; the encoding contains no whitespace). The colors mark the role of each byte: type/marker length/count/size data. + +
+ + + + + + + + + + +
JSONBytesMeaning
{45 00 00 00document size: 69 bytes (int32, little-endian)
"id": 300,10 69 64 00 2C 01 00 00int32 element; key as NUL-terminated string; value (little-endian)
"name": "json",02 6E 61 6D 65 00 05 00 00 00 6A 73 6F 6E 00string element; key; length incl. NUL (int32); NUL-terminated UTF-8 bytes
"pi": 3.14,01 70 69 00 1F 85 EB 51 B8 1E 09 40double element; key; value (little-endian)
"tags": [04 74 61 67 73 00 17 00 00 00array element; key; embedded document of 23 bytes (keys are the indices)
"a",02 30 00 02 00 00 00 61 00string element; key "0"; length 2; a + NUL
"b"02 31 00 02 00 00 00 62 00string element; key "1"; length 2; b + NUL
]00end of the array document
}00end of the document
+ ## Serialization The library uses the following mapping from JSON values types to BSON types: @@ -60,7 +93,6 @@ The library uses the following mapping from JSON values types to BSON types: --8<-- "examples/to_bson.output" ``` - ## Deserialization The library maps BSON record types to JSON value types as follows: diff --git a/docs/mkdocs/docs/features/binary_formats/cbor.md b/docs/mkdocs/docs/features/binary_formats/cbor.md index a0a0ed8d0..8933630da 100644 --- a/docs/mkdocs/docs/features/binary_formats/cbor.md +++ b/docs/mkdocs/docs/features/binary_formats/cbor.md @@ -9,6 +9,39 @@ extremely small code sizes, fairly small message size, and extensibility without - [CBOR Playground](http://cbor.me) - an interactive webpage to translate between JSON and CBOR - [RFC 8949](https://www.rfc-editor.org/rfc/rfc8949.html) - the CBOR specification +## Byte layout + +CBOR encodes every data item as a *head* followed by optional content. The first byte of the head holds the *major type* in its upper three bits (unsigned integer, negative integer, byte string, text string, array, map, tag, or simple/float) and an *additional information* value in its lower five bits. Additional information below 24 is the argument itself (for example, a small integer or a short length); the values 24 to 27 announce that the argument follows in 1, 2, 4, or 8 big-endian bytes. Strings, arrays, and maps are prefixed with their length (or count of elements/pairs), and content follows directly, so a reader always knows how many items to expect without a terminator. + +Consider the following JSON value (shown formatted for readability): + +```json +{ + "id": 300, + "name": "json", + "pi": 3.14, + "tags": [ + "a", + "b" + ] +} +``` + +`to_cbor` encodes it to 39 bytes (shown in hexadecimal; the encoding contains no whitespace). The colors mark the role of each byte: type/marker data. + +
+ + + + + + + + + + +
JSONBytesMeaning
{A4map with 4 key/value pairs
"id": 300,62 69 64 19 01 2Ckey: text string of length 2, then its UTF-8 bytes; value: unsigned integer, 2 bytes follow (big-endian)
"name": "json",64 6E 61 6D 65 64 6A 73 6F 6Ekey: text string of length 4; value: text string of length 4
"pi": 3.14,62 70 69 FB 40 09 1E B8 51 EB 85 1Fkey: text string of length 2; value: double-precision float (IEEE 754, big-endian)
"tags": [64 74 61 67 73 82key: text string of length 4; value: array with 2 elements
"a",61 61text string of length 1
"b"61 62text string of length 1
]no end marker: the element count was given up front
}no end marker: the member count was given up front
+ ## Serialization The library uses the following mapping from JSON values types to CBOR types according to the CBOR specification diff --git a/docs/mkdocs/docs/features/binary_formats/index.md b/docs/mkdocs/docs/features/binary_formats/index.md index 2c45995a6..8024b99cf 100644 --- a/docs/mkdocs/docs/features/binary_formats/index.md +++ b/docs/mkdocs/docs/features/binary_formats/index.md @@ -45,12 +45,19 @@ See [binary values](../binary_values.md) for more information. | BJData | 53.2 % | 91.1 % | 78.1 % | 96.6 % | | BJData (size) | 58.6 % | 92.1 % | 86.7 % | 97.4 % | | BJData (size+type) | 58.6 % | 92.1 % | 86.5 % | 97.4 % | -| BON8 | 50.5 % | 83.8 % | 63.5 % | 87.5 % | +| BON8 | **50.5 %** | **83.8 %** | **63.5 %** | **87.5 %** | | BSON | 85.8 % | 95.2 % | 95.8 % | 106.7 % | -| CBOR | 50.5 % | 86.3 % | 68.4 % | 88.0 % | -| MessagePack | 50.5 % | 86.0 % | 68.5 % | 87.9 % | +| CBOR | **50.5 %** | 86.3 % | 68.4 % | 88.0 % | +| MessagePack | **50.5 %** | 86.0 % | 68.5 % | 87.9 % | | UBJSON | 53.2 % | 91.3 % | 78.2 % | 96.6 % | | UBJSON (size) | 58.6 % | 92.3 % | 86.8 % | 97.4 % | | UBJSON (size+type) | 55.9 % | 92.3 % | 85.0 % | 95.0 % | -Sizes compared to minified JSON value. +Sizes compared to minified JSON value; the smallest size per file is printed in bold. + +## See also + +- [Binary values](../binary_values.md) - binary values and subtypes +- [**to_cbor**](../../api/basic_json/to_cbor.md) / [**from_cbor**](../../api/basic_json/from_cbor.md), [**to_msgpack**](../../api/basic_json/to_msgpack.md) / [**from_msgpack**](../../api/basic_json/from_msgpack.md), [**to_bson**](../../api/basic_json/to_bson.md) / [**from_bson**](../../api/basic_json/from_bson.md), [**to_ubjson**](../../api/basic_json/to_ubjson.md) / [**from_ubjson**](../../api/basic_json/from_ubjson.md), [**to_bjdata**](../../api/basic_json/to_bjdata.md) / [**from_bjdata**](../../api/basic_json/from_bjdata.md), [**to_bon8**](../../api/basic_json/to_bon8.md) / [**from_bon8**](../../api/basic_json/from_bon8.md) - the conversion functions +- [Parsing untrusted input](../parsing/untrusted_input.md) +- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value diff --git a/docs/mkdocs/docs/features/binary_formats/messagepack.md b/docs/mkdocs/docs/features/binary_formats/messagepack.md index 24eeab139..90e4c295e 100644 --- a/docs/mkdocs/docs/features/binary_formats/messagepack.md +++ b/docs/mkdocs/docs/features/binary_formats/messagepack.md @@ -9,6 +9,39 @@ extra byte in addition to the strings themselves. - [MessagePack website](https://msgpack.org) - [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md) +## Byte layout + +MessagePack encodes each value as a one-byte *format marker*, optionally followed by a length or the payload. The marker range is partitioned to keep common values small: small non-negative integers (0 to 127) and small negative integers (-32 to -1) are the marker byte itself; short strings, arrays, and maps (up to 31 or 15 elements) hide their length in the marker's lower bits. Larger values use dedicated markers that announce the width (8, 16, 32, or 64 bits) of the length or number that follows, in big-endian byte order. Like CBOR, containers are length-prefixed, so no terminator is needed. + +Consider the following JSON value (shown formatted for readability): + +```json +{ + "id": 300, + "name": "json", + "pi": 3.14, + "tags": [ + "a", + "b" + ] +} +``` + +`to_msgpack` encodes it to 39 bytes (shown in hexadecimal; the encoding contains no whitespace). The colors mark the role of each byte: type/marker data. + +
+ + + + + + + + + + +
JSONBytesMeaning
{84map with 4 key/value pairs
"id": 300,A2 69 64 CD 01 2Ckey: fixstr of length 2, then its UTF-8 bytes; value: uint 16 (big-endian)
"name": "json",A4 6E 61 6D 65 A4 6A 73 6F 6Ekey: fixstr of length 4; value: fixstr of length 4
"pi": 3.14,A2 70 69 CB 40 09 1E B8 51 EB 85 1Fkey: fixstr of length 2; value: float 64 (IEEE 754, big-endian)
"tags": [A4 74 61 67 73 92key: fixstr of length 4; value: fixarray with 2 elements
"a",A1 61fixstr of length 1
"b"A1 62fixstr of length 1
]no end marker: the element count was given up front
}no end marker: the member count was given up front
+ ## Serialization The library uses the following mapping from JSON values types to MessagePack types according to the MessagePack diff --git a/docs/mkdocs/docs/features/binary_formats/ubjson.md b/docs/mkdocs/docs/features/binary_formats/ubjson.md index f7a14d855..ba12ee4f6 100644 --- a/docs/mkdocs/docs/features/binary_formats/ubjson.md +++ b/docs/mkdocs/docs/features/binary_formats/ubjson.md @@ -7,6 +7,39 @@ achieve the generality of JSON, combined with being much easier to process than - [UBJSON Website](http://ubjson.org) +## Byte layout + +UBJSON is built from just a one-byte, human-readable ASCII *type marker* (for example, `i` for int8, `S` for string, `[` for array) followed by the payload of that type. Strings and the like are prefixed with their length, which is itself a UBJSON integer value including its marker. Numbers are stored in big-endian byte order. Containers can be written in two ways: as an *unoptimized* container that is opened with `[` or `{` and closed with `]` or `}`, or as an *optimized* container that declares an element type (`$`) and/or a count (`#`) after the opening marker, which saves per-element markers and (for a count) the closing marker. + +Consider the following JSON value (shown formatted for readability): + +```json +{ + "id": 300, + "name": "json", + "pi": 3.14, + "tags": [ + "a", + "b" + ] +} +``` + +`to_ubjson` encodes it to 51 bytes (shown in hexadecimal; the encoding contains no whitespace). The colors mark the role of each byte: type/marker length/count/size data. + +
+ + + + + + + + + + +
JSONBytesMeaning
{7Bobject start ({)
"id": 300,69 02 69 64 49 01 2Ckey: length 2 as int8 (i), then UTF-8 bytes; value: int16 (I), big-endian
"name": "json",69 04 6E 61 6D 65 53 69 04 6A 73 6F 6Ekey: length 4; value: string (S), length 4 as int8, UTF-8 bytes
"pi": 3.14,69 02 70 69 44 40 09 1E B8 51 EB 85 1Fkey: length 2; value: float64 (D), big-endian
"tags": [69 04 74 61 67 73 5Bkey: length 4; value: array start ([)
"a",53 69 01 61string, length 1
"b"53 69 01 62string, length 1
]5Darray end (])
}7Dobject end (})
+ ## Serialization The library uses the following mapping from JSON values types to UBJSON types according to the UBJSON specification: diff --git a/docs/mkdocs/docs/features/conversions.md b/docs/mkdocs/docs/features/conversions.md index 5ba42b7cf..c0d383a5a 100644 --- a/docs/mkdocs/docs/features/conversions.md +++ b/docs/mkdocs/docs/features/conversions.md @@ -38,6 +38,18 @@ which avoids repeating it: --8<-- "examples/get_to.output" ``` +!!! info "Getting values out of a document or a view" + + A [view](../api/basic_json_view/index.md) offers the same `get()` conversion. For the built-in types (booleans, + numbers, strings, `#!cpp std::vector`, `#!cpp std::map`, ...) it reads the value straight from the document's + index, without building a `basic_json` value first; user-defined types with a `from_json()` are converted from a + `basic_json` value, see the [list](../api/basic_json_view/get.md). Two conversions never copy at all: + [`get_string()`](../api/basic_json_view/get_string.md) returns a string as a view into the source text, and + [`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other + type is obtained from a subtree that is built on demand with + [`materialize()`](../api/basic_json_view/materialize.md). See + [Zero-copy JSON views](json_view.md#getting-values-out-without-copying). + The library already knows how to convert to and from the scalar types and the STL containers (such as `#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON object back to a `#!cpp std::map` or a JSON array back to a `#!cpp std::vector` therefore works without any extra code: @@ -112,6 +124,29 @@ Implicit conversions are convenient but can be surprising (for example, in overl be disabled by defining [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) to `#!cpp 0`, which forces the explicit `get` form and can catch unintended conversions at compile time. +!!! warning "`auto` copies the JSON value; it does not convert it" + + The type of `#!cpp j["name"]` is `json`, not the C++ type of the value inside. `#!cpp auto` therefore deduces + `json`: + + ```cpp + json j = {{"name", "Mary"}, {"age", 42}}; + + auto name = j["name"]; // json: a copy of the value; prints as "Mary" (with quotes) + std::string s1 = j["name"]; // std::string: implicit conversion + auto s2 = j["name"].get(); // std::string: explicit conversion + auto& age = j["age"]; // json&: a reference, no copy + age = 43; // changes j + ``` + + This is also why `#!cpp std::cout << j["name"]` prints the quotes (see + [getting a string without quotes](#getting-values-out)), and why a `json` value cannot be passed to `printf`: the + argument is not an `int` or a `char*`, and a compiler either rejects passing it through `...` (Clang does) or + compiles it and prints garbage. Convert first: `#!cpp std::printf("%d", j["age"].get());`. + + Prefer `get()` over `auto` when you want the C++ value, and `auto&` or `const auto&` when you want to avoid a + copy of a large value. + !!! warning "Conversions do not range-check numbers" Just like C++ itself, the `get` family performs numeric conversions without range checks — retrieving a @@ -234,3 +269,4 @@ The conversions above are built in for standard types. To make the same syntax w - [`get_ref`](../api/basic_json/get_ref.md) / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying - [Arbitrary types conversions](arbitrary_types.md) - support your own types - [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions +- [Zero-copy JSON views](json_view.md) - get values out of a document or a view diff --git a/docs/mkdocs/docs/features/creating_values.md b/docs/mkdocs/docs/features/creating_values.md index fc35b3401..fd4c32cd8 100644 --- a/docs/mkdocs/docs/features/creating_values.md +++ b/docs/mkdocs/docs/features/creating_values.md @@ -1,7 +1,9 @@ # Creating JSON values There are several ways to create a JSON value in memory. This page gives an overview; to read a value from JSON text -instead, see [parsing](parsing/index.md). +instead, see [parsing](parsing/index.md). A JSON text can also be parsed into a +[document](../api/basic_json_document/index.md) and read through a [view](../api/basic_json_view/index.md) instead of +into a value; see [Zero-copy JSON views](json_view.md). ## From C++ values @@ -59,6 +61,25 @@ with a string as the first element is treated as an object, everything else as a [FAQ](../home/faq.md#brace-initialization-yields-arrays) for details and the opt-in [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) macro. +### What does this produce? + +The following table shows how some common spellings are interpreted. When in doubt, use `#!cpp json::array()`, +`#!cpp json::object()`, or the `#!cpp json(value)` form with parentheses. + +| code | result | note | +|-------------------------------------------------|------------------------------|----------------------------------------------| +| `#!cpp json j = {};` / `#!cpp json j{};` | `#!json null` | use `json::object()` or `json::array()` | +| `#!cpp json j = json::object();` | `#!json {}` | | +| `#!cpp json j = json::array();` | `#!json []` | | +| `#!cpp json j(true);` | `#!json true` | parentheses create the value itself | +| `#!cpp json j = {true};` | `#!json [true]` | braces create a one-element array (`#!cpp json j{true};` depends on the compiler, see the [FAQ](../home/faq.md#brace-initialization-yields-arrays)) | +| `#!cpp json j = {{"a", 1}};` | `#!json {"a":1}` | all elements are `[string, value]` pairs: object | +| `#!cpp json j = {{"a", 1}, {"b"}};` | `#!json [["a",1],["b"]]` | one element is not a pair: array | +| `#!cpp json j = {{"a", 1}, 2};` | `#!json [["a",1],2]` | a plain value among pairs: array | +| `#!cpp json j = {{"a", 1, 2}};` | `#!json [["a",1,2]]` | a three-element list is not a pair: array | +| `#!cpp json j = "{\"a\":1}";` | `#!json "{\"a\":1}"` | a string **value**; it is not parsed | +| `#!cpp json j = json::parse("{\"a\":1}");` | `#!json {"a":1}` | parse text with `parse` or `_json` | + ## Building incrementally A value can also be built up piece by piece. Accessing a non-existing object key or array index with @@ -101,3 +122,4 @@ string `#!json "42"`. - [`operator""_json`](../api/operator_literal_json.md) - the `_json` literal - [Converting values](conversions.md) - which C++ types can be used - [Parsing](parsing/index.md) - create a value from JSON text +- [Zero-copy JSON views](json_view.md) - create a document and a view from JSON text instead of a value diff --git a/docs/mkdocs/docs/features/element_access/index.md b/docs/mkdocs/docs/features/element_access/index.md index 262c057b8..4ec625a20 100644 --- a/docs/mkdocs/docs/features/element_access/index.md +++ b/docs/mkdocs/docs/features/element_access/index.md @@ -21,3 +21,51 @@ flowchart TD A --> F{"just testing first?"} F -->|"yes"| G["contains() / find()"] ``` + +## Does this key exist? + +Use [`contains`](../../api/basic_json/contains.md) to test for a key; it does not modify the value and does not throw: + +```cpp +json j = {{"name", "Mary"}, {"address", {{"city", "Berlin"}}}}; + +j.contains("name"); // true +j.contains("age"); // false +j.contains("city"); // false: contains() only looks at the top level +j["address"].contains("city"); // true +j.contains(json::json_pointer("/address/city")); // true: a JSON pointer tests a nested path in one call +``` + +If the value is needed afterwards, [`find`](../../api/basic_json/find.md) tests and accesses with a single lookup: + +```cpp +if (auto it = j.find("name"); it != j.end()) +{ + std::cout << *it << '\n'; +} +``` + +`contains` also returns `true` for a key whose value is `null`; use `j.contains("k") && !j["k"].is_null()` if a +`null` should count as missing. To read a value with a fallback instead, see [`value`](default_value.md). + +!!! warning "`operator[]` inserts missing keys" + + Like `#!cpp std::map::operator[]`, the non-const [`operator[]`](unchecked_access.md) adds a `null` value if the key + is missing. Testing a key with it therefore *changes the object*: + + ```cpp + json j = {{"foo", 1}}; + bool absent = j["bar"].is_null(); // true, but j is now {"bar": null, "foo": 1} + ``` + + Use `contains`/`find` to test, and [`at`](checked_access.md) to access a value that must exist (it throws an + [`out_of_range`](../../home/exceptions.md#out-of-range) exception otherwise). + +## See also + +- [**operator[]**](../../api/basic_json/operator%5B%5D.md) - unchecked access +- [**at**](../../api/basic_json/at.md) - checked access +- [**value**](../../api/basic_json/value.md) - access with default value +- [**contains**](../../api/basic_json/contains.md) and [**find**](../../api/basic_json/find.md) - test for existence +- [**front**](../../api/basic_json/front.md) and [**back**](../../api/basic_json/back.md) - first and last element +- [**items**](../../api/basic_json/items.md) - iterate over keys and values diff --git a/docs/mkdocs/docs/features/index.md b/docs/mkdocs/docs/features/index.md index 4ffe7e585..88443c796 100644 --- a/docs/mkdocs/docs/features/index.md +++ b/docs/mkdocs/docs/features/index.md @@ -59,3 +59,11 @@ C++ types, and finally serialize it again. This section gives conceptual overviews. For the precise signature, parameters, and return value of a function, see the [API Documentation](../api/basic_json/index.md). + +## See also + +- [API documentation](../api/basic_json/index.md) - reference of all classes and functions +- [FAQ](../home/faq.md) - answers to common questions +- [Exceptions](../home/exceptions.md) - all exceptions and their error codes +- [Design goals](../home/design_goals.md) +- [Integration](../integration/index.md) - how to add the library to a project diff --git a/docs/mkdocs/docs/features/macros.md b/docs/mkdocs/docs/features/macros.md index 620cbe0da..773ea5ac3 100644 --- a/docs/mkdocs/docs/features/macros.md +++ b/docs/mkdocs/docs/features/macros.md @@ -287,6 +287,23 @@ external dependency and is not defined by default. See [full documentation of `JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md). +## `JSON_VIEW_NO_SIMD` + +When defined, the parser of [`basic_json_document`](../api/basic_json_document/index.md) +(``) uses only portable C++ to scan strings, instead of vector instructions (SSE2 on x86-64, +NEON on AArch64). The same input is accepted or rejected either way; only the speed differs. It is not defined by +default. + +See [full documentation of `JSON_VIEW_NO_SIMD`](../api/macros/json_view_no_simd.md). + +## `JSON_VIEW_USE_SSSE3` + +When defined on x86-64, the parser of [`basic_json_document`](../api/basic_json_document/index.md) +(``) validates non-ASCII text in strings with SSSE3 without asking the CPU first. It is not +defined by default. + +See [full documentation of `JSON_VIEW_USE_SSSE3`](../api/macros/json_view_use_ssse3.md). + ## `NLOHMANN_DEFINE_TYPE_*(...)`, `NLOHMANN_DEFINE_DERIVED_TYPE_*(...)` The library defines 12 macros to simplify the serialization/deserialization of types. See the page on diff --git a/docs/mkdocs/docs/features/modifying_values.md b/docs/mkdocs/docs/features/modifying_values.md index 10814ac2e..1d6f2e27d 100644 --- a/docs/mkdocs/docs/features/modifying_values.md +++ b/docs/mkdocs/docs/features/modifying_values.md @@ -85,6 +85,18 @@ json a = {1, 2, 3, 4}; a.erase(1); // [1,3,4] (erase by index) ``` +## Editing a document + +The operations above work on a `basic_json` value. A JSON text that was parsed into a +[document](json_view.md#editing-a-document) can also be changed in place, without building a `basic_json` tree for the +parts that are not touched: [`set`](../api/basic_json_document/set.md) replaces a value, adds or replaces an object +member, or replaces an array element (also through a JSON pointer), +[`push_back`](../api/basic_json_document/push_back.md) appends to an array, +[`insert`](../api/basic_json_document/insert.md) inserts into an array (unlike `basic_json::insert`, it has no object +overload; members are added with `set`), and [`erase`](../api/basic_json_document/erase.md) removes a member or an +element. These functions are available on the +editable document types, such as [`json_editable_document`](../api/json_editable_document.md). + ## See also - [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array @@ -93,3 +105,4 @@ a.erase(1); // [1,3,4] (erase by index) - [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7396 merge patch - [`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 +- [Editing a document](json_view.md#editing-a-document) - change a parsed document in place diff --git a/docs/mkdocs/docs/features/modules.md b/docs/mkdocs/docs/features/modules.md index 28b538a2e..b87cb8950 100644 --- a/docs/mkdocs/docs/features/modules.md +++ b/docs/mkdocs/docs/features/modules.md @@ -15,7 +15,7 @@ When using modules rather than headers, the previous example for creating a `jso import std; import nlohmann.json; -using json = nlohmann::json; +using nlohmann::json; // ... diff --git a/docs/mkdocs/docs/features/no_exceptions.md b/docs/mkdocs/docs/features/no_exceptions.md new file mode 100644 index 000000000..cfb489876 --- /dev/null +++ b/docs/mkdocs/docs/features/no_exceptions.md @@ -0,0 +1,113 @@ +# Working Without Exceptions + +By default, the library reports errors by throwing exceptions (see [Exceptions](../home/exceptions.md)). This page +collects what to use when exceptions are unwanted or unavailable, for example in embedded systems, game engines, or +code compiled with `-fno-exceptions`. + +## Two different questions + +1. **Can my code avoid *catching* exceptions?** Yes. For parsing and for accessing values there are functions that + report failure through a return value. They work in a normal build and need no `#!cpp try`/`#!cpp catch`. +2. **Can the library be *compiled* without exceptions?** Yes, with `-fno-exceptions` or by defining + [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md). Both have the same effect: every `#!cpp throw` in the + library becomes a call to `std::abort()`. The functions listed below never reach a `#!cpp throw` for the failures + they report, so they keep working in this configuration. Anything else that fails aborts the program. + +## Parsing + +| function | on invalid input | +|---------------------------------------------------------------------------------------------|-----------------------------------------------------| +| `#!cpp json::parse(input, nullptr, false)` | returns a *discarded* value; test with `is_discarded()` | +| [`accept`](../api/basic_json/accept.md) | returns `#!cpp false` | +| `#!cpp json::from_cbor(input, true, false)` (and the other `from_*` functions) | returns a *discarded* value | +| [`sax_parse`](../api/basic_json/sax_parse.md) with your own handler | returns `#!cpp false` after calling `parse_error` | + +??? example "Example: parse and validate without exceptions" + + ```cpp + --8<-- "examples/no_exceptions_parse.cpp" + ``` + + Output: + + ``` + --8<-- "examples/no_exceptions_parse.output" + ``` + +None of the calls in this example throws, so it also compiles and runs with `-fno-exceptions`. + +The discarded value carries no message. If you need the position or the reason, use the SAX interface; see +[Parsing and exceptions](parsing/parse_exceptions.md). + +## Accessing values + +The access functions are not equally safe. Only some of them never throw: + +| expression | if the key is missing or the type is wrong | +|-------------------------------------|-------------------------------------------------------------------------------| +| `#!cpp j.contains("k")` | returns `#!cpp false` for a missing key; never throws | +| `#!cpp j.find("k")` | returns `end()` for a missing key; never throws | +| `#!cpp j.is_string()`, ... | the `is_*` functions test the type; never throw | +| `#!cpp j.get_ptr()` | returns `#!cpp nullptr` for a different type; never throws | +| `#!cpp j.at("k")` | throws `out_of_range.403` for a missing key | +| `#!cpp j.get()` | throws `type_error.302` for a value that is not a number | +| `#!cpp j.value("k", 0)` | returns the default for a missing key, but **throws `type_error.302` if `k` exists with another type** | +| `#!cpp j["k"]` (non-const) | inserts `null` for a missing key | +| `#!cpp j["k"]` (const) | undefined behavior for a missing key | + +So reading a value that may be absent or of the wrong type without exceptions takes two steps: look up, then test the +type. + +??? example "Example: look up a key and test its type" + + ```cpp + --8<-- "examples/no_exceptions_access.cpp" + ``` + + Output: + + ``` + --8<-- "examples/no_exceptions_access.output" + ``` + +The last lines of the example show why `value` is not on the safe list: it returns the default only for a *missing* +key. + +For numbers, test with `is_number()` (or `is_number_integer()`, `is_number_unsigned()`, `is_number_float()`) before +calling `get()`, and see [number handling](types/number_handling.md) for range and precision. Whether a value +fits into the target type is not checked by `get`. + +## Converting to your own types + +`from_json` functions (including the ones generated by the +[`NLOHMANN_DEFINE_TYPE_*` macros](arbitrary_types.md#simplify-your-life-with-macros)) report problems by throwing. +There is no variant that returns an error code. Without exceptions you have two options: + +- Validate the JSON value yourself, as shown above, and fill your struct field by field. +- Convert in a function that is compiled **with** exceptions enabled and catches them at the boundary, returning + `std::optional` or an error code to the rest of your code: + +??? example "Example: convert to a struct and report failure as std::optional" + + ```cpp + --8<-- "examples/no_exceptions_convert.c++17.cpp" + ``` + + Output: + + ``` + --8<-- "examples/no_exceptions_convert.c++17.output" + ``` + +## Overriding what happens instead of an exception + +With exceptions off, you can replace the `abort()` by your own code, for example to log the message first. See +[Switch off exceptions](../home/exceptions.md#switch-off-exceptions) and +[`JSON_THROW_USER`](../api/macros/json_throw_user.md). The handler must not return normally. + +## See also + +- [Exceptions](../home/exceptions.md) - all exceptions and their ids +- [Parsing and exceptions](parsing/parse_exceptions.md) - the parsing alternatives in detail +- [Parsing untrusted input](parsing/untrusted_input.md) +- [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md) diff --git a/docs/mkdocs/docs/features/object_order.md b/docs/mkdocs/docs/features/object_order.md index b90637711..b9997238e 100644 --- a/docs/mkdocs/docs/features/object_order.md +++ b/docs/mkdocs/docs/features/object_order.md @@ -12,7 +12,7 @@ The default type `nlohmann::json` uses a `std::map` to store JSON objects, and t #include #include - using json = nlohmann::json; + using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/features/parsing/index.md b/docs/mkdocs/docs/features/parsing/index.md index 07fb6500d..447b65ffc 100644 --- a/docs/mkdocs/docs/features/parsing/index.md +++ b/docs/mkdocs/docs/features/parsing/index.md @@ -18,6 +18,7 @@ flowchart LR The [`parse`](../../api/basic_json/parse.md) function reads a JSON value from an input. The input can be - a string (`#!cpp std::string`, C string, or string literal), +- a wide string (`#!cpp std::u16string`, `#!cpp std::u32string`, or `#!cpp std::wstring`, or a wide string literal), - a `#!cpp std::istream` (e.g., an `#!cpp std::ifstream` reading from a file), - a `#!cpp FILE*` pointer, - a pair of iterators over a contiguous range (e.g., a `#!cpp std::vector`), or @@ -32,12 +33,20 @@ std::ifstream f("example.json"); json data = json::parse(f); ``` -The input must be encoded in UTF-8; other encodings are not supported. A single input may contain only one JSON value. -Inputs consisting of multiple values separated by newlines are handled by the [JSON Lines](json_lines.md) format. +The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which +is read as UTF-16 or UTF-32 -- depending on the character type -- and converted to UTF-8; see the +[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value. +Inputs consisting of multiple values separated by newlines are handled by the [JSON Lines](json_lines.md) format; input +that arrives in chunks or contains several values is covered in +[Large, streamed, and partial input](large_and_partial_input.md). By default, the library rejects comments and trailing commas. Both can be enabled with parameters of the `parse` function — see [comments](../comments.md) and [trailing commas](../trailing_commas.md). +[`basic_json_document::parse`](../../api/basic_json_document/parse.md) accepts the same inputs with the same options, +but keeps the result in a document rather than in a `basic_json` value; see +[Documents and views](#documents-and-views) below. + ## Strictness and trailing data [`parse`](../../api/basic_json/parse.md) reads a single JSON value and requires the whole input to be consumed: any @@ -85,7 +94,7 @@ again without changing any result. ## SAX vs. DOM parsing -The library offers two parsing models: +The library offers two parsing models, and a third, read-only alternative: - **DOM parsing** (the default): the complete input is read and stored as an in-memory `basic_json` value that can be traversed and modified freely. This is what [`parse`](../../api/basic_json/parse.md) does, and it is the right choice @@ -95,9 +104,36 @@ The library offers two parsing models: inputs or when you only need to extract parts of the input. See the [SAX interface](sax_interface.md) for details and [`sax_parse`](../../api/basic_json/sax_parse.md) for the API. +- **Document and views** (`#!cpp `, added in version 3.13.0): [`json_document::parse`](../../api/basic_json_document/parse.md) + reads the same JSON grammar but builds a flat, read-only index of the values instead of a `basic_json` tree. Strings and + floating-point numbers are read from the text on demand, and only the parts you + [`materialize()`](../../api/basic_json_view/materialize.md) become `basic_json` values. This avoids most of the work when you + need only a few values from a large or frequently parsed text; see [Zero-copy JSON views](../json_view.md). It is read-only, so use + `parse` when you need to modify the value. + You can influence a DOM parse without switching to the SAX interface by passing a [parser callback](parser_callbacks.md), which is called during parsing and can, for example, discard parts of the input. +## Documents and views + +A third model sits between DOM and SAX: [`basic_json_document::parse`](../../api/basic_json_document/parse.md) parses +the input into a document that keeps the text and a flat index of the values in it, and +[`basic_json_view`](../../api/basic_json_view/index.md) gives read-only access to the values through that index. +The whole input is validated during parsing, but only integers and strings that contain escape sequences are converted +right away (the strings into a buffer owned by the document). Floating-point numbers and all other strings stay in the +source text and are only converted when they are read, which is faster and uses less memory than building a tree of `basic_json` values when only parts +of the document are needed. A document can also be +modified in place, and it can be saved and loaded again without parsing it. + +```cpp +#include + +json_document doc = json_document::parse(R"({"happy": true, "pi": 3.141})"); +std::cout << doc.root()["happy"].get() << '\n'; +``` + +See [Zero-copy JSON views](../json_view.md) for the concept and the trade-offs. + ## Exceptions When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or @@ -108,10 +144,14 @@ options. ## See also - [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input +- [`basic_json_document::parse`](../../api/basic_json_document/parse.md) - deserialize into a document instead of a value - [`accept`](../../api/basic_json/accept.md) - check if the input is valid JSON - [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events - [JSON Lines](json_lines.md) - parse newline-delimited JSON +- [Large, streamed, and partial input](large_and_partial_input.md) - chunked, concatenated, and very large input - [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function - [SAX interface](sax_interface.md) - implement a custom SAX handler +- [Zero-copy JSON views](../json_view.md) - parse into a read-only document and read it through views - [parsing and exceptions](parse_exceptions.md) - control error handling - [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources +- [Zero-copy JSON views](../json_view.md) - parse into a document and read it through a view diff --git a/docs/mkdocs/docs/features/parsing/large_and_partial_input.md b/docs/mkdocs/docs/features/parsing/large_and_partial_input.md new file mode 100644 index 000000000..d24b5fab1 --- /dev/null +++ b/docs/mkdocs/docs/features/parsing/large_and_partial_input.md @@ -0,0 +1,154 @@ +# Large, Streamed, and Partial Input + +[`parse`](../../api/basic_json/parse.md) reads exactly one complete JSON value and builds it in memory. It cannot be +paused and resumed, and it does not hand back a half-parsed value when the input ends early. This page shows what to do +when the input does not fit that model: it arrives in pieces, it contains several values, or it is too large to hold +as a `json` value. + +```mermaid +flowchart TD + A["my input is..."] --> B{"several JSON values in one stream?"} + B -->|"yes"| C["read them one at a time"] + A --> D{"arriving in chunks (socket, pipe)?"} + D -->|"yes"| E["frame the messages, or buffer until complete"] + A --> F{"too large to keep in memory?"} + F -->|"yes"| G["discard parts with a callback, or use SAX"] + A --> H{"only a small part is needed?"} + H -->|"yes"| I["stop early with SAX, or read through a view"] +``` + +## Several values in one stream + +For newline-delimited input, read line by line and parse each line, as described in [JSON Lines](json_lines.md). For +values that are simply concatenated, [`operator>>`](../../api/operator_gtgt.md) reads one value and leaves the stream +positioned after it. Check for the end of the stream before each read, because a read at the end throws: + +??? example "Example: read concatenated values" + + ```cpp + --8<-- "examples/partial_input_concatenated.cpp" + ``` + + Output: + + ``` + --8<-- "examples/partial_input_concatenated.output" + ``` + +The same works for binary formats. [`from_cbor`](../../api/basic_json/from_cbor.md) and its relatives accept +`#!cpp strict = false` to stop after the first value and leave the rest of the stream untouched; `tellg()` then reports +how many bytes the value took: + +??? example "Example: read one CBOR value and find out how many bytes it used" + + ```cpp + --8<-- "examples/partial_input_cbor.cpp" + ``` + + Output: + + ``` + --8<-- "examples/partial_input_cbor.output" + ``` + +## Input arriving in chunks + +A socket, pipe, or HTTP body hands over data in pieces of arbitrary size, and a piece can end in the middle of a +number, a string, or an object. The parser does not keep state between calls, so there are two ways to deal with this. + +**Frame the messages.** The most robust approach is a protocol that says where a value ends: one value per line +([JSON Lines](json_lines.md)), a length prefix, or a delimiter. Collect the bytes of one message and parse them when +the frame is complete. Prefer this whenever you control the protocol. + +**Buffer until the value is complete.** If the data has no framing, append every chunk to a buffer and try to parse +it. A parse error at the very end of the buffer means that the input ended prematurely, while an error anywhere else is +a real syntax error. The member `byte` of the [`parse_error`](../../home/exceptions.md#jsonexceptionparse_error101) +exception is one past the buffer size in the first case: + +??? example "Example: buffer chunks until the value is complete" + + ```cpp + --8<-- "examples/partial_input_buffering.cpp" + ``` + + Output: + + ``` + --8<-- "examples/partial_input_buffering.output" + ``` + +The example feeds a document in five chunks. The first four are reported as incomplete, and a syntax error is reported as +such, not as missing data. + +!!! warning "Limits of buffering" + + - The buffer must contain exactly one value. Data that belongs to the next value is a syntax error. Use framing if + values follow each other without a separator. + - A number at the end of the buffer is a complete value, even if more digits are still to come (`12` may be the + start of `123`). Buffering cannot detect this; use framing for top-level numbers. + - Every attempt parses the buffer from the beginning, so the total work grows quadratically with the size of the + value. Limit the size of the buffer for input you do not trust (see [untrusted input](untrusted_input.md)). + +## Input too large for memory + +The input does not have to become one huge `json` value. The two tools below process it piece by piece. + +### Handle and discard elements with a callback + +A [parser callback](parser_callbacks.md) is called for every finished value. If it returns `#!cpp false`, the value is +discarded and its memory is released at once. For a file that consists of one huge array (or one huge object) of +records, handle each record at depth 1 and discard it: + +??? example "Example: handle and discard each record of a large array" + + ```cpp + --8<-- "examples/partial_input_discard_records.cpp" + ``` + + Output: + + ``` + --8<-- "examples/partial_input_discard_records.output" + ``` + +Only one record is in memory at any time. The same recipe, with more detail, is in the +[parser callbacks](parser_callbacks.md#recipe-streaming-a-large-homogeneous-array) page. To skip entire members you do not +need, discard them when their key is read; see the [example](parser_callbacks.md) for `parse_event_t::key`. + +### Extract a few values and stop with SAX + +With the [SAX interface](sax_interface.md), no value is built at all, and **returning `#!cpp false` from any handler +function stops the parser**. [`sax_parse`](../../api/basic_json/sax_parse.md) then returns `#!cpp false`, and the rest +of the input is not read. The example finds the first `id` in a document without parsing the remainder: + +??? example "Example: stop parsing as soon as a value is found" + + ```cpp + --8<-- "examples/partial_input_sax_stop.cpp" + ``` + + Output: + + ``` + --8<-- "examples/partial_input_sax_stop.output" + ``` + +Note that `sax_parse` returns `#!cpp false` both when a handler stopped the parse and when the input is invalid, so +record the reason in the handler (here: `id`) if you need to tell the two apart. + +### Read parts of a large document with a view + +If the whole text is available and you only need to read some of it, a [`json_document`](../json_view.md) from +`` (added in version 3.13.0) avoids the `basic_json` tree altogether: it keeps the text, builds a flat index of the values, +and gives read-only [views](../../api/basic_json_view/index.md) of the parts you look at. Only what you +[`materialize()`](../../api/basic_json_view/materialize.md) becomes a `basic_json` value. See +[Zero-copy JSON views](../json_view.md) for details and for the lifetime rules. + +## See also + +- [Parsing](index.md) - overview of the parsing functions +- [JSON Lines](json_lines.md) - newline-delimited JSON +- [Parser callbacks](parser_callbacks.md) - discard values while parsing +- [SAX interface](sax_interface.md) - event-based parsing +- [Zero-copy JSON views](../json_view.md) - read parts of a large document without building a tree +- [Untrusted input](untrusted_input.md) - limits for input you do not control diff --git a/docs/mkdocs/docs/features/parsing/parse_exceptions.md b/docs/mkdocs/docs/features/parsing/parse_exceptions.md index f524b67d8..eb0ae5f58 100644 --- a/docs/mkdocs/docs/features/parsing/parse_exceptions.md +++ b/docs/mkdocs/docs/features/parsing/parse_exceptions.md @@ -21,6 +21,10 @@ catch (json::parse_error& ex) In case exceptions are undesired or not supported by the environment, there are different ways to proceed: +!!! tip + + [Working without exceptions](../no_exceptions.md) also covers accessing values and converting to your own types. + ## Switch off exceptions The [`parse()`](../../api/basic_json/parse.md) function accepts a `#!cpp bool` parameter `allow_exceptions` which @@ -81,3 +85,61 @@ The return value indicates whether the parsing should continue, so the function ``` --8<-- "examples/sax_no_exception.output" ``` + +## Where did the error occur? + +The message of a [`parse_error`](../../home/exceptions.md#jsonexceptionparse_error101) for JSON text contains the line +and column of the error: + +``` +[json.exception.parse_error.101] parse error at line 3, column 8: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal +``` + +The exception object itself only stores the **byte index** of the last read character in the member `byte` (the first +byte of the input has index 1). The same index is passed to `parse_error` in the [SAX interface](sax_interface.md). If +you need the line and column as numbers, for example to show them in an editor, compute them from the input: + +??? example "Example: compute line and column of a parse error" + + ```cpp + --8<-- "examples/parse_error_position.cpp" + ``` + + Output: + + ``` + --8<-- "examples/parse_error_position.output" + ``` + +Columns count bytes, so a line with multi-byte UTF-8 characters before the error may differ from the column your editor +shows. + +### Where is the value that caused a later error? + +A `parse_error` is raised while the text is read. Many mistakes show up later: the text is valid JSON, but a value has +the wrong type when you convert it (`type_error.302`), or a key is missing (`out_of_range.403`). The exception then says +what is wrong, but not where in the input the value was. Define +[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) before including the library to add the +byte range of the offending value to such messages (since version 3.12.0), and to query the range of any value with +[`start_pos()`](../../api/basic_json/start_pos.md) and [`end_pos()`](../../api/basic_json/end_pos.md): + +??? example "Example: locate a value with diagnostic positions" + + ```cpp + --8<-- "examples/parse_error_position_diagnostic.cpp" + ``` + + Output: + + ``` + --8<-- "examples/parse_error_position_diagnostic.output" + ``` + +Positions are only available for values created by [`parse`](../../api/basic_json/parse.md), they are not updated when a +value is changed, and the macro makes every value 16 bytes larger on 64-bit platforms, so use it where the cost is +acceptable, for example for configuration files. Note that `start_pos()` is a 0-based index into the input, whereas +`byte` of a `parse_error` starts at 1. + +For binary formats such as CBOR, there are no lines; `byte` is the position in the byte sequence. To find out how many +bytes a successfully parsed value used, see +[Several values in one stream](large_and_partial_input.md#several-values-in-one-stream). diff --git a/docs/mkdocs/docs/features/parsing/untrusted_input.md b/docs/mkdocs/docs/features/parsing/untrusted_input.md index 9c5fa4c9e..65f1bda25 100644 --- a/docs/mkdocs/docs/features/parsing/untrusted_input.md +++ b/docs/mkdocs/docs/features/parsing/untrusted_input.md @@ -26,9 +26,11 @@ several alternatives; see [Parsing and exceptions](parse_exceptions.md) for the [example](parse_exceptions.md#user-defined-sax-interface) that overrides it to print instead of throw. If exceptions are unavailable entirely (`-fno-exceptions`, or [`JSON_NOEXCEPTION`](../../api/macros/json_noexception.md) -defined), every `#!cpp throw` in the library becomes a call to `std::abort()` -- there is no way to recover from a -parse error of untrusted input in that configuration; see -[Switch off exceptions](../../home/exceptions.md#switch-off-exceptions) for the details and for overriding this with +defined), every `#!cpp throw` in the library becomes a call to `std::abort()`. The alternatives above do not throw, so +`parse()` with `allow_exceptions` set to `#!cpp false` (and likewise `accept()` and the `from_*` functions with +`allow_exceptions` set to `#!cpp false`) can still reject invalid input in that configuration; a plain `parse()` call +aborts on invalid input. See [Working without exceptions](../no_exceptions.md) and +[Switch off exceptions](../../home/exceptions.md#switch-off-exceptions) for the details and for overriding the abort with `JSON_THROW_USER`. ## Nesting depth @@ -81,6 +83,9 @@ Invalid UTF-8 is rejected while parsing, not just while serializing: - In JSON text, an ill-formed UTF-8 byte in a string is a [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) ("invalid string: ill-formed UTF-8 byte"). +- In UTF-16 or UTF-32 input, an ill-formed sequence is a + [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) as well: a lone surrogate, a high surrogate + that is not followed by its low surrogate, and a code point above `U+10FFFF` are all rejected. - In a binary format, a string that is not valid UTF-8 is a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113). diff --git a/docs/mkdocs/docs/features/performance.md b/docs/mkdocs/docs/features/performance.md index a15fbf38b..7ad7b187e 100644 --- a/docs/mkdocs/docs/features/performance.md +++ b/docs/mkdocs/docs/features/performance.md @@ -122,7 +122,9 @@ an adapter, since their template argument order does not match what `basic_json` [linear in its size](../api/basic_json/basic_json.md#complexity) for the copy constructor but [constant](../api/basic_json/basic_json.md#complexity) for the move constructor. The same applies to assigning a large `#!cpp std::string`, `#!cpp std::vector`, or other container into a value: pass it as `#!cpp std::move(x)` - rather than `x` whenever `x` is no longer needed afterwards. + rather than `x` whenever `x` is no longer needed afterwards. Since version 3.13.0, converting an rvalue container + (`#!cpp json j = std::move(vec);`) moves the elements instead of copying them; the source is then left in a valid but + unspecified state. - **Access without copying.** [`get()`](../api/basic_json/get.md) returns a copy of the stored value converted to `T`. When a reference or pointer to the value already stored inside the `basic_json` is enough, [`get_ref()`](../api/basic_json/get_ref.md) and [`get_ptr()`](../api/basic_json/get_ptr.md) access it directly: @@ -151,6 +153,29 @@ an adapter, since their template argument order does not match what `basic_json` ["reserving array capacity"](element_access/unchecked_access.md#performance-reserving-array-capacity) for the one-line recipe. +## Memory + +- **A value is small, its content is on the heap.** A `basic_json` is 16 bytes on a 64-bit platform (a type tag and a + union). Strings, arrays, objects, and binary values are allocated on the heap and owned by the value. Destroying the + value frees everything it owns. +- **Copies are deep.** After `#!cpp json b = a;`, changing `b` does not change `a`, and both own their memory. Copying a + large value therefore doubles its memory; see [Avoiding copies](#avoiding-copies). `#!cpp auto x = j["key"];` is a + copy, `#!cpp auto& x = j["key"];` is not. +- **A moved-from value is `null`.** After `#!cpp json c = std::move(a);` the value `a` is `#!json null` and owns nothing. +- **Reading does not require a tree.** If you only read the data, parse it into a [`json_document`](json_view.md) + (`#!cpp `, added in version 3.13.0) instead of a `basic_json`. It stores 16 bytes per value, does not copy floating-point + numbers or strings without escape sequences out of the text, and creates no `basic_json` value except for the parts + you [`materialize()`](../api/basic_json_view/materialize.md). A document that borrows the text needs the text to + outlive it; see [Zero-copy JSON views](json_view.md). +- **Containers keep their capacity.** `clear()` and `erase()` on an array remove the elements, but the underlying + `#!cpp std::vector` keeps its allocated capacity, like `#!cpp std::vector::clear` does. To give the memory back, + assign a new value (`#!cpp j = json::array();`) or destroy the value. +- **Freed memory does not always go back to the operating system.** The library returns memory to the allocator + (`#!cpp std::allocator` by default, or the [one you configured](types/template_parameters.md)). Whether the process + size shrinks then depends on the allocator and the platform, as for any C++ program. A process that parses a large + document and drops it is not leaking if its resident size stays high; run it under a tool such as + [Valgrind](https://valgrind.org) or the sanitizers to tell a leak from retained memory. + ## Serialization [`dump()`](../api/basic_json/dump.md) with the default `#!cpp indent = -1` selects "the most compact representation" diff --git a/docs/mkdocs/docs/features/type_recipes.md b/docs/mkdocs/docs/features/type_recipes.md new file mode 100644 index 000000000..314425dc6 --- /dev/null +++ b/docs/mkdocs/docs/features/type_recipes.md @@ -0,0 +1,124 @@ +# Recipes for Common Types + +The library converts the standard containers, strings, numbers, tuples, `std::optional`, and `std::filesystem::path` +(the last two since version 3.12.0, C++17) out of the box, and your own types once you write conversion functions or use +the macros (see [Arbitrary type conversions](arbitrary_types.md)). It deliberately does **not** convert smart pointers, +`std::variant`, `std::chrono` types, or class hierarchies, because there is more than one reasonable JSON +representation for each of them. This page shows a representation for each that you can copy and adapt. + +All recipes use a specialization of [`adl_serializer`](../api/adl_serializer/index.md), which you may provide for types +you do not own, or free `to_json`/`from_json` functions in the namespace of the type. Place them where they are +visible before any code that converts the type; see +[Why isn't my `to_json` found?](arbitrary_types.md#why-isnt-my-to_json-or-from_json-found). + +## Smart pointers + +A `std::shared_ptr` or `std::unique_ptr` is written as the value it points to, or as `#!json null` for an empty +pointer, and read back by allocating a new `T`: + +??? example "Example: serialize shared_ptr and unique_ptr" + + ```cpp + --8<-- "examples/type_recipes_smart_pointers.c++14.cpp" + ``` + + Output: + + ``` + --8<-- "examples/type_recipes_smart_pointers.c++14.output" + ``` + +With this, containers of smart pointers, pointers to `const`, and members of these types in your own structs convert as +usual. A pointer to a base class cannot be filled this way, because `T` would have +to be created as a derived type; see the next section. + +Reading creates a new object. Two `shared_ptr`s that pointed to the same object are written twice and read back as two +separate objects. + +## Class hierarchies + +!!! tip "Derived classes whose type is known at compile time" + + If you only need to serialize the data members of a derived class, and the code always knows the concrete type, you do + not need a type tag. The [`NLOHMANN_DEFINE_DERIVED_TYPE_*` macros](arbitrary_types.md#simplify-your-life-with-macros) + (since version 3.12.0) add the members of the derived class to those of its base class. This section is for the case + where the concrete type is only known at run time, such as a `std::unique_ptr` that holds a circle or a + rectangle. + +JSON has no notion of types, so a value in a hierarchy needs a **type tag** that says which derived class to create. +Write the tag in the object, and select the class from it when reading: + +??? example "Example: serialize a class hierarchy with a type tag" + + ```cpp + --8<-- "examples/type_recipes_class_hierarchy.c++14.cpp" + ``` + + Output: + + ``` + --8<-- "examples/type_recipes_class_hierarchy.c++14.output" + ``` + +A `#!cpp std::vector>` converts to an array of tagged objects and back, creating a `circle` +and a `rectangle`. The same works for `std::shared_ptr`. Remember the virtual destructor: without it, deleting a +derived object through the base pointer is undefined behavior. + +## `std::variant` + +A variant has the same problem: the alternative must be recorded. If the alternatives are your own types, write a type +tag next to their members, exactly as for a hierarchy: + +??? example "Example: serialize a std::variant with a type tag" + + ```cpp + --8<-- "examples/type_recipes_variant.c++17.cpp" + ``` + + Output: + + ``` + --8<-- "examples/type_recipes_variant.c++17.output" + ``` + +In the example, the functions are found +because `geo` is the namespace of the template arguments of `#!cpp std::variant`; for a variant of only +standard types (for example `#!cpp std::variant`) there is no such namespace, and you need an +`adl_serializer` specialization or a wrapper type of your own. + +If the alternatives are JSON-native (numbers, strings, ...), a variant such as `#!cpp std::variant` +can instead be written as the plain value, and read by testing the JSON type (`is_number_integer()`, `is_string()`) +to choose the alternative. + +## `std::chrono` + +A duration is written as an object with the keys `unit` and `value`, for example `{"unit": "ms", "value": 1500}`, so +the JSON value is self-describing. The serializer names the unit of the period (`ns`, `us`, `ms`, `s`, `min`, `h`; +extend the list for other periods), and the deserializer rejects a value whose unit differs from the requested +duration type instead of silently misreading it: + +??? example "Example: serialize durations and system_clock time points" + + ```cpp + --8<-- "examples/type_recipes_chrono.cpp" + ``` + + Output: + + ``` + --8<-- "examples/type_recipes_chrono.output" + ``` + +A `system_clock::time_point` with millisecond precision is written as the duration since the Unix epoch, that is, as an object with `unit` and `value`. +The time point serializer is limited to `system_clock`, +because the epoch of other clocks (for example `steady_clock`) has no meaning outside the running program. + +If you prefer a text representation such as ISO 8601 (`2023-11-14T22:13:20Z`), write the string with +`#!cpp std::format("{:%FT%TZ}", t)` and read it with `std::chrono::parse` (both C++20), or with a date library, in the +same two functions. + +## See also + +- [Arbitrary type conversions](arbitrary_types.md) - the conversion mechanism and macros +- [Why isn't my `to_json` found?](arbitrary_types.md#why-isnt-my-to_json-or-from_json-found) +- [Converting values](conversions.md) - `get`, implicit conversions, and `std::optional` diff --git a/docs/mkdocs/docs/features/types/index.md b/docs/mkdocs/docs/features/types/index.md index 67e97df6e..03e8c0bae 100644 --- a/docs/mkdocs/docs/features/types/index.md +++ b/docs/mkdocs/docs/features/types/index.md @@ -286,3 +286,12 @@ This implementation does exactly follow this approach, as it uses double precisi ### Storage Integer number values, unsigned integer number values, and floating-point number values are stored directly inside a `basic_json` type. + +## See also + +- [Number handling](number_handling.md) - how numbers are stored and converted +- [Template parameter requirements](template_parameters.md) +- [**basic_json**](../../api/basic_json/index.md) - the class template and its member types +- [**type**](../../api/basic_json/type.md) and [**value_t**](../../api/basic_json/value_t.md) - query the type of a value +- [**object_t**](../../api/basic_json/object_t.md), [**array_t**](../../api/basic_json/array_t.md), [**string_t**](../../api/basic_json/string_t.md), [**number_integer_t**](../../api/basic_json/number_integer_t.md), [**number_float_t**](../../api/basic_json/number_float_t.md) - the underlying types +- [Object order](../object_order.md) diff --git a/docs/mkdocs/docs/home/debugging.md b/docs/mkdocs/docs/home/debugging.md index 36aa5f39f..362afaa6b 100644 --- a/docs/mkdocs/docs/home/debugging.md +++ b/docs/mkdocs/docs/home/debugging.md @@ -1,7 +1,7 @@ # Debugging -This page collects the library's built-in debugger integrations and other debugging-related features. They are -not linked from a single place elsewhere in the docs, so are collected here. +This page collects the library's built-in debugger integrations and other debugging-related features. They are not +covered by any single page elsewhere in the docs, so they are collected here. ## Visual Studio (natvis) diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index 7658fb3cb..4b8f6edd9 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -39,6 +39,8 @@ classDiagram Exceptions are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md). In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `#!cpp throw`), `JSON_TRY_USER` (overriding `#!cpp try`), and `JSON_CATCH_USER` (overriding `#!cpp catch`). +See [Working without exceptions](../features/no_exceptions.md) for the functions that report errors without throwing. + Note that [`JSON_THROW_USER`](../api/macros/json_throw_user.md) should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. ??? example "Example: switch off exceptions and log errors before aborting" @@ -99,7 +101,8 @@ As this global context comes at the price of storing one additional pointer per Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type. -See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for more information. +See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for more information, and +[Debugging](debugging.md) for the debugger integrations of the library. ## Parse errors @@ -192,6 +195,34 @@ This error indicates a syntax error while deserializing a JSON text. The error m - 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 or a tool like [jq](https://jqlang.github.io/jq/). +??? info "Parsing a file fails at line 1, column 1 (unexpected end of input)" + + The messages `attempting to parse an empty input` (since version 3.11.3) and `unexpected end of input` (also the message of + earlier versions for an empty input) at the very start of the input mean + that the parser did not see a single character. The library only reads from the stream it is given: it does not + open files and cannot tell a missing file from an empty one. The usual causes are: + + - **The file was not opened.** A relative path is resolved against the *working directory of the running program*, + not against the source file or the project. IDEs and build tools often start the program in a build directory + (for example `build/` or `cmake-build-debug/`), so `"example.json"` may need to be `"../example.json"`. Use an + absolute path to rule this out. + - **The file is empty**, or a previous run truncated it (for example when it was opened for writing). + - **The stream was already read.** A stream whose content has been consumed (by printing it with + `std::cout << file.rdbuf()`, or by an earlier `accept`/`parse`/`>>`) is at its end. Parse it once, or rewind it + with `seekg(0)` after clearing the state with `clear()`. + + Check the stream before parsing it: + + ```cpp + std::ifstream f("example.json"); + if (!f.is_open()) + { + std::cerr << "cannot open example.json\n"; + return 1; + } + json data = json::parse(f); + ``` + ### json.exception.parse_error.102 JSON uses the `\uxxxx` format to describe Unicode characters. Code points above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. diff --git a/docs/mkdocs/docs/home/faq.md b/docs/mkdocs/docs/home/faq.md index 2381f4f7e..d52bdc006 100644 --- a/docs/mkdocs/docs/home/faq.md +++ b/docs/mkdocs/docs/home/faq.md @@ -58,6 +58,208 @@ json j{obj}; // -> {"key":"value"} (copy, not array) Without the macro (default behavior), `json j{obj}` creates `[{"key":"value"}]`. This opt-in macro fixes issue #5074 while preserving backwards compatibility for existing code. +## Getting started + +### Which file do I need? + +!!! question + + Do I have to clone the repository or build the library? + +No. The library is header-only: the single file [`json.hpp`](https://github.com/nlohmann/json/releases) from the +releases page is all you need, including the `_json` literals. Put it in a directory `nlohmann` on your include path. +Companion headers (`json_fwd.hpp`, `json_view.hpp`, `json_literals.hpp`) are only needed if you use them. See +[Integration](../integration/index.md#header-only). + +### Using CMake or a package manager + +!!! question + + How do I add the library with CMake, Conan, vcpkg, Homebrew, or another tool? + +See [CMake](../integration/cmake.md) for `find_package`, `add_subdirectory`, and `FetchContent`, and +[Package managers](../integration/package_managers.md) for the list of supported package managers. + +### The IDE cannot open the header + +!!! question + + The compiler finds `nlohmann/json.hpp`, but my IDE reports "cannot open source file". Why? + +The compiler's include path is set correctly, but the IDE's is not. Add the same directory to the include paths of the +IDE (or of its language server); the build itself is not affected. + +### Compile times + +!!! question + + Including the library slows down my build. What can I do? + +Include [`json_fwd.hpp`](../integration/compile_times.md#include-json_fwdhpp-in-headers) in headers and `json.hpp` only +in the source files that need it, instantiate `basic_json` once, and consider precompiled headers or +[C++20 modules](../features/modules.md). See [Compile times](../integration/compile_times.md). + +## Parsing + +### Parsing a file fails with "unexpected end of input" + +!!! question + + `json::parse(file)` throws `parse_error.101` at line 1, column 1 for a file that is valid JSON. Why? + +The parser did not see any character: the file was most likely not opened. A relative path is resolved against the +working directory of the running program, which is often the build directory of your IDE, not the source directory. +Check `is_open()` and try an absolute path. Other causes are an empty file and a stream that was already read. See +[`parse_error.101`](exceptions.md#jsonexceptionparse_error101). + +### Input that arrives in chunks + +!!! question + + My data comes from a socket in pieces. How do I parse it? + +The parser cannot be resumed. Frame the messages (one value per line, a length prefix) and parse each complete frame, or +buffer the chunks until the value is complete; a `parse_error` at the end of the buffer means the input ended +prematurely. See [Large, streamed, and partial input](../features/parsing/large_and_partial_input.md#input-arriving-in-chunks). + +### Several values in one stream + +!!! question + + How do I read concatenated JSON values or one value per line? + +Read newline-delimited input line by line ([JSON Lines](../features/parsing/json_lines.md)). For concatenated values, use +`operator>>` and check for the end of the stream before each read, as shown in +[Several values in one stream](../features/parsing/large_and_partial_input.md#several-values-in-one-stream). + +### Files that are too large for memory + +!!! question + + How do I process a huge JSON file? + +Handle and discard the records of a large array with a [parser callback](../features/parsing/parser_callbacks.md), stop +early with the [SAX interface](../features/parsing/sax_interface.md), or read parts of the text through a +[`json_document`](../features/json_view.md) (added in version 3.13.0). See +[Input too large for memory](../features/parsing/large_and_partial_input.md#input-too-large-for-memory). + +### Position of a parse error + +!!! question + + How do I get the line and column of a parse error? + +The message contains them. The exception stores the byte index in `byte`, from which you can compute both; see +[Where did the error occur?](../features/parsing/parse_exceptions.md#where-did-the-error-occur). To locate a value that +causes a *later* error, such as a `type_error`, use +[`JSON_DIAGNOSTIC_POSITIONS`](../features/parsing/parse_exceptions.md#where-is-the-value-that-caused-a-later-error). + +### Comments and trailing commas + +!!! question + + Can the parser read JSON with comments or trailing commas? + +Yes, when asked to: see [Comments](../features/comments.md) and [Trailing commas](../features/trailing_commas.md). Both +are rejected by default. + +## Accessing and creating values + +### Reading a value into a variable + +!!! question + + How do I get a C++ value out of a `json` value? + +Use [`get()`](../api/basic_json/get.md), for example `#!cpp int age = j["age"].get();`, or an implicit conversion. +`#!cpp auto x = j["age"];` is **not** a conversion: it copies the `json` value. See +[Converting values](../features/conversions.md). + +### Strings are printed with quotes + +!!! question + + `std::cout << j["name"]` prints `"Mary"` with quotes. How do I get the string? + +The value is a `json`, and printing it produces JSON text. Convert it first: `#!cpp j["name"].get()`. See +[Getting a string without quotes](../features/conversions.md#getting-values-out). + +### Testing whether a key exists + +!!! question + + How do I check if a key exists, without changing the object? + +Use `contains` or `find`. Do not test with the non-const `operator[]`: like `std::map::operator[]`, it inserts a `null` +value for a missing key. See [Does this key exist?](../features/element_access/index.md#does-this-key-exist). + +### Reading a value with a fallback + +!!! question + + How do I read a value that may be missing? + +Use [`value`](../features/element_access/default_value.md), which returns a default for a missing key. It still throws if +the key exists with a different type; see [Working without exceptions](../features/no_exceptions.md). + +### Iterating and nested values + +!!! question + + How do I loop over an array or object, or reach a nested value? + +See [Iterators](../features/iterators.md) and [`items()`](../api/basic_json/items.md) for loops, and +[JSON Pointer](../features/json_pointer.md) to address or test a nested value by path. + +### Unexpected arrays when creating values + +!!! question + + Why does `json j{x}` or `{{"a", 1}, {"b"}}` produce an array instead of what I expected? + +Braces create arrays and objects based on their content, and single-element braces create a one-element array. See +[Brace initialization yields arrays](#brace-initialization-yields-arrays) and the table in +[Creating values](../features/creating_values.md#what-does-this-produce). + +### Keeping the order of object keys + +!!! question + + The keys of my object come out sorted. How do I keep the insertion order? + +Use `ordered_json`, and call `ordered_json::parse` when reading. See [Object order](../features/object_order.md). + +## Converting your own types + +### Structs, enums, and optional fields + +!!! question + + How do I convert my own types to and from JSON? + +Write `to_json`/`from_json` functions or use the `NLOHMANN_DEFINE_TYPE_*` macros; see +[Arbitrary type conversions](../features/arbitrary_types.md). Enums can be mapped to strings +([Enum conversion](../features/enum_conversion.md)), and an empty `std::optional` can be omitted +([Omitting a field](../features/conversions.md#omitting-a-field-when-serializing-stdoptional)). + +### Smart pointers, variants, chrono, and class hierarchies + +!!! question + + Why can't I convert `std::shared_ptr`, `std::variant`, `std::chrono` types, or a pointer to a base class? + +There is more than one sensible JSON representation for them, so the library leaves the choice to you. +[Recipes for common types](../features/type_recipes.md) shows one for each. + +### My `to_json` is not found + +!!! question + + I wrote `to_json`/`from_json`, but the compiler does not find them. Why? + +Typical causes are a type alias of a standard type, a function in the wrong namespace, or a declaration that comes too +late. See [Why isn't my `to_json` found?](../features/arbitrary_types.md#why-isnt-my-to_json-or-from_json-found). + ## Limitations ### Relaxed parsing @@ -81,8 +283,9 @@ rejected by default, and the library never adds them when serializing. The library supports **Unicode input** as follows: -- Only **UTF-8** encoded input is supported, which is the default encoding for JSON, according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). -- `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers. +- **UTF-8** encoded input is supported, which is the default encoding for JSON, according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). +- `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively; `std::wstring` works as well and is interpreted as UTF-16 or UTF-32, depending on the width of the platform's `wchar_t`. These encodings cannot be read from a `FILE*` or a `std::istream`, which carry bytes only. +- Ill-formed UTF-16 or UTF-32 input is rejected: a lone surrogate, a high surrogate that is not followed by its low surrogate, and a code point above `U+10FFFF` all yield a parse error. The same holds for iterators over 2- or 4-byte characters. - Other encodings such as Latin-1 or ISO 8859-1 are **not** supported and will yield parse or serialization errors. - The library will not replace [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1). - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors. @@ -146,7 +349,7 @@ json::parse(std::string("\"") + '\0' + "\""); // throws parse_error.101: control Why are wide strings (e.g., `std::wstring`) dumped as arrays of numbers? -As described [above](#parse-errors-reading-non-ascii-characters), the library assumes UTF-8 as encoding. To store a wide string, you need to change the encoding. +As described [above](#parse-errors-reading-non-ascii-characters), the library stores its strings as UTF-8. To store a wide string as a string, convert it to a UTF-8 encoded `std::string` first, as in the example below. !!! example @@ -202,6 +405,36 @@ Not directly, but the companion project [json-schema-validator](https://github.c builds JSON Schema (draft 7; draft 4 in its older, now-superseded 1.x releases) validation on top of this library and is a common recommendation for this use case. +### Memory use + +!!! question + + Does the library leak memory? My process stays large after I free a `json` value. + +Destroying a value frees everything it owns. Freed memory is returned to the allocator, which does not necessarily give +it back to the operating system, so a high resident size alone is not a leak. Copies are deep, and `clear()` keeps the +capacity of an array. See [Memory](../features/performance.md#memory), and use Valgrind or the sanitizers to tell a leak +from retained memory. + +### Reproducible builds + +!!! question + + The header path ends up in my binary. How do I get path-independent builds? + +The runtime assertions embed `__FILE__`. Define `NDEBUG` (the default for release builds) or map the path prefix; see +[Notes for builds and distribution](../integration/index.md#notes-for-builds-and-distribution). + +### Slow debug builds + +!!! question + + Parsing a large file is very slow in my debug build. Can I compile the library with optimizations? + +The library is header-only, so there is no separate source file. Keep the code that uses `json` in its own source file and +compile that file with optimizations; see +[Notes for builds and distribution](../integration/index.md#notes-for-builds-and-distribution). + ## Exceptions ### Parsing without exceptions @@ -210,7 +443,8 @@ and is a common recommendation for this use case. Is it possible to indicate a parse error without throwing an exception? -Yes, see [Parsing and exceptions](../features/parsing/parse_exceptions.md). +Yes, see [Parsing and exceptions](../features/parsing/parse_exceptions.md) and +[Working without exceptions](../features/no_exceptions.md). ### Key name in exceptions diff --git a/docs/mkdocs/docs/home/releases.md b/docs/mkdocs/docs/home/releases.md deleted file mode 100644 index c0865270e..000000000 --- a/docs/mkdocs/docs/home/releases.md +++ /dev/null @@ -1,453 +0,0 @@ -# Releases - -This page summarizes the notable changes of every release and links to the relevant documentation. -The **complete release notes** — including all changes, the download files, and their checksums — are -published on the [GitHub releases page](https://github.com/nlohmann/json/releases). - -!!! info "Unreleased changes" - - This documentation is built from the `develop` branch and may describe changes that are not part of a release - yet. Their version numbers are followed by an unreleased badge. - -## Unreleased - -- Converting an rvalue container to JSON (`json j = std::move(vec);`, `json j = std::map{...};`) - now **moves** the elements instead of copying them (e.g., `std::vector` with 100,000 strings: 4.7 ms to - 2.3 ms). Two consequences: the source container is left in a valid but unspecified (moved-from) state, and if an - exception is thrown during the conversion, the source may be partially moved-from, so the strong exception guarantee - no longer holds for the source of an rvalue conversion (the JSON value itself is unaffected). Lvalues and `const` - objects are still copied. See [`basic_json`](../api/basic_json/basic_json.md). - -## v3.12.0 (2025-04-11) - -Fixes bugs found in 3.11.3 and adds several features. All changes are backward-compatible. - -- Adds diagnostic byte positions via [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md), - exposed through the new [`start_pos`](../api/basic_json/start_pos.md) and - [`end_pos`](../api/basic_json/end_pos.md) member functions. -- Makes the [conversion macros](../features/arbitrary_types.md#simplify-your-life-with-macros) - templated (so they also work with [`ordered_json`](../api/ordered_json.md)) and adds - [`NLOHMANN_DEFINE_DERIVED_TYPE`](../api/macros/nlohmann_define_derived_type.md) for derived classes. -- Adds `std::optional` support (C++17) and lets [`patch`](../api/basic_json/patch.md), - [`diff`](../api/basic_json/diff.md), and [`flatten`](../api/basic_json/flatten.md) work with - arbitrary string types. -- Extends the [binary formats](../features/binary_formats/index.md): - [BJData](../features/binary_formats/bjdata.md) draft 3 and unsigned 64-bit integers for - [BSON](../features/binary_formats/bson.md). -- Adds multidimensional C-array conversion and UTF-8 encoded `std::filesystem::path` conversions, and - 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). - -## v3.11.3 (2023-11-28) - -Adds features and fixes bugs found in 3.11.2. All changes are backward-compatible. - -- Adds a [custom base class](../api/basic_json/json_base_class_t.md) as a node customization point. -- Adds serialization-only [conversion macros](../features/macros.md) - (`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE` and `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE`) - and a clearer parse error for empty input. -- Adds [Bazel](../integration/package_managers.md#bazel) and - [Swift Package Manager](../integration/package_managers.md#swift-package-manager) build support. -- Fixes custom allocators, a memory leak in [`adl_serializer`](../api/adl_serializer/to_json.md)'s - `to_json`, initializer-list construction when `size_type` is not `int`, and many compiler warnings. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.3). - -## v3.11.2 (2022-08-12) - -Fixes bugs found in 3.11.1 and restructures the namespace. All changes are backward-compatible. - -- Fixes the [`value`](../api/basic_json/value.md) function (broken for strings, size types, and - `nullptr` in 3.11.0) and makes `json_fwd.hpp` self-contained. -- Restores using [`json_pointer`](../api/json_pointer/index.md) as a key in associative containers and - comparing it with strings. -- Restructures the inline [namespace](../features/namespace.md) and allows disabling the version - component, and avoids heap allocations in the [BJData](../features/binary_formats/bjdata.md) parser. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.2). - -## v3.11.1 (2022-08-01) - -Fixes a regression from 3.11.0. All changes are backward-compatible. - -- Restores the global [user-defined string literals](../api/macros/json_use_global_udls.md) - [`operator""_json`](../api/operator_literal_json.md) and - [`operator""_json_pointer`](../api/operator_literal_json_pointer.md), which 3.11.0 had moved into a - namespace by default. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.1). - -## v3.11.0 (2022-08-01) - -One of the largest releases ever. All changes are backward-compatible. - -- Allows `std::string_view` as object keys in [`at`](../api/basic_json/at.md), - [`operator[]`](../api/basic_json/operator%5B%5D.md), [`value`](../api/basic_json/value.md), - [`erase`](../api/basic_json/erase.md), [`find`](../api/basic_json/find.md), - [`contains`](../api/basic_json/contains.md), and [`count`](../api/basic_json/count.md). -- Adds the [BJData](../features/binary_formats/bjdata.md) binary format (the fifth supported format). -- Improves C++20 support, including [`operator<=>`](../api/basic_json/operator_spaceship.md) and - ``-compatible iterators. -- Adds a versioned, ABI-tagged inline [namespace](../features/namespace.md) - ([`NLOHMANN_JSON_NAMESPACE`](../api/macros/nlohmann_json_namespace.md)) and the option to move the - UDLs out of the global namespace ([`JSON_USE_GLOBAL_UDLS`](../api/macros/json_use_global_udls.md)). -- Adds [`patch_inplace`](../api/basic_json/patch_inplace.md), default values for the - [conversion macros](../features/arbitrary_types.md#simplify-your-life-with-macros), and an option to - disable enum serialization ([`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md)). - -This release introduced a UDL regression that was fixed in -[3.11.1](https://github.com/nlohmann/json/releases/tag/v3.11.1). -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.0). - -## v3.10.5 (2022-01-03) - -Bug-fix release. All changes are backward-compatible. - -- Guards the `std::filesystem` conversions behind compiler-support checks - ([`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md)), which can be set to `0` to disable - them altogether. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.5). - -## v3.10.4 (2021-10-16) - -Fixes regressions introduced in 3.10.0. All changes are backward-compatible. - -- Fixes the `std::filesystem::path` conversion (which could trigger a stack overflow and broke - compilation on Windows). -- Fixes compilation for types with an explicit defaulted constructor and for code relying on the - return values of `std::find` and `std::remove`. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.4). - -## v3.10.3 (2021-10-08) - -Fixes more regressions from 3.10.0. All changes are backward-compatible. - -- Fixes [extended-diagnostics](../api/macros/json_diagnostics.md) assertions triggered by - [`update`](../api/basic_json/update.md) and by inserting into arrays. -- Supports custom allocators when writing binary formats into a `std::vector`, and allows conversion - from types that only provide `begin()`/`end()`. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.3). - -## v3.10.2 (2021-08-26) - -Re-release of 3.10.1, whose Git tag pointed at the wrong commit due to a bug in the release script. -All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.2). - -## v3.10.1 (2021-08-24) - -Fixes a regression from 3.10.0. All changes are backward-compatible. - -- Fixes an [extended-diagnostics](../api/macros/json_diagnostics.md) assertion triggered when used - with [`ordered_json`](../api/ordered_json.md), and hardens the GDB pretty-printer. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.1). - -## v3.10.0 (2021-08-17) - -Feature release. All changes are backward-compatible. - -- Adds [extended diagnostic messages](../api/macros/json_diagnostics.md) - ([`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md)) that prepend a JSON pointer to exception - messages to pinpoint the offending value. -- Adds a GDB pretty-printer and a [`cbor_tag_handler_t`](../api/basic_json/cbor_tag_handler_t.md) - `store` option to keep CBOR tags as binary subtypes. -- Supports containers with non-default-constructible types and parsing from `std::byte`. -- Adds [`JSON_NO_IO`](../api/macros/json_no_io.md) to exclude the I/O headers and the - [`JSON_HAS_CPP_*`](../api/macros/json_has_cpp_11.md) macros to override the detected C++ standard. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.0). - -## v3.9.1 (2020-08-06) - -Fixes two regressions from 3.9.0. All changes are backward-compatible. - -- Accepts consecutive [comments](../features/comments.md) and completes the - [`ordered_json`](../api/ordered_json.md) interface (e.g. `ordered_json::parse`). - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.9.1). - -## v3.9.0 (2020-07-27) - -Feature release adding four long-requested features. All changes are backward-compatible. - -- Optional [comment](../features/comments.md) parsing in [`parse`](../api/basic_json/parse.md) via the - `ignore_comments` parameter. -- [`ordered_json`](../api/ordered_json.md) to preserve the [insertion order](../features/object_order.md) - of object keys. -- An option to switch off [implicit conversions](../api/macros/json_use_implicit_conversions.md). -- The [`NLOHMANN_DEFINE_TYPE_*`](../features/arbitrary_types.md#simplify-your-life-with-macros) - convenience macros, plus high-precision-number support for - [UBJSON](../features/binary_formats/ubjson.md) and CBOR tag handling. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.9.0). - -## v3.8.0 (2020-06-14) - -Feature release. All changes are backward-compatible. - -- Introduces a [binary value](../features/binary_values.md) type that is read from and written to - [CBOR](../features/binary_formats/cbor.md), [BSON](../features/binary_formats/bson.md), and - [MessagePack](../features/binary_formats/messagepack.md), and can be shared between formats. -- Generalizes the input adapters to read from any `LegacyInputIterator` container (3–10 % faster - parsing). -- Fixes [`contains`](../api/basic_json/contains.md) for JSON pointers and makes the binary - [`from_cbor`](../api/basic_json/from_cbor.md)/[`from_msgpack`](../api/basic_json/from_msgpack.md)/etc. - functions respect `allow_exceptions`. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.8.0). - -## v3.7.3 (2019-11-17) - -Fixes a regression from 3.7.2 that could yield quadratic complexity in destructor calls. All changes -are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.3). - -## v3.7.2 (2019-11-10) - -Fixes a stack overflow for deeply nested input by making the destructor iterative; parsing is now -bounded only by available memory. All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.2). - -## v3.7.1 (2019-11-06) - -Bug-fix release. All changes are backward-compatible. - -- Fixes a segmentation fault when serializing the `std::int64_t` minimum value and fixes - [`contains`](../api/basic_json/contains.md) for JSON pointers. -- Allows [`items`](../api/basic_json/items.md) with a custom string type and makes `json_pointer::back` - `const`. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.1). - -## v3.7.0 (2019-07-28) - -Convenience features and house-keeping. All changes are backward-compatible. - -- Adds a [`contains`](../api/basic_json/contains.md) overload that checks a JSON pointer without - throwing, a generic `to_string`, and a return value for - [`emplace_back`](../api/basic_json/emplace_back.md). - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.0). - -## v3.6.1 (2019-03-20) - -Fixes a regression (GCC 7/8 compilation) and a `` build error introduced in 3.6.0. All -changes are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v3.6.1). - -## v3.6.0 (2019-03-20) - -Feature release. All changes are backward-compatible. - -- Reworks the [JSON pointer](../features/json_pointer.md) interface (`operator/`, `push_back`, - `parent_pointer`, …). -- Adds a [`contains`](../api/basic_json/contains.md) function to test for an object key and greatly - improves the performance of integer serialization. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.6.0). - -## v3.5.0 (2018-12-22) - -Feature release. All changes are backward-compatible. - -- Adds structured-binding support via the [`items`](../api/basic_json/items.md) function and reading - from `FILE*` in the [`parse`](../api/basic_json/parse.md) function. -- Fixes the `eofbit` handling on input streams and a bug in the BSON SAX parser. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.5.0). - -## v3.4.0 (2018-10-30) - -Feature release. All changes are backward-compatible. - -- Adds [BSON](../features/binary_formats/bson.md) read/write support. -- Adds configurable Unicode error handlers to [`dump`](../api/basic_json/dump.md) (throw, replace with - U+FFFD, or ignore) and the - [`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md) macro for - [enum conversion](../features/enum_conversion.md). -- Improves parse-error messages with line/column positions and context. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.4.0). - -## v3.3.0 (2018-10-05) - -Feature release. All changes are backward-compatible. - -- Adds GCC 4.8 support, the [`get_to`](../api/basic_json/get_to.md) function, and an overhauled and - documented [CMake](../integration/cmake.md) integration. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.3.0). - -## v3.2.0 (2018-08-20) - -Feature release. All changes are backward-compatible. - -- Adds a [SAX interface](../features/parsing/sax_interface.md) and a non-recursive parser. -- Adds parsing from wide-string types (`std::wstring`, `std::u16string`, `std::u32string`) and - `std::string_view` (C++17), and round-tripping of `std::map`/`std::unordered_map` with non-string - keys. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.2.0). - -## v3.1.2 (2018-03-14) - -Bug-fix release. All changes are backward-compatible. - -- Fixes a memory leak in the parser callback and adds user-defined string-type support to the parser - and serializer. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.1.2). - -## v3.1.1 (2018-02-13) - -Bug-fix release. All changes are backward-compatible. - -- Fixes parsing of indefinite-length CBOR strings, a user-defined conversion to vector types, and - overflow detection for UBJSON containers. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.1.1). - -## v3.1.0 (2018-02-01) - -Feature release. All changes are backward-compatible. - -- Adds [UBJSON](../features/binary_formats/ubjson.md) read/write support and - [JSON Merge Patch](../features/merge_patch.md) via [`merge_patch`](../api/basic_json/merge_patch.md). -- Switches to the Grisu2 algorithm for short, round-trippable floating-point output, and splits the - header into [multiple files](../integration/index.md) with a forward-declaration header. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.1.0). - -## v3.0.1 (2017-12-29) - -Fixes small issues in the [JSON Pointer](../features/json_pointer.md) and -[JSON Patch](../features/json_patch.md) implementations (invalid "copy" targets and non-integer array -indices). All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.0.1). - -## v3.0.0 (2017-12-17) - -First 3.x release — a major release with breaking changes (see the -[migration guide](../integration/migration_guide.md)). - -- Introduces user-defined [exceptions](exceptions.md) (`json::exception` and subtypes, each with an - identifier). -- Adds a non-throwing [`accept`](../api/basic_json/accept.md) function and an `allow_exceptions` flag - for [`parse`](../api/basic_json/parse.md), and an [`update`](../api/basic_json/update.md) function to - merge objects. -- Adds streaming for CBOR and MessagePack and allows storing NaN/infinity. -- Non-UTF-8 strings now throw on serialization, and the iterator category changed to bidirectional. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.0.0). - -## v2.1.1 (2017-02-25) - -Bug-fix release. All changes are backward-compatible. - -- Makes number parsing and serialization locale-independent with correct floating-point - round-tripping; released files are now GPG-signed. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.1.1). - -## v2.1.0 (2017-01-28) - -Feature release. All changes are backward-compatible. - -- Adds conversions from and to [arbitrary user-defined types](../features/arbitrary_types.md) via - `to_json`/`from_json`, the [`meta`](../api/basic_json/meta.md) function, and the option to switch off - exceptions ([`JSON_NOEXCEPTION`](../api/macros/json_noexception.md)). - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.1.0). - -## v2.0.10 (2017-01-02) - -Fixes several security-relevant bugs in the CBOR and MessagePack parsers found by OSS-Fuzz. All -changes are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.10). - -## v2.0.9 (2016-12-16) - -Adds the [CBOR](../features/binary_formats/cbor.md) and -[MessagePack](../features/binary_formats/messagepack.md) binary formats. All changes are -backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.9). - -## v2.0.8 (2016-12-02) - -Adds the [`emplace`](../api/basic_json/emplace.md) and -[`emplace_back`](../api/basic_json/emplace_back.md) functions and improves parsing and serialization -performance. All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.8). - -## v2.0.7 (2016-11-02) - -Fixes several parser bugs found through the "Parsing JSON is a Minefield" study (short files, encoding -detection, surrogate pairs). All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.7). - -## v2.0.6 (2016-10-15) - -Fixes [`operator[]`](../api/basic_json/operator%5B%5D.md) for [JSON pointers](../features/json_pointer.md) -so that it creates missing values like the other overloads. All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.6). - -## v2.0.5 (2016-09-14) - -Fixes a remaining stream end-of-file detection bug in the parser. All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.5). - -## v2.0.4 (2016-09-11) - -Fixes stream end-of-file detection in the parser. All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.4). - -## v2.0.3 (2016-08-31) - -Generalizes the parser to accept any contiguous sequence of one-byte elements and deprecates the -input-stream constructor in favor of the [`parse`](../api/basic_json/parse.md) function. All changes -are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.3). - -## v2.0.2 (2016-07-31) - -Overhauls the parser (now rejecting unescaped control characters), tightens the class invariants, and -cleans up the code. All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.2). - -## v2.0.1 (2016-06-28) - -Fixes a performance regression in the [`dump`](../api/basic_json/dump.md) function by adjusting the -stream locale once per serialization. All changes are backward-compatible. -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.1). - -## v2.0.0 (2016-06-24) - -Feature release with a minor (potentially non-backward-compatible) API change from added `noexcept` -and `constexpr` specifiers. - -- Adds [JSON Pointer](../features/json_pointer.md) support in [`at`](../api/basic_json/at.md) and - [`operator[]`](../api/basic_json/operator%5B%5D.md), plus [`flatten`](../api/basic_json/flatten.md) - and [`unflatten`](../api/basic_json/unflatten.md). -- Adds [JSON Patch](../features/json_patch.md) via [`diff`](../api/basic_json/diff.md) and - [`patch`](../api/basic_json/patch.md), unsigned 64-bit integer support, and locale-independent - serialization. - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.0). - -## v1.1.0 (2016-01-24) - -Bug-fix and feature release. All changes are backward-compatible. - -- Improves floating-point round-tripping, adds a `get_ref` accessor for stored values, and introduces - runtime [assertions](../features/assertions.md). - -[Full release notes](https://github.com/nlohmann/json/releases/tag/v1.1.0). - -## v1.0.0 (2015-12-28) - -First official release. [Full release notes](https://github.com/nlohmann/json/releases/tag/v1.0.0). - -## See also - -- [Migration Guide](../integration/migration_guide.md) — how to future-proof your code for the next - major version and replace deprecated functions. diff --git a/docs/mkdocs/docs/home/releases/index.md b/docs/mkdocs/docs/home/releases/index.md new file mode 100644 index 000000000..e0dbca2d5 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/index.md @@ -0,0 +1,155 @@ +# Releases + +This section summarizes the notable changes of every release and links to the relevant documentation. +The **complete release notes** — including all changes, the download files, and their checksums — are +published on the [GitHub releases page](https://github.com/nlohmann/json/releases). + +!!! info "Unreleased changes" + + This documentation is built from the `develop` branch and may describe changes that are not part of a release + yet. Their version numbers are followed by an unreleased badge. + +Every release has its own page with a summary of its notable changes and an overview of the API that was added or +changed in it. The API overview is generated from the "Version history" section of the +[API pages](../../api/basic_json/index.md); it is not a change log, but helps to see whether a feature is available in +the version you use. + +## Unreleased + +| Release | Date | Summary | +|---------|------|---------| +| [Unreleased](unreleased.md) | | Changes on the `develop` branch that are not part of a release yet. | + +## Version 3.12.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.12.0](v3.12.0.md) | 2025-04-11 | Fixes bugs found in 3.11.3 and adds several features. No public API was removed or renamed. | + +## Version 3.11.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.11.3](v3.11.3.md) | 2023-11-28 | Adds features and fixes bugs found in 3.11.2. All changes are backward-compatible. | +| [v3.11.2](v3.11.2.md) | 2022-08-12 | Fixes bugs found in 3.11.1 and restructures the namespace. All changes are backward-compatible. | +| [v3.11.1](v3.11.1.md) | 2022-08-01 | Fixes a regression from 3.11.0. All changes are backward-compatible. | +| [v3.11.0](v3.11.0.md) | 2022-08-01 | One of the largest releases ever. All changes are backward-compatible. | + +## Version 3.10.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.10.5](v3.10.5.md) | 2022-01-03 | Bug-fix release. All changes are backward-compatible. | +| [v3.10.4](v3.10.4.md) | 2021-10-16 | Fixes regressions introduced in 3.10.0. All changes are backward-compatible. | +| [v3.10.3](v3.10.3.md) | 2021-10-08 | Fixes more regressions from 3.10.0. All changes are backward-compatible. | +| [v3.10.2](v3.10.2.md) | 2021-08-26 | Re-release of 3.10.1, whose Git tag pointed at the wrong commit due to a bug in the release script. All changes are backward-compatible. | +| [v3.10.1](v3.10.1.md) | 2021-08-24 | Fixes a regression from 3.10.0. All changes are backward-compatible. | +| [v3.10.0](v3.10.0.md) | 2021-08-17 | Feature release. All changes are backward-compatible. | + +## Version 3.9.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.9.1](v3.9.1.md) | 2020-08-06 | Fixes two regressions from 3.9.0. All changes are backward-compatible. | +| [v3.9.0](v3.9.0.md) | 2020-07-27 | Feature release adding four long-requested features. All changes are backward-compatible. | + +## Version 3.8.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.8.0](v3.8.0.md) | 2020-06-14 | Feature release. All changes are backward-compatible. | + +## Version 3.7.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.7.3](v3.7.3.md) | 2019-11-17 | Fixes a regression from 3.7.2 that could yield quadratic complexity in destructor calls. All changes are backward-compatible. | +| [v3.7.2](v3.7.2.md) | 2019-11-10 | Fixes a stack overflow for deeply nested input by making the destructor iterative; parsing of JSON text is now bounded only by available memory (the binary-format parsers still recurse). All changes are backward-compatible. | +| [v3.7.1](v3.7.1.md) | 2019-11-06 | Bug-fix release. All changes are backward-compatible. | +| [v3.7.0](v3.7.0.md) | 2019-07-28 | Convenience features and house-keeping. All changes are backward-compatible. | + +## Version 3.6.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.6.1](v3.6.1.md) | 2019-03-20 | Fixes a regression (GCC 7/8 compilation) and a `` build error introduced in 3.6.0. All changes are backward-compatible. | +| [v3.6.0](v3.6.0.md) | 2019-03-20 | Feature release. All changes are backward-compatible. | + +## Version 3.5.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.5.0](v3.5.0.md) | 2018-12-22 | Feature release. All changes are backward-compatible. | + +## Version 3.4.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.4.0](v3.4.0.md) | 2018-10-30 | Feature release. All changes are backward-compatible. | + +## Version 3.3.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.3.0](v3.3.0.md) | 2018-10-05 | Feature release. All changes are backward-compatible. | + +## Version 3.2.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.2.0](v3.2.0.md) | 2018-08-20 | Feature release. All changes are backward-compatible. | + +## Version 3.1.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.1.2](v3.1.2.md) | 2018-03-14 | Bug-fix release. All changes are backward-compatible. | +| [v3.1.1](v3.1.1.md) | 2018-02-13 | Bug-fix release. All changes are backward-compatible. | +| [v3.1.0](v3.1.0.md) | 2018-02-01 | Feature release. All changes are backward-compatible. | + +## Version 3.0.x + +| Release | Date | Summary | +|---------|------|---------| +| [v3.0.1](v3.0.1.md) | 2017-12-29 | Fixes small issues in the [JSON Pointer](../../features/json_pointer.md) and [JSON Patch](../../features/json_patch.md) implementations (invalid "copy" targets and non-integer array indices). All changes are backward-compatible. | +| [v3.0.0](v3.0.0.md) | 2017-12-17 | First 3.x release — a major release with breaking changes (see the [migration guide](../../integration/migration_guide.md)). | + +## Version 2.1.x + +| Release | Date | Summary | +|---------|------|---------| +| [v2.1.1](v2.1.1.md) | 2017-02-25 | Bug-fix release. All changes are backward-compatible. | +| [v2.1.0](v2.1.0.md) | 2017-01-28 | Feature release. All changes are backward-compatible. | + +## Version 2.0.x + +| Release | Date | Summary | +|---------|------|---------| +| [v2.0.10](v2.0.10.md) | 2017-01-02 | Fixes several security-relevant bugs in the CBOR and MessagePack parsers found by OSS-Fuzz. All changes are backward-compatible. | +| [v2.0.9](v2.0.9.md) | 2016-12-16 | Adds the [CBOR](../../features/binary_formats/cbor.md) and [MessagePack](../../features/binary_formats/messagepack.md) binary formats. All changes are backward-compatible. | +| [v2.0.8](v2.0.8.md) | 2016-12-02 | Adds the [`emplace`](../../api/basic_json/emplace.md) and [`emplace_back`](../../api/basic_json/emplace_back.md) functions and improves parsing and serialization performance. All changes are backward-compatible. | +| [v2.0.7](v2.0.7.md) | 2016-11-02 | Fixes several parser bugs found through the "Parsing JSON is a Minefield" study (short files, encoding detection, surrogate pairs). All changes are backward-compatible. | +| [v2.0.6](v2.0.6.md) | 2016-10-15 | Fixes [`operator[]`](../../api/basic_json/operator%5B%5D.md) for [JSON pointers](../../features/json_pointer.md) so that it creates missing values like the other overloads. All changes are backward-compatible. | +| [v2.0.5](v2.0.5.md) | 2016-09-14 | Fixes a remaining stream end-of-file detection bug in the parser. All changes are backward-compatible. | +| [v2.0.4](v2.0.4.md) | 2016-09-11 | Fixes stream end-of-file detection in the parser. All changes are backward-compatible. | +| [v2.0.3](v2.0.3.md) | 2016-08-31 | Generalizes the parser to accept any contiguous sequence of one-byte elements and deprecates the input-stream constructor in favor of the [`parse`](../../api/basic_json/parse.md) function. All changes are backward-compatible. | +| [v2.0.2](v2.0.2.md) | 2016-07-31 | Overhauls the parser (now rejecting unescaped control characters), tightens the class invariants, and cleans up the code. All changes are backward-compatible. | +| [v2.0.1](v2.0.1.md) | 2016-06-28 | Fixes a performance regression in the [`dump`](../../api/basic_json/dump.md) function by adjusting the stream locale once per serialization. All changes are backward-compatible. | +| [v2.0.0](v2.0.0.md) | 2016-06-24 | Feature release with a minor (potentially non-backward-compatible) API change from added `noexcept` and `constexpr` specifiers. | + +## Version 1.1.x + +| Release | Date | Summary | +|---------|------|---------| +| [v1.1.0](v1.1.0.md) | 2016-01-24 | Bug-fix and feature release. All changes are backward-compatible. | + +## Version 1.0.x + +| Release | Date | Summary | +|---------|------|---------| +| [v1.0.0](v1.0.0.md) | 2015-12-28 | First official release. | + +## See also + +- [Migration Guide](../../integration/migration_guide.md) — how to future-proof your code for the next + major version and replace deprecated functions. diff --git a/docs/mkdocs/docs/home/releases/unreleased.md b/docs/mkdocs/docs/home/releases/unreleased.md new file mode 100644 index 000000000..b27da9748 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/unreleased.md @@ -0,0 +1,14 @@ +# Unreleased + +These changes are part of the `develop` branch, but not of a release yet. + +- Converting an rvalue container to JSON (`json j = std::move(vec);`, `json j = std::map{...};`) + now **moves** the elements instead of copying them (e.g., `std::vector` with 100,000 strings: 4.7 ms to + 2.3 ms). Two consequences: the source container is left in a valid but unspecified (moved-from) state, and if an + exception is thrown during the conversion, the source may be partially moved-from, so the strong exception guarantee + no longer holds for the source of an rvalue conversion (the JSON value itself is unaffected). Lvalues and `const` + objects are still copied. See [`basic_json`](../../api/basic_json/basic_json.md). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v1.0.0.md b/docs/mkdocs/docs/home/releases/v1.0.0.md new file mode 100644 index 000000000..dde38ca57 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v1.0.0.md @@ -0,0 +1,9 @@ +# v1.0.0 + +Released on 2015-12-28. + +First official release. [Full release notes](https://github.com/nlohmann/json/releases/tag/v1.0.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v1.1.0.md b/docs/mkdocs/docs/home/releases/v1.1.0.md new file mode 100644 index 000000000..1bdff4b59 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v1.1.0.md @@ -0,0 +1,14 @@ +# v1.1.0 + +Released on 2016-01-24. + +Bug-fix and feature release. All changes are backward-compatible. + +- Improves floating-point round-tripping, adds a `get_ref` accessor for stored values, and introduces + runtime [assertions](../../features/assertions.md). + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v1.1.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.0.md b/docs/mkdocs/docs/home/releases/v2.0.0.md new file mode 100644 index 000000000..540593592 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.0.md @@ -0,0 +1,19 @@ +# v2.0.0 + +Released on 2016-06-24. + +Feature release with a minor (potentially non-backward-compatible) API change from added `noexcept` +and `constexpr` specifiers. + +- Adds [JSON Pointer](../../features/json_pointer.md) support in [`at`](../../api/basic_json/at.md) and + [`operator[]`](../../api/basic_json/operator%5B%5D.md), plus [`flatten`](../../api/basic_json/flatten.md) + and [`unflatten`](../../api/basic_json/unflatten.md). +- Adds [JSON Patch](../../features/json_patch.md) via [`diff`](../../api/basic_json/diff.md) and + [`patch`](../../api/basic_json/patch.md), unsigned 64-bit integer support, and locale-independent + serialization. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.1.md b/docs/mkdocs/docs/home/releases/v2.0.1.md new file mode 100644 index 000000000..8d2820f14 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.1.md @@ -0,0 +1,11 @@ +# v2.0.1 + +Released on 2016-06-28. + +Fixes a performance regression in the [`dump`](../../api/basic_json/dump.md) function by adjusting the +stream locale once per serialization. All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.10.md b/docs/mkdocs/docs/home/releases/v2.0.10.md new file mode 100644 index 000000000..56e390f44 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.10.md @@ -0,0 +1,10 @@ +# v2.0.10 + +Released on 2017-01-02. + +Fixes several security-relevant bugs in the CBOR and MessagePack parsers found by OSS-Fuzz. All +changes are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.10). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.2.md b/docs/mkdocs/docs/home/releases/v2.0.2.md new file mode 100644 index 000000000..4824db4a9 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.2.md @@ -0,0 +1,11 @@ +# v2.0.2 + +Released on 2016-07-31. + +Overhauls the parser (now rejecting unescaped control characters), tightens the class invariants, and +cleans up the code. All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.2). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.3.md b/docs/mkdocs/docs/home/releases/v2.0.3.md new file mode 100644 index 000000000..66182cb1d --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.3.md @@ -0,0 +1,11 @@ +# v2.0.3 + +Released on 2016-08-31. + +Generalizes the parser to accept any contiguous sequence of one-byte elements and deprecates the +input-stream constructor in favor of the [`parse`](../../api/basic_json/parse.md) function. All changes +are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.3). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.4.md b/docs/mkdocs/docs/home/releases/v2.0.4.md new file mode 100644 index 000000000..b7cebb942 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.4.md @@ -0,0 +1,10 @@ +# v2.0.4 + +Released on 2016-09-11. + +Fixes stream end-of-file detection in the parser. All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.4). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.5.md b/docs/mkdocs/docs/home/releases/v2.0.5.md new file mode 100644 index 000000000..2f383efc9 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.5.md @@ -0,0 +1,10 @@ +# v2.0.5 + +Released on 2016-09-14. + +Fixes a remaining stream end-of-file detection bug in the parser. All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.5). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.6.md b/docs/mkdocs/docs/home/releases/v2.0.6.md new file mode 100644 index 000000000..cfee027fb --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.6.md @@ -0,0 +1,11 @@ +# v2.0.6 + +Released on 2016-10-15. + +Fixes [`operator[]`](../../api/basic_json/operator%5B%5D.md) for [JSON pointers](../../features/json_pointer.md) +so that it creates missing values like the other overloads. All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.6). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.7.md b/docs/mkdocs/docs/home/releases/v2.0.7.md new file mode 100644 index 000000000..3d8069ae6 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.7.md @@ -0,0 +1,11 @@ +# v2.0.7 + +Released on 2016-11-02. + +Fixes several parser bugs found through the "Parsing JSON is a Minefield" study (short files, encoding +detection, surrogate pairs). All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.7). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.8.md b/docs/mkdocs/docs/home/releases/v2.0.8.md new file mode 100644 index 000000000..177e335a5 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.8.md @@ -0,0 +1,12 @@ +# v2.0.8 + +Released on 2016-12-02. + +Adds the [`emplace`](../../api/basic_json/emplace.md) and +[`emplace_back`](../../api/basic_json/emplace_back.md) functions and improves parsing and serialization +performance. All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.8). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.0.9.md b/docs/mkdocs/docs/home/releases/v2.0.9.md new file mode 100644 index 000000000..2587c790e --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.0.9.md @@ -0,0 +1,11 @@ +# v2.0.9 + +Released on 2016-12-16. + +Adds the [CBOR](../../features/binary_formats/cbor.md) and +[MessagePack](../../features/binary_formats/messagepack.md) binary formats. All changes are +backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v2.0.9). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.1.0.md b/docs/mkdocs/docs/home/releases/v2.1.0.md new file mode 100644 index 000000000..3e42cbd32 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.1.0.md @@ -0,0 +1,15 @@ +# v2.1.0 + +Released on 2017-01-28. + +Feature release. All changes are backward-compatible. + +- Adds conversions from and to [arbitrary user-defined types](../../features/arbitrary_types.md) via + `to_json`/`from_json`, the [`meta`](../../api/basic_json/meta.md) function, and the option to switch off + exceptions ([`JSON_NOEXCEPTION`](../../api/macros/json_noexception.md)). + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.1.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v2.1.1.md b/docs/mkdocs/docs/home/releases/v2.1.1.md new file mode 100644 index 000000000..7167dcd16 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v2.1.1.md @@ -0,0 +1,14 @@ +# v2.1.1 + +Released on 2017-02-25. + +Bug-fix release. All changes are backward-compatible. + +- Makes number parsing and serialization locale-independent with correct floating-point + round-tripping; commits and released files are GPG-signed. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v2.1.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.0.0.md b/docs/mkdocs/docs/home/releases/v3.0.0.md new file mode 100644 index 000000000..cea928ee1 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.0.0.md @@ -0,0 +1,20 @@ +# v3.0.0 + +Released on 2017-12-17. + +First 3.x release — a major release with breaking changes (see the +[migration guide](../../integration/migration_guide.md)). + +- Introduces user-defined [exceptions](../exceptions.md) (`json::exception` and subtypes, each with an + identifier). +- Adds a non-throwing [`accept`](../../api/basic_json/accept.md) function and an `allow_exceptions` flag + for [`parse`](../../api/basic_json/parse.md), and an [`update`](../../api/basic_json/update.md) function to + merge objects. +- Adds streaming for CBOR and MessagePack and allows storing NaN/infinity. +- Non-UTF-8 strings now throw on serialization, and the iterator category changed to bidirectional. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.0.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.0.1.md b/docs/mkdocs/docs/home/releases/v3.0.1.md new file mode 100644 index 000000000..95c6141cf --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.0.1.md @@ -0,0 +1,12 @@ +# v3.0.1 + +Released on 2017-12-29. + +Fixes small issues in the [JSON Pointer](../../features/json_pointer.md) and +[JSON Patch](../../features/json_patch.md) implementations (invalid "copy" targets and non-integer array +indices). All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.0.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.1.0.md b/docs/mkdocs/docs/home/releases/v3.1.0.md new file mode 100644 index 000000000..31428a66f --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.1.0.md @@ -0,0 +1,16 @@ +# v3.1.0 + +Released on 2018-02-01. + +Feature release. All changes are backward-compatible. + +- Adds [UBJSON](../../features/binary_formats/ubjson.md) read/write support and + [JSON Merge Patch](../../features/merge_patch.md) via [`merge_patch`](../../api/basic_json/merge_patch.md). +- Switches to the Grisu2 algorithm for short, round-trippable floating-point output, and splits the + header into [multiple files](../../integration/index.md) with a forward-declaration header. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.1.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.1.1.md b/docs/mkdocs/docs/home/releases/v3.1.1.md new file mode 100644 index 000000000..b7c6aca1f --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.1.1.md @@ -0,0 +1,14 @@ +# v3.1.1 + +Released on 2018-02-13. + +Bug-fix release. All changes are backward-compatible. + +- Fixes parsing of indefinite-length CBOR strings, a user-defined conversion to vector types, and + overflow detection for UBJSON containers. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.1.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.1.2.md b/docs/mkdocs/docs/home/releases/v3.1.2.md new file mode 100644 index 000000000..30917e031 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.1.2.md @@ -0,0 +1,14 @@ +# v3.1.2 + +Released on 2018-03-14. + +Bug-fix release. All changes are backward-compatible. + +- Fixes a memory leak in the parser callback and adds user-defined string-type support to the parser + and serializer. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.1.2). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.10.0.md b/docs/mkdocs/docs/home/releases/v3.10.0.md new file mode 100644 index 000000000..520e8ab39 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.10.0.md @@ -0,0 +1,20 @@ +# v3.10.0 + +Released on 2021-08-17. + +Feature release. All changes are backward-compatible. + +- Adds [extended diagnostic messages](../../api/macros/json_diagnostics.md) + ([`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md)) that prepend a JSON pointer to exception + messages to pinpoint the offending value. +- Adds a GDB pretty-printer and a [`cbor_tag_handler_t`](../../api/basic_json/cbor_tag_handler_t.md) + `store` option to keep CBOR tags as binary subtypes. +- Supports containers with non-default-constructible types and parsing from `std::byte`. +- Adds [`JSON_NO_IO`](../../api/macros/json_no_io.md) to exclude the I/O headers and the + [`JSON_HAS_CPP_*`](../../api/macros/json_has_cpp_11.md) macros to override the detected C++ standard. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.10.1.md b/docs/mkdocs/docs/home/releases/v3.10.1.md new file mode 100644 index 000000000..e6881beee --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.10.1.md @@ -0,0 +1,14 @@ +# v3.10.1 + +Released on 2021-08-24. + +Fixes a regression from 3.10.0. All changes are backward-compatible. + +- Fixes an [extended-diagnostics](../../api/macros/json_diagnostics.md) assertion triggered when used + with [`ordered_json`](../../api/ordered_json.md), and hardens the GDB pretty-printer. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.10.2.md b/docs/mkdocs/docs/home/releases/v3.10.2.md new file mode 100644 index 000000000..c442ac668 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.10.2.md @@ -0,0 +1,11 @@ +# v3.10.2 + +Released on 2021-08-26. + +Re-release of 3.10.1, whose Git tag pointed at the wrong commit due to a bug in the release script. +All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.2). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.10.3.md b/docs/mkdocs/docs/home/releases/v3.10.3.md new file mode 100644 index 000000000..0c84ccfee --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.10.3.md @@ -0,0 +1,16 @@ +# v3.10.3 + +Released on 2021-10-08. + +Fixes more regressions from 3.10.0. All changes are backward-compatible. + +- Fixes [extended-diagnostics](../../api/macros/json_diagnostics.md) assertions triggered by + [`update`](../../api/basic_json/update.md) and by inserting into arrays. +- Supports custom allocators when writing binary formats into a `std::vector`, and allows conversion + from types that only provide `begin()`/`end()`. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.3). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.10.4.md b/docs/mkdocs/docs/home/releases/v3.10.4.md new file mode 100644 index 000000000..dcb81e38a --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.10.4.md @@ -0,0 +1,16 @@ +# v3.10.4 + +Released on 2021-10-16. + +Fixes regressions introduced in 3.10.0. All changes are backward-compatible. + +- Fixes the `std::filesystem::path` conversion (which could trigger a stack overflow and broke + compilation on Windows). +- Fixes compilation for types with an explicit defaulted constructor and for code relying on the + return values of `std::find` and `std::remove`. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.4). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.10.5.md b/docs/mkdocs/docs/home/releases/v3.10.5.md new file mode 100644 index 000000000..e1c82ba41 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.10.5.md @@ -0,0 +1,15 @@ +# v3.10.5 + +Released on 2022-01-03. + +Bug-fix release. All changes are backward-compatible. + +- Guards the `std::filesystem` conversions behind compiler-support checks + ([`JSON_HAS_FILESYSTEM`](../../api/macros/json_has_filesystem.md)), which can be set to `0` to disable + them altogether. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.10.5). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.11.0.md b/docs/mkdocs/docs/home/releases/v3.11.0.md new file mode 100644 index 000000000..c25a069a2 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.11.0.md @@ -0,0 +1,27 @@ +# v3.11.0 + +Released on 2022-08-01. + +One of the largest releases ever. All changes are backward-compatible. + +- Allows `std::string_view` as object keys in [`at`](../../api/basic_json/at.md), + [`operator[]`](../../api/basic_json/operator%5B%5D.md), [`value`](../../api/basic_json/value.md), + [`erase`](../../api/basic_json/erase.md), [`find`](../../api/basic_json/find.md), + [`contains`](../../api/basic_json/contains.md), and [`count`](../../api/basic_json/count.md). +- Adds the [BJData](../../features/binary_formats/bjdata.md) binary format (the fifth supported format). +- Improves C++20 support, including [`operator<=>`](../../api/basic_json/operator_spaceship.md) and + ``-compatible iterators. +- Adds a versioned, ABI-tagged inline [namespace](../../features/namespace.md) + ([`NLOHMANN_JSON_NAMESPACE`](../../api/macros/nlohmann_json_namespace.md)) and the option to move the + UDLs out of the global namespace ([`JSON_USE_GLOBAL_UDLS`](../../api/macros/json_use_global_udls.md)). +- Adds [`patch_inplace`](../../api/basic_json/patch_inplace.md), default values for the + [conversion macros](../../features/arbitrary_types.md#simplify-your-life-with-macros), and an option to + disable enum serialization ([`JSON_DISABLE_ENUM_SERIALIZATION`](../../api/macros/json_disable_enum_serialization.md)). + +This release introduced a UDL regression that was fixed in +[3.11.1](https://github.com/nlohmann/json/releases/tag/v3.11.1). +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.11.1.md b/docs/mkdocs/docs/home/releases/v3.11.1.md new file mode 100644 index 000000000..666a8951b --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.11.1.md @@ -0,0 +1,16 @@ +# v3.11.1 + +Released on 2022-08-01. + +Fixes a regression from 3.11.0. All changes are backward-compatible. + +- Restores the global [user-defined string literals](../../api/macros/json_use_global_udls.md) + [`operator""_json`](../../api/operator_literal_json.md) and + [`operator""_json_pointer`](../../api/operator_literal_json_pointer.md), which 3.11.0 had moved into a + namespace by default. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.11.2.md b/docs/mkdocs/docs/home/releases/v3.11.2.md new file mode 100644 index 000000000..a5f9e7ce3 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.11.2.md @@ -0,0 +1,18 @@ +# v3.11.2 + +Released on 2022-08-12. + +Fixes bugs found in 3.11.1 and restructures the namespace. All changes are backward-compatible. + +- Fixes the [`value`](../../api/basic_json/value.md) function (broken for strings, size types, and + `nullptr` in 3.11.0) and makes `json_fwd.hpp` self-contained. +- Restores using [`json_pointer`](../../api/json_pointer/index.md) as a key in associative containers and + comparing it with strings. +- Restructures the inline [namespace](../../features/namespace.md) and allows disabling the version + component, and avoids heap allocations in the [BJData](../../features/binary_formats/bjdata.md) parser. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.2). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.11.3.md b/docs/mkdocs/docs/home/releases/v3.11.3.md new file mode 100644 index 000000000..238a774c7 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.11.3.md @@ -0,0 +1,20 @@ +# v3.11.3 + +Released on 2023-11-28. + +Adds features and fixes bugs found in 3.11.2. All changes are backward-compatible. + +- Adds a [custom base class](../../api/basic_json/json_base_class_t.md) as a node customization point. +- Adds serialization-only [conversion macros](../../features/macros.md) + (`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE` and `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE`) + and a clearer parse error for empty input. +- Adds [Bazel](../../integration/package_managers.md#bazel) and + [Swift Package Manager](../../integration/package_managers.md#swift-package-manager) build support. +- Fixes custom allocators, a memory leak in [`adl_serializer`](../../api/adl_serializer/to_json.md)'s + `to_json`, initializer-list construction when `size_type` is not `int`, and many compiler warnings. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.11.3). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.12.0.md b/docs/mkdocs/docs/home/releases/v3.12.0.md new file mode 100644 index 000000000..07be55494 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.12.0.md @@ -0,0 +1,26 @@ +# v3.12.0 + +Released on 2025-04-11. + +Fixes bugs found in 3.11.3 and adds several features. No public API was removed or renamed. + +- Adds diagnostic byte positions via [`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md), + exposed through the new [`start_pos`](../../api/basic_json/start_pos.md) and + [`end_pos`](../../api/basic_json/end_pos.md) member functions. +- Makes the [conversion macros](../../features/arbitrary_types.md#simplify-your-life-with-macros) + templated (so they also work with [`ordered_json`](../../api/ordered_json.md)) and adds + [`NLOHMANN_DEFINE_DERIVED_TYPE`](../../api/macros/nlohmann_define_derived_type.md) for derived classes. +- Adds `std::optional` support (C++17) and lets [`patch`](../../api/basic_json/patch.md), + [`diff`](../../api/basic_json/diff.md), and [`flatten`](../../api/basic_json/flatten.md) work with + arbitrary string types. +- Extends the [binary formats](../../features/binary_formats/index.md): + [BJData](../../features/binary_formats/bjdata.md) draft 3 and unsigned 64-bit integers for + [BSON](../../features/binary_formats/bson.md). +- Adds multidimensional C-array conversion and UTF-8 encoded `std::filesystem::path` conversions, and + 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). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.2.0.md b/docs/mkdocs/docs/home/releases/v3.2.0.md new file mode 100644 index 000000000..6e6690254 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.2.0.md @@ -0,0 +1,16 @@ +# v3.2.0 + +Released on 2018-08-20. + +Feature release. All changes are backward-compatible. + +- Adds a [SAX interface](../../features/parsing/sax_interface.md) and a non-recursive parser. +- Adds parsing from wide-string types (`std::wstring`, `std::u16string`, `std::u32string`), conversion to + `std::string_view` (C++17), and round-tripping of `std::map`/`std::unordered_map` with non-string + keys. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.2.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.3.0.md b/docs/mkdocs/docs/home/releases/v3.3.0.md new file mode 100644 index 000000000..e4ee07c84 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.3.0.md @@ -0,0 +1,14 @@ +# v3.3.0 + +Released on 2018-10-05. + +Feature release. All changes are backward-compatible. + +- Adds GCC 4.8 support, the [`get_to`](../../api/basic_json/get_to.md) function, and an overhauled and + documented [CMake](../../integration/cmake.md) integration. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.3.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.4.0.md b/docs/mkdocs/docs/home/releases/v3.4.0.md new file mode 100644 index 000000000..49a4cf595 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.4.0.md @@ -0,0 +1,18 @@ +# v3.4.0 + +Released on 2018-10-30. + +Feature release. All changes are backward-compatible. + +- Adds [BSON](../../features/binary_formats/bson.md) read/write support. +- Adds configurable Unicode error handlers to [`dump`](../../api/basic_json/dump.md) (throw, replace with + U+FFFD, or ignore) and the + [`NLOHMANN_JSON_SERIALIZE_ENUM`](../../api/macros/nlohmann_json_serialize_enum.md) macro for + [enum conversion](../../features/enum_conversion.md). +- Improves parse-error messages with line/column positions and context. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.4.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.5.0.md b/docs/mkdocs/docs/home/releases/v3.5.0.md new file mode 100644 index 000000000..40a14e159 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.5.0.md @@ -0,0 +1,15 @@ +# v3.5.0 + +Released on 2018-12-22. + +Feature release. All changes are backward-compatible. + +- Adds structured-binding support via the [`items`](../../api/basic_json/items.md) function and reading + from `FILE*` in the [`parse`](../../api/basic_json/parse.md) function. +- Fixes the `eofbit` handling on input streams and a bug in the BSON SAX parser. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.5.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.6.0.md b/docs/mkdocs/docs/home/releases/v3.6.0.md new file mode 100644 index 000000000..48aa6e9b9 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.6.0.md @@ -0,0 +1,16 @@ +# v3.6.0 + +Released on 2019-03-20. + +Feature release. All changes are backward-compatible. + +- Reworks the [JSON pointer](../../features/json_pointer.md) interface (`operator/`, `push_back`, + `parent_pointer`, …). +- Adds a [`contains`](../../api/basic_json/contains.md) function to test for an object key and greatly + improves the performance of integer serialization. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.6.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.6.1.md b/docs/mkdocs/docs/home/releases/v3.6.1.md new file mode 100644 index 000000000..c1e8daf47 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.6.1.md @@ -0,0 +1,10 @@ +# v3.6.1 + +Released on 2019-03-20. + +Fixes a regression (GCC 7/8 compilation) and a `` build error introduced in 3.6.0. All +changes are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v3.6.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.7.0.md b/docs/mkdocs/docs/home/releases/v3.7.0.md new file mode 100644 index 000000000..c9e7b15b6 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.7.0.md @@ -0,0 +1,15 @@ +# v3.7.0 + +Released on 2019-07-28. + +Convenience features and house-keeping. All changes are backward-compatible. + +- Adds a [`contains`](../../api/basic_json/contains.md) overload that checks a JSON pointer without + throwing, a generic `to_string`, and a return value for + [`emplace_back`](../../api/basic_json/emplace_back.md). + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.7.1.md b/docs/mkdocs/docs/home/releases/v3.7.1.md new file mode 100644 index 000000000..98a85fb68 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.7.1.md @@ -0,0 +1,16 @@ +# v3.7.1 + +Released on 2019-11-06. + +Bug-fix release. All changes are backward-compatible. + +- Fixes a segmentation fault when serializing the `std::int64_t` minimum value and fixes + [`contains`](../../api/basic_json/contains.md) for JSON pointers. +- Allows [`items`](../../api/basic_json/items.md) with a custom string type and makes `json_pointer::back` + `const`. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.1). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.7.2.md b/docs/mkdocs/docs/home/releases/v3.7.2.md new file mode 100644 index 000000000..aea237be8 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.7.2.md @@ -0,0 +1,11 @@ +# v3.7.2 + +Released on 2019-11-10. + +Fixes a stack overflow for deeply nested input by making the destructor iterative; parsing of JSON +text is now bounded only by available memory (the binary-format parsers still recurse). All changes are backward-compatible. +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.2). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.7.3.md b/docs/mkdocs/docs/home/releases/v3.7.3.md new file mode 100644 index 000000000..f0a106f39 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.7.3.md @@ -0,0 +1,10 @@ +# v3.7.3 + +Released on 2019-11-17. + +Fixes a regression from 3.7.2 that could yield quadratic complexity in destructor calls. All changes +are backward-compatible. [Full release notes](https://github.com/nlohmann/json/releases/tag/v3.7.3). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.8.0.md b/docs/mkdocs/docs/home/releases/v3.8.0.md new file mode 100644 index 000000000..babd55fac --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.8.0.md @@ -0,0 +1,20 @@ +# v3.8.0 + +Released on 2020-06-14. + +Feature release. All changes are backward-compatible. + +- Introduces a [binary value](../../features/binary_values.md) type that is read from and written to + [CBOR](../../features/binary_formats/cbor.md), [BSON](../../features/binary_formats/bson.md), and + [MessagePack](../../features/binary_formats/messagepack.md), and can be shared between formats. +- Generalizes the input adapters to read from any `LegacyInputIterator` container (3–10 % faster + parsing). +- Fixes [`contains`](../../api/basic_json/contains.md) for JSON pointers and makes the binary + [`from_cbor`](../../api/basic_json/from_cbor.md)/[`from_msgpack`](../../api/basic_json/from_msgpack.md)/etc. + functions respect `allow_exceptions`. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.8.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.9.0.md b/docs/mkdocs/docs/home/releases/v3.9.0.md new file mode 100644 index 000000000..c04668c62 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.9.0.md @@ -0,0 +1,20 @@ +# v3.9.0 + +Released on 2020-07-27. + +Feature release adding four long-requested features. All changes are backward-compatible. + +- Optional [comment](../../features/comments.md) parsing in [`parse`](../../api/basic_json/parse.md) via the + `ignore_comments` parameter. +- [`ordered_json`](../../api/ordered_json.md) to preserve the [insertion order](../../features/object_order.md) + of object keys. +- An option to switch off [implicit conversions](../../api/macros/json_use_implicit_conversions.md). +- The [`NLOHMANN_DEFINE_TYPE_*`](../../features/arbitrary_types.md#simplify-your-life-with-macros) + convenience macros, plus high-precision-number support for + [UBJSON](../../features/binary_formats/ubjson.md) and CBOR tag handling. + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.9.0). + +## API changes + + diff --git a/docs/mkdocs/docs/home/releases/v3.9.1.md b/docs/mkdocs/docs/home/releases/v3.9.1.md new file mode 100644 index 000000000..a06f819d7 --- /dev/null +++ b/docs/mkdocs/docs/home/releases/v3.9.1.md @@ -0,0 +1,14 @@ +# v3.9.1 + +Released on 2020-08-06. + +Fixes two regressions from 3.9.0. All changes are backward-compatible. + +- Accepts consecutive [comments](../../features/comments.md) and completes the + [`ordered_json`](../../api/ordered_json.md) interface (e.g. `ordered_json::parse`). + +[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.9.1). + +## API changes + + diff --git a/docs/mkdocs/docs/images/range-begin-end.svg b/docs/mkdocs/docs/images/range-begin-end.svg index 8e2b2fb6c..99c9f8662 100644 --- a/docs/mkdocs/docs/images/range-begin-end.svg +++ b/docs/mkdocs/docs/images/range-begin-end.svg @@ -16,6 +16,18 @@ version="1.1" inkscape:version="0.48.4 r9939" sodipodi:docname="range-begin-end.svg"> + + ` holds the user-defined string literals on their own, and +`` adds zero-copy views and editable documents. See +[header-only integration](integration/index.md) for details. It is heavily unit-tested with 100% code coverage, checked with Valgrind and the Clang Sanitizers for memory leaks, and continuously fuzz-tested by Google OSS-Fuzz. @@ -18,7 +20,7 @@ Add the single header to your project and use the library like this: #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { @@ -29,6 +31,10 @@ int main() j["name"] = "Niels"; j["list"] = {1, 0, 2}; + // read a value into a C++ variable and use it + double pi = j["pi"].get(); + std::cout << "2 * pi = " << 2 * pi << '\n'; + // serialize with an indent of 4 spaces std::cout << j.dump(4) << '\n'; } @@ -58,7 +64,8 @@ Conda, and more), and pkg-config. --- - Creating, parsing, accessing, and serializing JSON values, JSON Pointer/Patch, binary formats, and more. + Creating, parsing, accessing, and serializing JSON values, JSON Pointer/Patch, binary formats, zero-copy views, + and more. [:octicons-arrow-right-24: Features](features/index.md) @@ -74,7 +81,8 @@ Conda, and more), and pkg-config. --- - The complete reference for `basic_json` and its member functions, types, and related classes. + The complete reference for `basic_json` and its member functions, types, and related classes, as well as for + `json_document` and `json_view`. [:octicons-arrow-right-24: API documentation](api/basic_json/index.md) @@ -92,7 +100,7 @@ Conda, and more), and pkg-config. What changed in each release, with links to the relevant documentation. - [:octicons-arrow-right-24: Releases](home/releases.md) + [:octicons-arrow-right-24: Releases](home/releases/index.md) - :octicons-people-24:{ .lg .middle } __Community__ @@ -108,7 +116,7 @@ Conda, and more), and pkg-config. This documentation is built from the `develop` branch and may describe changes that are not part of a release yet. Their version numbers are followed by an unreleased badge; see - [Releases](home/releases.md) for what shipped in each version. + [Releases](home/releases/index.md) for what shipped in each version. The library is licensed under the [MIT License](home/license.md). The source code, issue tracker, and discussions are on [GitHub](https://github.com/nlohmann/json). diff --git a/docs/mkdocs/docs/integration/bazel/example.cpp b/docs/mkdocs/docs/integration/bazel/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/bazel/example.cpp +++ b/docs/mkdocs/docs/integration/bazel/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/build2/example.cpp b/docs/mkdocs/docs/integration/build2/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/build2/example.cpp +++ b/docs/mkdocs/docs/integration/build2/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/cget/example.cpp b/docs/mkdocs/docs/integration/cget/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/cget/example.cpp +++ b/docs/mkdocs/docs/integration/cget/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/conan/example.cpp b/docs/mkdocs/docs/integration/conan/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/conan/example.cpp +++ b/docs/mkdocs/docs/integration/conan/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/conda/example.cpp b/docs/mkdocs/docs/integration/conda/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/conda/example.cpp +++ b/docs/mkdocs/docs/integration/conda/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/cpm/example.cpp b/docs/mkdocs/docs/integration/cpm/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/cpm/example.cpp +++ b/docs/mkdocs/docs/integration/cpm/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/example.cpp b/docs/mkdocs/docs/integration/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/example.cpp +++ b/docs/mkdocs/docs/integration/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/homebrew/example.cpp b/docs/mkdocs/docs/integration/homebrew/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/homebrew/example.cpp +++ b/docs/mkdocs/docs/integration/homebrew/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/hunter/example.cpp b/docs/mkdocs/docs/integration/hunter/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/hunter/example.cpp +++ b/docs/mkdocs/docs/integration/hunter/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/index.md b/docs/mkdocs/docs/integration/index.md index f022ec6ed..6b461ff99 100644 --- a/docs/mkdocs/docs/integration/index.md +++ b/docs/mkdocs/docs/integration/index.md @@ -37,12 +37,27 @@ file in `single_include/nlohmann` or [released here](https://github.com/nlohmann #include // for convenience -using json = nlohmann::json; +using nlohmann::json; ``` to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang). +!!! tip "Which file do I download?" + + For a project without a package manager, the library itself, including the `_json` literals, is **one file**: + [`json.hpp`](https://github.com/nlohmann/json/releases/latest/download/json.hpp) from the + [releases page](https://github.com/nlohmann/json/releases). You do not need to clone the repository, build + anything, or link a library. Put the file in a directory named `nlohmann` that is on your include path (for + example `third_party/nlohmann/json.hpp` with `-I third_party`), so that `#include ` works. + Download the companion headers listed below only if you use them: `json_view.hpp` for the + [zero-copy views](../features/json_view.md), `json_fwd.hpp` for forward declarations, and `json_literals.hpp` if + you define `JSON_NO_AUTOMATIC_UDLS`. Keep them in the same `nlohmann` directory. + + Do not copy only parts of the repository's `include/` directory: the headers in it include each other and must be + kept together. If your IDE reports "cannot open source file" while the compiler succeeds, the compiler's include + path is set correctly but the IDE's is not; add the same directory to the IDE's include paths. + You can further use file [`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp) for forward declarations (see [Compile times](compile_times.md)), and file @@ -54,3 +69,30 @@ For the read-only, non-owning [`basic_json_document`](../api/basic_json_document types, additionally include [`single_include/nlohmann/json_view.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_view.hpp); see [Zero-copy JSON views](../features/json_view.md). + +## Notes for builds and distribution + +**Reproducible and path-independent builds.** The runtime assertions use `assert`, which embeds the name of the source +file (`__FILE__`) in the binary. Release builds do not contain them, because they define `NDEBUG`. If you compile with +assertions enabled and need binaries that do not depend on the build directory, either define `NDEBUG` or map the +prefix of the path with the compiler option `-ffile-prefix-map==` (GCC 8 and later, Clang 10 and later). See +[Runtime assertions](../features/assertions.md). + +**There is no separate source file to compile.** The library is header-only, so there is no `.cpp` that could be +built with different options than the code that includes it. It is also not available as a precompiled library. To +compile the library with optimizations in an otherwise unoptimized build (for example to parse large files in a debug +build, which is slow because of the unoptimized code and the checks of the debug standard library), keep the code that +uses `json` in a separate source file and set the options for that file only. [Compile times](compile_times.md) +shows how to [instantiate `basic_json` once](compile_times.md#instantiate-basic_json-once). + +**Debugger visualization.** The Natvis file works with the Visual Studio debugger, including the `cppvsdbg` engine in +Visual Studio Code, but not with debuggers that wrap LLDB, such as `codelldb`; see [Debugging](../home/debugging.md). + +## See also + +- [CMake](cmake.md) - integrate the library into a CMake project +- [Package managers](package_managers.md) +- [pkg-config](pkg-config.md) +- [Compile times](compile_times.md) - reduce the compile time overhead +- [Migration guide](migration_guide.md) - upgrade from an earlier version +- [Releases](../home/releases/index.md) diff --git a/docs/mkdocs/docs/integration/macports/example.cpp b/docs/mkdocs/docs/integration/macports/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/macports/example.cpp +++ b/docs/mkdocs/docs/integration/macports/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/meson/example.cpp b/docs/mkdocs/docs/integration/meson/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/meson/example.cpp +++ b/docs/mkdocs/docs/integration/meson/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/msys2/example.cpp b/docs/mkdocs/docs/integration/msys2/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/msys2/example.cpp +++ b/docs/mkdocs/docs/integration/msys2/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/spack/example.cpp b/docs/mkdocs/docs/integration/spack/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/spack/example.cpp +++ b/docs/mkdocs/docs/integration/spack/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/swift/example.cpp b/docs/mkdocs/docs/integration/swift/example.cpp index ad0a06827..33ccbfef0 100644 --- a/docs/mkdocs/docs/integration/swift/example.cpp +++ b/docs/mkdocs/docs/integration/swift/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/vcpkg/example.cpp b/docs/mkdocs/docs/integration/vcpkg/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/vcpkg/example.cpp +++ b/docs/mkdocs/docs/integration/vcpkg/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/docs/integration/xmake/example.cpp b/docs/mkdocs/docs/integration/xmake/example.cpp index 1a7ac4de2..c51a502aa 100644 --- a/docs/mkdocs/docs/integration/xmake/example.cpp +++ b/docs/mkdocs/docs/integration/xmake/example.cpp @@ -2,7 +2,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; int main() { diff --git a/docs/mkdocs/hooks/api_changes.py b/docs/mkdocs/hooks/api_changes.py new file mode 100644 index 000000000..94073f534 --- /dev/null +++ b/docs/mkdocs/hooks/api_changes.py @@ -0,0 +1,163 @@ +"""Generate the "API changes" section of the release pages from the version histories of the API pages.""" + +# Every page below docs/api ends in a "Version history" section ("Added in version 3.8.0.", "... since version 3.13.0."). +# This hook collects these entries and replaces the `` marker of each page in home/releases with +# the pages that were added in that release and the changes made to existing pages. The page of a release is named +# after its version (v3.12.0.md); unreleased.md collects all versions newer than the latest release. The result is an +# overview of what changed in the API, not a change log. + +import logging +import os +import posixpath +import re + +log = logging.getLogger("mkdocs.hooks.api_changes") + +_DIR = "home/releases" +_PAGE_VERSION = re.compile(r"v(\d+)\.(\d+)\.(\d+)\.md") +_MARKER = "" +_HISTORY = re.compile(r"^## Version history\n(.*?)(?=^## |\Z)", re.DOTALL | re.MULTILINE) +_TITLE = re.compile(r"^# (.+)$", re.MULTILINE) +_ITEM = re.compile(r"^(?:[-*]|\d+\.)\s+") +_SENTENCE = re.compile(r"(?<=\.)\s+(?=[A-Z`\[])") +_VERSION = re.compile(r"\b(?:version|since|in)\s+(\d+)\.(\d+)\.(\d+)\b", re.IGNORECASE) +_FUTURE = re.compile(r"\bwill\b|\bplanned\b", re.IGNORECASE) # statements about versions that do not exist yet +_NEXT_MAJOR = (4, 0, 0) # versions from here on are announcements, not changes +_ADDED = re.compile(r"^(?:Added|Introduced)\b.*\bversion \d|^Since version \d+\.\d+\.\d+\.?$", re.IGNORECASE) +_LINK = re.compile(r"(\]\()([^)\s]+)(\))") + + +def on_page_markdown(markdown, *, page, config, files): + uri = page.file.src_uri + if posixpath.dirname(uri) != _DIR or _MARKER not in markdown: + return markdown + docs_dir = config["docs_dir"] + entries = _collect(docs_dir) + released = sorted(tuple(map(int, m.groups())) for m in + (_PAGE_VERSION.fullmatch(name) for name in os.listdir(os.path.join(docs_dir, _DIR))) if m) + match = _PAGE_VERSION.fullmatch(posixpath.basename(uri)) + if match: + versions = [tuple(map(int, match.groups()))] + else: # unreleased.md + versions = sorted((v for v in entries if not released or v > released[-1]), reverse=True) + return markdown.replace(_MARKER, _render(uri, [(v, entries[v]) for v in versions if v in entries])) + + +def _title(text, path): + match = _TITLE.search(text) + title = match.group(1) if match else os.path.splitext(os.path.basename(path))[0] + title = re.sub(r"", "", title) + title = title.replace("`", "").replace("\\", "").strip() + return title[len("nlohmann::"):] if title.startswith("nlohmann::") else title + + +def _collect(docs_dir): + """Return {version: {"added": [(title, uri)], "changed": [(title, uri, text)]}}.""" + result = {} + api = os.path.join(docs_dir, "api") + for root, _, names in sorted(os.walk(api)): + for name in sorted(names): + if not name.endswith(".md"): + continue + path = os.path.join(root, name) + uri = posixpath.relpath(path.replace(os.sep, "/"), docs_dir.replace(os.sep, "/")) + with open(path, encoding="utf-8") as source: + text = source.read() + history = _HISTORY.search(text) + if not history: + continue + title = _title(text, path) + found = _sentences(history.group(1)) + # a page is added in the earliest version named by an "Added in" sentence that opens an entry (an entry of + # a numbered list describes one overload; later unnumbered entries extend an existing page) + starts = [v for i, n, first, v, sentence in found if first and _ADDED.match(sentence) and (i == 0 or n)] + added = min(starts) if starts else None + page_added = False + for index, number, first, version, sentence in found: + if version >= _NEXT_MAJOR: + continue + bucket = result.setdefault(version, {"added": [], "changed": []}) + opens = first and _ADDED.match(sentence) and (index == 0 or number) + if opens and version == added: + if not page_added: + bucket["added"].append((title, uri)) + page_added = True + continue + if number and re.fullmatch(r"Added in version [\d.]+\.?", sentence): + sentence = f"Overload {number} added." + elif number: + sentence = f"Overload {number}: {sentence}" + if (title, uri, sentence) not in bucket["changed"]: + bucket["changed"].append((title, uri, sentence)) + return result + + +def _sentences(section): + """Yield (entry index, overload number, opens entry, version, sentence) for every sentence naming a version.""" + found = [] + for index, (number, entry) in enumerate(_entries(section)): + for position, sentence in enumerate(_SENTENCE.split(entry)): + version = _VERSION.search(sentence) + if not version or _FUTURE.search(sentence): + continue + found.append((index, number, position == 0, tuple(int(part) for part in version.groups()), sentence.strip())) + return found + + +def _entries(section): + entries = [] + for line in section.split("\n"): + item = _ITEM.match(line) + if item: + number = re.match(r"\d+", line) + entries.append([number.group(0) if number else None, _ITEM.sub("", line).strip()]) + elif line.strip() and entries and line[0].isspace(): + entries[-1][1] += " " + line.strip() # wrapped list item + elif line.strip(): + break # text after the list, such as an admonition + return [tuple(entry) for entry in entries] + + +def _relink(text, uri, page): + """Rewrite links relative to the API page `uri` so they work from the generated page.""" + def fix(match): + target = match.group(2) + if re.match(r"[a-z]+:|#|/", target): + return match.group(0) + resolved = posixpath.normpath(posixpath.join(posixpath.dirname(uri), target)) + return match.group(1) + posixpath.relpath(resolved, posixpath.dirname(page)) + match.group(3) + return _LINK.sub(fix, text) + + +def _link(title, uri, page): + return f"[`{title}`]({posixpath.relpath(uri, posixpath.dirname(page))})" + + +def _added(pages, page): + """Group the added pages by class: `basic_json`: [`get`](..), ...; pages without a class come first.""" + groups, single = {}, [] + for title, uri in pages: + directory = posixpath.dirname(uri) + if directory == "api": + single.append(_link(title, uri, page)) + elif posixpath.basename(uri) == "index.md": + groups.setdefault(directory, []).insert(0, f"[(overview)]({posixpath.relpath(uri, posixpath.dirname(page))})") + else: + groups.setdefault(directory, []).append(_link(title.split("::")[-1], uri, page)) + lines = [f"- {item}" for item in single] + for directory, items in groups.items(): + lines.append(f"- `{posixpath.basename(directory)}`: " + ", ".join(items)) + return lines + + +def _render(page, entries): + lines = [] + for version, bucket in entries: + if len(entries) > 1: + lines += [f"### Version {'.'.join(map(str, version))}", ""] + if bucket["added"]: + lines += [f"**Added ({len(bucket['added'])} pages):**", ""] + _added(bucket["added"], page) + [""] + if bucket["changed"]: + lines += ["**Changed:**", ""] + lines += [f"- {_link(t, u, page)}: {_relink(x, u, page)}" for t, u, x in bucket["changed"]] + [""] + return "\n".join(lines) if lines else "No documented API changes." diff --git a/docs/mkdocs/includes/glossary.md b/docs/mkdocs/includes/glossary.md index 86fbe313e..b84567b14 100644 --- a/docs/mkdocs/includes/glossary.md +++ b/docs/mkdocs/includes/glossary.md @@ -1,5 +1,7 @@ +*[AArch64]: 64-bit execution state of the Arm architecture +*[ABI]: Application Binary Interface *[ADL]: Argument-dependent lookup *[API]: Application Programming Interface *[ASCII]: American Standard Code for Information Interchange @@ -10,28 +12,51 @@ *[CBOR]: Concise Binary Object Representation *[CC0]: Creative Commons Zero *[CI]: Continuous Integration +*[CPU]: Central Processing Unit +*[CUDA]: Compute Unified Device Architecture +*[CVE]: Common Vulnerabilities and Exposures *[DOM]: Document Object Model *[EOF]: End of File *[FAQ]: Frequently Asked Questions *[GCC]: GNU Compiler Collection +*[GDB]: GNU Debugger +*[GiB]: Gibibyte (2^30 bytes) *[HTTP]: Hypertext Transfer Protocol *[ICC]: Intel C++ Compiler +*[IDE]: Integrated Development Environment *[IEEE]: Institute of Electrical and Electronics Engineers *[ISO]: International Organization for Standardization +*[IWYU]: Include What You Use *[JSON]: JavaScript Object Notation +*[JSONC]: JSON with Comments +*[JWCC]: JSON With Commas and Comments +*[LLDB]: LLVM Debugger +*[LTS]: Long-Term Support +*[MinGW]: Minimalist GNU for Windows *[MIT]: Massachusetts Institute of Technology -*[MSVC]: Microsoft Visual C++ *[MsgPack]: MessagePack +*[MSVC]: Microsoft Visual C++ +*[NaN]: Not a Number *[NASA]: National Aeronautics and Space Administration *[NDK]: Native Development Kit -*[NaN]: Not a Number +*[NEON]: Arm SIMD instruction set extension +*[NUL]: Null character (U+0000, byte value 0) +*[ODR]: One Definition Rule +*[REUSE]: Software licensing specification by the Free Software Foundation Europe *[RFC]: Request for Comments *[RTTI]: Runtime Type Information *[SAX]: Simple API for XML *[SDK]: Software Development Kit *[SFINAE]: Substitution failure is not an error +*[SGX]: Software Guard Extensions *[SHA]: Secure Hash Algorithm +*[SIMD]: Single Instruction, Multiple Data *[SPDX]: Software Package Data Exchange +*[SSE2]: Streaming SIMD Extensions 2 +*[SSSE3]: Supplemental Streaming SIMD Extensions 3 *[STL]: Standard Template Library *[UBJSON]: Universal Binary JSON +*[UDL]: User-defined literal +*[UDLs]: User-defined literals +*[URL]: Uniform Resource Locator *[UTF]: Unicode Transformation Format diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 90393ff0d..7e1c23aa8 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -51,7 +51,53 @@ nav: - home/license.md - "FAQ": home/faq.md - home/exceptions.md - - home/releases.md + - Releases: + - 'Overview': home/releases/index.md + - 'Unreleased': home/releases/unreleased.md + - 'v3.12.0': home/releases/v3.12.0.md + - 'v3.11.3': home/releases/v3.11.3.md + - 'v3.11.2': home/releases/v3.11.2.md + - 'v3.11.1': home/releases/v3.11.1.md + - 'v3.11.0': home/releases/v3.11.0.md + - 'v3.10.5': home/releases/v3.10.5.md + - 'v3.10.4': home/releases/v3.10.4.md + - 'v3.10.3': home/releases/v3.10.3.md + - 'v3.10.2': home/releases/v3.10.2.md + - 'v3.10.1': home/releases/v3.10.1.md + - 'v3.10.0': home/releases/v3.10.0.md + - 'v3.9.1': home/releases/v3.9.1.md + - 'v3.9.0': home/releases/v3.9.0.md + - 'v3.8.0': home/releases/v3.8.0.md + - 'v3.7.3': home/releases/v3.7.3.md + - 'v3.7.2': home/releases/v3.7.2.md + - 'v3.7.1': home/releases/v3.7.1.md + - 'v3.7.0': home/releases/v3.7.0.md + - 'v3.6.1': home/releases/v3.6.1.md + - 'v3.6.0': home/releases/v3.6.0.md + - 'v3.5.0': home/releases/v3.5.0.md + - 'v3.4.0': home/releases/v3.4.0.md + - 'v3.3.0': home/releases/v3.3.0.md + - 'v3.2.0': home/releases/v3.2.0.md + - 'v3.1.2': home/releases/v3.1.2.md + - 'v3.1.1': home/releases/v3.1.1.md + - 'v3.1.0': home/releases/v3.1.0.md + - 'v3.0.1': home/releases/v3.0.1.md + - 'v3.0.0': home/releases/v3.0.0.md + - 'v2.1.1': home/releases/v2.1.1.md + - 'v2.1.0': home/releases/v2.1.0.md + - 'v2.0.10': home/releases/v2.0.10.md + - 'v2.0.9': home/releases/v2.0.9.md + - 'v2.0.8': home/releases/v2.0.8.md + - 'v2.0.7': home/releases/v2.0.7.md + - 'v2.0.6': home/releases/v2.0.6.md + - 'v2.0.5': home/releases/v2.0.5.md + - 'v2.0.4': home/releases/v2.0.4.md + - 'v2.0.3': home/releases/v2.0.3.md + - 'v2.0.2': home/releases/v2.0.2.md + - 'v2.0.1': home/releases/v2.0.1.md + - 'v2.0.0': home/releases/v2.0.0.md + - 'v1.1.0': home/releases/v1.1.0.md + - 'v1.0.0': home/releases/v1.0.0.md - home/design_goals.md - home/architecture.md - home/customers.md @@ -60,6 +106,7 @@ nav: - Features: - features/index.md - features/arbitrary_types.md + - features/type_recipes.md - Binary Formats: - features/binary_formats/index.md - features/binary_formats/bjdata.md @@ -84,11 +131,13 @@ nav: - features/modifying_values.md - features/modules.md - 'nlohmann Namespace': features/namespace.md + - features/no_exceptions.md - features/object_order.md - features/performance.md - Parsing: - features/parsing/index.md - features/parsing/json_lines.md + - features/parsing/large_and_partial_input.md - features/parsing/parse_exceptions.md - features/parsing/parser_callbacks.md - features/parsing/sax_interface.md @@ -484,6 +533,7 @@ validation: unrecognized_links: warn hooks: + - hooks/api_changes.py - hooks/copy_markdown_source.py - hooks/unreleased_versions.py diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 5cb123c59..51f26706c 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -8,7 +8,7 @@ #pragma once -#include // transform +#include // min, transform #include // array #include // forward_list #include // inserter, front_inserter, end @@ -69,12 +69,10 @@ void from_json(const BasicJsonType& j, std::optional& opt) } #endif // JSON_HAS_CPP_17 -// overloads for basic_json template parameters -template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < std::is_arithmetic::value&& - !std::is_same::value, - int > = 0 > -void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +// read a number value of any of the three number types into val; +// returns false (leaving val untouched) if j is not a number +template +bool get_number_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) { @@ -85,17 +83,17 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) const auto* ptr = j.template get_ptr(); JSON_ASSERT(ptr != nullptr); val = static_cast(*ptr); - break; + return true; } case value_t::number_integer: { val = static_cast(*j.template get_ptr()); - break; + return true; } case value_t::number_float: { val = static_cast(*j.template get_ptr()); - break; + return true; } case value_t::null: @@ -106,7 +104,20 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) case value_t::binary: case value_t::discarded: default: - throw_type_must_be("number", j); + return false; + } +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + if (JSON_HEDLEY_UNLIKELY(!get_number_value(j, val))) + { + throw_type_must_be("number", j); } } @@ -245,29 +256,44 @@ inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType: arr = *j.template get_ptr(); } +// fill a std::array from the JSON array j: the size is checked once and the +// elements are read in order, so a short array converts the elements it has and +// then throws the out_of_range error at() would throw for the first missing one +template +void from_json_std_array(const BasicJsonType& j, std::array& arr) +{ + const auto& inner_array = *j.template get_ptr(); + const std::size_t size = (std::min)(N, inner_array.size()); + for (std::size_t i = 0; i < size; ++i) + { + arr[i] = inner_array[i].template get(); + } + if (JSON_HEDLEY_UNLIKELY(size < N)) + { + static_cast(j.at(size)); + } +} + template auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { - for (std::size_t i = 0; i < N; ++i) - { - arr[i] = j.at(i).template get(); - } + from_json_std_array(j, arr); } -// reserve() is called through this pair (modeled on from_json_object_reserve) -// so from_json_array_impl below has a single body for both ConstructibleArrayType -// that support reserve() and those that don't. -template -auto from_json_array_reserve(ConstructibleArrayType& arr, typename ConstructibleArrayType::size_type size, priority_tag<1> /*unused*/) --> decltype(arr.reserve(size), void()) +// reserve() is called through this pair (used for arrays and objects alike) +// so from_json_array_impl and the object from_json have a single body for both +// containers that support reserve() and those that don't. +template +auto reserve_if_possible(ConstructibleContainerType& c, typename ConstructibleContainerType::size_type size, priority_tag<1> /*unused*/) +-> decltype(c.reserve(size), void()) { - arr.reserve(size); + c.reserve(size); } -template -void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/) +template +void reserve_if_possible(ConstructibleContainerType& /*c*/, std::size_t /*size*/, priority_tag<0> /*unused*/) {} // append through push_back() where available; containers without it (such as @@ -297,7 +323,7 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p { ConstructibleArrayType ret; const auto* inner_array = j.template get_ptr(); - from_json_array_reserve(ret, inner_array->size(), priority_tag<1> {}); + reserve_if_possible(ret, inner_array->size(), priority_tag<1> {}); for (const auto& elem : *inner_array) { // get() returns *this, this won't call a from_json @@ -336,6 +362,37 @@ std::array from_json_inplace_array_impl(const BasicJsonType& return { { j.at(Idx).template get()... } }; } +// std::is_trivial is deprecated in C++26 (see binary_writer::to_char_type); +// a struct rather than an alias, as astyle indents an #ifdef block of aliases +template +struct is_trivial_element +#ifdef JSON_HAS_CPP_26 + : conjunction, std::is_trivially_default_constructible> +#else + : std::is_trivial +#endif +{}; + +// the element type may not be default constructible: construct every element +// in place (a pack expansion that is unrolled N times) +template +std::array from_json_make_array(const BasicJsonType& j, identity_tag> tag, + std::false_type /*is_trivial_element*/) +{ + return from_json_inplace_array_impl(j, tag, make_index_sequence {}); +} + +// a trivial element type cannot tell default construction plus assignment from +// construction in place, so use a loop instead of unrolling N conversions +template +std::array from_json_make_array(const BasicJsonType& j, identity_tag> /*unused*/, + std::true_type /*is_trivial_element*/) +{ + std::array arr; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init): every element is assigned or an exception is thrown + from_json_std_array(j, arr); + return arr; +} + template < typename BasicJsonType, typename T, std::size_t N > auto from_json(const BasicJsonType& j, identity_tag> tag) -> decltype(from_json_inplace_array_impl(j, tag, make_index_sequence {})) @@ -345,7 +402,7 @@ auto from_json(const BasicJsonType& j, identity_tag> tag) throw_type_must_be("array", j); } - return from_json_inplace_array_impl(j, tag, make_index_sequence {}); + return from_json_make_array(j, tag, is_trivial_element {}); } template @@ -378,17 +435,6 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } -template -auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/) --> decltype(obj.reserve(size), void()) -{ - obj.reserve(size); -} - -template -inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/) -{} - // the keys of the source are sorted for the usual object types, so hinting the // end makes every insertion constant-time (for containers that order their keys // the same way); containers without emplace_hint() (such as ordered_map) fall @@ -417,7 +463,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); - from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {}); + reserve_if_possible(ret, inner_object->size(), priority_tag<1> {}); for (const auto& p : *inner_object) { from_json_object_emplace(ret, p.first, p.second.template get(), priority_tag<1> {}); @@ -437,42 +483,17 @@ template < typename BasicJsonType, typename ArithmeticType, int > = 0 > inline void from_json(const BasicJsonType& j, ArithmeticType& val) { - switch (static_cast(j)) + if (get_number_value(j, val)) { - case value_t::number_unsigned: - { - // never null: a basic_json whose number_unsigned_t is number_integer_t - // stores no number_unsigned values (#2573) - const auto* ptr = j.template get_ptr(); - JSON_ASSERT(ptr != nullptr); - val = static_cast(*ptr); - break; - } - case value_t::number_integer: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_float: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::boolean: - { - val = static_cast(*j.template get_ptr()); - break; - } - - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::binary: - case value_t::discarded: - default: - throw_type_must_be("number", j); + return; } + + // unlike get_arithmetic_value(), a boolean is accepted here + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + throw_type_must_be("number", j); + } + val = static_cast(*j.template get_ptr()); } template diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index 7d7f4e038..cdef3019f 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -66,6 +66,7 @@ enum class exception_id : int 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_document_image = 116, ///< json_document::load() rejects an image // 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 @@ -100,6 +101,8 @@ enum class exception_id : int 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 + document_binary_value = 319, ///< a binary value is stored in a json_document + document_image_unsupported = 320, ///< a json_document image cannot be saved or loaded (discarded document, big-endian target) 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 @@ -117,6 +120,7 @@ enum class exception_id : int 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 + document_too_large = 416, ///< a json_document input, its edit storage, or an image reaches the 32-bit (4 GiB) limit // other_error internal_error = 500, ///< unreachable code was reached patch_test_failed = 501, ///< a JSON Patch 'test' operation failed @@ -146,6 +150,13 @@ class exception : public std::exception return concat("[json.exception.", ename, '.', std::to_string(id_), "] "); } + /// build the message "[json.exception..] " + template + static std::string what_impl(const std::string& ename, int id_, const std::string& what_arg, BasicJsonContext context) + { + return concat(name(ename, id_), diagnostics(context), what_arg); + } + static std::string diagnostics(std::nullptr_t /*leaf_element*/) { return ""; @@ -158,48 +169,33 @@ class exception : public std::exception std::vector tokens; for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent) { - switch (current->m_parent->type()) + const auto* parent = current->m_parent; + if (parent->is_array()) { - case value_t::array: + for (std::size_t i = 0; i < parent->m_data.m_value.array->size(); ++i) { - for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i) + if (&parent->m_data.m_value.array->operator[](i) == current) { - if (¤t->m_parent->m_data.m_value.array->operator[](i) == current) - { - tokens.emplace_back(std::to_string(i)); - break; - } + tokens.emplace_back(std::to_string(i)); + break; } - break; } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_data.m_value.object) - { - if (&element.second == current) - { - // data() is null-terminated, so a key containing - // a null byte is cut short here rather than - // truncating the whole message at what() - tokens.emplace_back(element.first.data()); - break; - } - } - break; - } - - case value_t::null: // LCOV_EXCL_LINE - case value_t::string: // LCOV_EXCL_LINE - case value_t::boolean: // LCOV_EXCL_LINE - case value_t::number_integer: // LCOV_EXCL_LINE - case value_t::number_unsigned: // LCOV_EXCL_LINE - case value_t::number_float: // LCOV_EXCL_LINE - case value_t::binary: // LCOV_EXCL_LINE - case value_t::discarded: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE } + else if (parent->is_object()) + { + for (const auto& element : *parent->m_data.m_value.object) + { + if (&element.second == current) + { + // data() is null-terminated, so a key containing + // a null byte is cut short here rather than + // truncating the whole message at what() + tokens.emplace_back(element.first.data()); + break; + } + } + } + // all other parent types (primitives) contribute no token } if (tokens.empty()) @@ -315,7 +311,7 @@ class invalid_iterator : public exception template::value, int> = 0> static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("invalid_iterator", id_, what_arg, context); return {id_, w.c_str()}; } @@ -339,7 +335,7 @@ class type_error : public exception template::value, int> = 0> static type_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("type_error", id_, what_arg, context); return {id_, w.c_str()}; } @@ -362,7 +358,7 @@ class out_of_range : public exception template::value, int> = 0> static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("out_of_range", id_, what_arg, context); return {id_, w.c_str()}; } @@ -385,7 +381,7 @@ class other_error : public exception template::value, int> = 0> static other_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("other_error", id_, what_arg, context); return {id_, w.c_str()}; } diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 6c7168d47..d3c3d5bdc 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -293,6 +293,40 @@ class binary_reader return is_object ? sax->end_object() : sax->end_array(); } + /*! + @brief close the innermost open container and report whether that + completed the value being read + + The value begun by the driver is complete once its container is not + inside another one. + + @param[out] value_complete whether no container is left open + @return whether the SAX parser accepted the end event + */ + bool leave_container_and_check(bool& value_complete) + { + if (JSON_HEDLEY_UNLIKELY(!leave_container())) + { + return false; + } + value_complete = container_stack.empty(); + return true; + } + + /*! + @brief read an object key and pass it to the SAX parser + + @tparam GetKey member function that reads the key into a string + @param[in,out] key string the key is read into; cleared first + @return whether reading the key and the SAX event succeeded + */ + template + bool read_object_key(string_t& key) + { + key.clear(); + return (this->*GetKey)(key) && sax->key(key); + } + ////////// // BSON // ////////// @@ -1064,6 +1098,34 @@ class binary_reader 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); } + /*! + @brief report an object key that is not a string + + @param[in] found description of what was found instead of a string key + @return false + */ + bool unsupported_key_type(const char* found) const + { + return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found); + } + + /*! + @brief read the length of a CBOR string or byte array and then its bytes + + @tparam NumberType the type of the length field + @tparam ContainerType the destination container (string_t or binary_t) + @param[in] context further context information (for diagnostics) + @param[out] result container the bytes are appended to + + @return whether reading completed + */ + template + bool get_cbor_sized_bytes(const char* context, ContainerType& result) + { + NumberType len{}; + return get_number(len) && get_bytes(len, context, result); + } + /*! @brief reads a definite-length CBOR string @@ -1114,26 +1176,22 @@ class binary_reader case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - std::uint8_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - std::uint16_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { - std::uint32_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { - std::uint64_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } default: @@ -1268,7 +1326,7 @@ class binary_reader break; } - return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found); + return unsupported_key_type(found); } /*! @@ -1321,30 +1379,22 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { - std::uint8_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { - std::uint16_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { - std::uint32_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { - std::uint64_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } default: @@ -1559,12 +1609,12 @@ class binary_reader if (at_end) { - if (JSON_HEDLEY_UNLIKELY(!leave_container())) + bool value_complete = false; + if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete))) { return false; } - // the value begun here is complete once its container is - if (container_stack.empty()) + if (value_complete) { return true; } @@ -1573,8 +1623,7 @@ class binary_reader if (top.is_object) { - key.clear(); - if (JSON_HEDLEY_UNLIKELY(!get_cbor_object_key(key) || !sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_cbor_object_key>(key))) { return false; } @@ -2045,20 +2094,17 @@ class binary_reader case 0xD9: // str 8 { - std::uint8_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 0xDA: // str 16 { - std::uint16_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 0xDB: // str 32 { - std::uint32_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } default: @@ -2155,7 +2201,7 @@ class binary_reader break; } - return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found); + return unsupported_key_type(found); } /*! @@ -2300,12 +2346,12 @@ class binary_reader if (container_stack.back().remaining == 0) { - if (JSON_HEDLEY_UNLIKELY(!leave_container())) + bool value_complete = false; + if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete))) { return false; } - // the value begun here is complete once its container is - if (container_stack.empty()) + if (value_complete) { return true; } @@ -2318,8 +2364,7 @@ class binary_reader if (is_object) { get(); - key.clear(); - if (JSON_HEDLEY_UNLIKELY(!get_msgpack_object_key(key) || !sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_msgpack_object_key>(key))) { return false; } @@ -2503,8 +2548,7 @@ class binary_reader { case 'U': { - std::uint8_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 'i': @@ -2537,8 +2581,7 @@ class binary_reader { break; } - std::uint16_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 'm': @@ -2547,8 +2590,7 @@ class binary_reader { break; } - std::uint32_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 'M': @@ -2557,8 +2599,7 @@ class binary_reader { break; } - std::uint64_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } default: @@ -3374,12 +3415,12 @@ class binary_reader if (at_end) { - if (JSON_HEDLEY_UNLIKELY(!leave_container())) + bool value_complete = false; + if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete))) { return false; } - // the value begun here is complete once its container is - if (container_stack.empty()) + if (value_complete) { return true; } @@ -3388,8 +3429,7 @@ class binary_reader if (top.is_object) { - key.clear(); - if (JSON_HEDLEY_UNLIKELY(!get_bon8_key(key) || !sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_bon8_key>(key))) { return false; } @@ -4129,6 +4169,22 @@ class binary_reader return true; } + /*! + @brief read a length of type @a NumberType, then a string of that many bytes + + @tparam NumberType the type of the length field + @param[out] result string the bytes are appended to + @param[in] context further context information (for diagnostics) + + @return whether reading and validating the string succeeded + */ + template + bool get_string_with_length(string_t& result, const char* context) + { + NumberType len{}; + return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + } + /*! @brief create a byte array by reading bytes from the input @@ -4338,37 +4394,7 @@ class binary_reader const std::string& context) const { std::string error_msg = "syntax error while parsing "; - - switch (input_format) - { - case input_format_t::cbor: - error_msg += "CBOR"; - break; - - case input_format_t::msgpack: - error_msg += "MessagePack"; - break; - - case input_format_t::ubjson: - error_msg += "UBJSON"; - break; - - case input_format_t::bson: - error_msg += "BSON"; - break; - - case input_format_t::bjdata: - error_msg += "BJData"; - break; - - case input_format_t::bon8: - error_msg += "BON8"; - break; - - case input_format_t::json: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - } + error_msg += input_format_name(input_format); return concat(error_msg, ' ', context, ": ", detail); } diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 05fd4c816..af1a6d537 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -67,6 +67,36 @@ namespace detail /// the supported input formats enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 }; +/// @return the name of a binary input format, as used in exception messages +inline const char* input_format_name(const input_format_t format) noexcept +{ + switch (format) + { + case input_format_t::cbor: + return "CBOR"; + case input_format_t::msgpack: + return "MessagePack"; + case input_format_t::ubjson: + return "UBJSON"; + case input_format_t::bson: + return "BSON"; + case input_format_t::bjdata: + return "BJData"; + case input_format_t::bon8: + return "BON8"; + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return ""; // LCOV_EXCL_LINE + } +} + +/// @return the message of the parse_error thrown or reported for an empty input +inline const char* empty_input_message() noexcept +{ + return "attempting to parse an empty input; check that your input string or stream contains the expected JSON"; +} + //////////////////// // input adapters // //////////////////// @@ -1268,7 +1298,7 @@ inline file_input_adapter input_adapter(std::FILE* file) { if (file == 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)); + JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr)); } return file_input_adapter(file); } @@ -1277,7 +1307,7 @@ inline input_stream_adapter input_adapter(std::istream& stream) { if (stream.rdbuf() == 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)); + JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr)); } return input_stream_adapter(stream); } @@ -1302,7 +1332,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b) { if (b == 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)); + JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr)); } auto length = std::strlen(reinterpret_cast(b)); const auto* ptr = reinterpret_cast(b); diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index db95685f0..04201efbe 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -321,6 +321,52 @@ struct diagnostic_positions }; #endif +/*! +@brief error state shared by the DOM-building SAX parsers + +Holds whether a parse error occurred and whether errors are thrown, and +implements the parse_error() and is_errored() members of the SAX interface for +json_sax_dom_parser and json_sax_dom_callback_parser. +*/ +class json_sax_dom_error_state +{ + public: + explicit json_sax_dom_error_state(const bool allow_exceptions_) noexcept + : allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_error_state(const json_sax_dom_error_state&) = delete; + json_sax_dom_error_state(json_sax_dom_error_state&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_error_state& operator=(const json_sax_dom_error_state&) = delete; + json_sax_dom_error_state& operator=(json_sax_dom_error_state&&) = delete; // the const member prevents assignment + ~json_sax_dom_error_state() = default; + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + bool is_errored() const + { + return errored; + } + + private: + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + /*! @brief a hash table that finds the position of a key in an object whose elements are kept in a vector (ordered_map) @@ -429,7 +475,7 @@ read up to it. parse error) */ template -class json_sax_dom_parser +class json_sax_dom_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -445,7 +491,7 @@ class json_sax_dom_parser @param[in] allow_exceptions_ whether parse errors yield exceptions */ explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_) {} // make class move-only @@ -596,24 +642,6 @@ class json_sax_dom_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: using object_t = typename BasicJsonType::object_t; using array_t = typename BasicJsonType::array_t; @@ -622,9 +650,17 @@ class json_sax_dom_parser /// so that a big object can find the keys it has in a hash table instead of /// searching all of them template - using detect_indexable_keys = decltype(std::declval().emplace_new_key(std::declval(), std::declval()), - std::hash()(std::declval())); - static constexpr bool indexed_keys = is_detected::value; + using detect_emplace_new_key = decltype(std::declval().emplace_new_key(std::declval(), std::declval())); + template + using detect_hash = decltype(std::hash()(std::declval())); + // std::hash is only named once the object can hold the keys in a vector: + // VS 2015 static-asserts on std::hash of a type without one instead of + // reporting a substitution failure + template::value> + struct detect_indexable_keys : std::false_type {}; + template + struct detect_indexable_keys : is_detected {}; + static constexpr bool indexed_keys = detect_indexable_keys::value; /// what a frame holds for objects that are kept in a vector: the key index /// of an object with many keys, and for an object that is collected on the @@ -746,7 +782,10 @@ class json_sax_dom_parser { const std::size_t new_capacity = capacity * 2; std::unique_ptr new_heap(new frame[new_capacity]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - std::copy(data, data + count, new_heap.get()); + for (std::size_t i = 0; i < count; ++i) + { + new_heap[i] = data[i]; + } heap = std::move(new_heap); data = heap.get(); capacity = new_capacity; @@ -764,7 +803,10 @@ class json_sax_dom_parser { if (other.heap == nullptr) { - std::copy(other.local.begin(), other.local.begin() + static_cast(other.count), local.begin()); + for (std::size_t i = 0; i < other.count; ++i) + { + local[i] = other.local[i]; + } data = local.data(); capacity = local_capacity; } @@ -909,6 +951,8 @@ class json_sax_dom_parser /// directly if the value was the one too many. void place_container(const frame& f, frame* enclosing, BasicJsonType&& value, const bool enclosing_is_innermost) { + // only a container with a slot may have no enclosing container + JSON_ASSERT(f.slot != nullptr || enclosing != nullptr); if (f.slot != nullptr) { *f.slot = std::move(value); @@ -1325,10 +1369,6 @@ class json_sax_dom_parser /// the array that stores the elements of the innermost array as they /// arrive (frames.back().array), or nullptr array_t* stored_array = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// the lexer reference to obtain the current position lexer_t* m_lexer_ref = nullptr; }; @@ -1353,7 +1393,7 @@ constructor contains the parsed value. @tparam BasicJsonType the JSON type */ template -class json_sax_dom_binary_parser +class json_sax_dom_binary_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -1369,7 +1409,7 @@ class json_sax_dom_binary_parser @param[in] allow_exceptions_ whether parse errors yield exceptions */ explicit json_sax_dom_binary_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_) {} // make class move-only @@ -1498,24 +1538,6 @@ class json_sax_dom_binary_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: /*! @@ -1569,16 +1591,12 @@ class json_sax_dom_binary_parser std::vector ref_stack {}; /// helper to hold the reference for the next object element BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// the lexer reference to obtain the current position lexer_t* m_lexer_ref = nullptr; }; template -class json_sax_dom_callback_parser +class json_sax_dom_callback_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -1594,7 +1612,7 @@ class json_sax_dom_callback_parser parser_callback_t cb, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), callback(std::move(cb)), m_lexer_ref(lexer_) { keep_stack.push_back(true); } @@ -1858,24 +1876,6 @@ class json_sax_dom_callback_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: /*! @@ -2142,12 +2142,8 @@ class json_sax_dom_callback_parser /// value is later rejected by the callback, instead of erasing the /// member entirely std::vector> duplicate_key_stash {}; - /// whether a syntax error occurred - bool errored = false; /// callback function const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// a discarded value for the callback BasicJsonType discarded = BasicJsonType::value_t::discarded; /// the value of the key event for the callback (see key_value()) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index aec10c5d2..14d14f42a 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -657,194 +657,74 @@ class lexer : public lexer_base // invalid control characters case 0x00: - { - error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; - return token_type::parse_error; - } - case 0x01: - { - error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; - return token_type::parse_error; - } - case 0x02: - { - error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; - return token_type::parse_error; - } - case 0x03: - { - error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; - return token_type::parse_error; - } - case 0x04: - { - error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; - return token_type::parse_error; - } - case 0x05: - { - error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; - return token_type::parse_error; - } - case 0x06: - { - error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; - return token_type::parse_error; - } - case 0x07: - { - error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; - return token_type::parse_error; - } - case 0x08: - { - error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; - return token_type::parse_error; - } - case 0x09: - { - error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; - return token_type::parse_error; - } - case 0x0A: - { - error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; - return token_type::parse_error; - } - case 0x0B: - { - error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; - return token_type::parse_error; - } - case 0x0C: - { - error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; - return token_type::parse_error; - } - case 0x0D: - { - error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; - return token_type::parse_error; - } - case 0x0E: - { - error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; - return token_type::parse_error; - } - case 0x0F: - { - error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; - return token_type::parse_error; - } - case 0x10: - { - error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; - return token_type::parse_error; - } - case 0x11: - { - error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; - return token_type::parse_error; - } - case 0x12: - { - error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; - return token_type::parse_error; - } - case 0x13: - { - error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; - return token_type::parse_error; - } - case 0x14: - { - error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; - return token_type::parse_error; - } - case 0x15: - { - error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; - return token_type::parse_error; - } - case 0x16: - { - error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; - return token_type::parse_error; - } - case 0x17: - { - error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; - return token_type::parse_error; - } - case 0x18: - { - error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; - return token_type::parse_error; - } - case 0x19: - { - error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; - return token_type::parse_error; - } - case 0x1A: - { - error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; - return token_type::parse_error; - } - case 0x1B: - { - error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; - return token_type::parse_error; - } - case 0x1C: - { - error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; - return token_type::parse_error; - } - case 0x1D: - { - error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; - return token_type::parse_error; - } - case 0x1E: - { - error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; - return token_type::parse_error; - } - case 0x1F: { - error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + static const std::array control_character_messages = {{ + "invalid string: control character U+0000 (NUL) must be escaped to \\u0000", + "invalid string: control character U+0001 (SOH) must be escaped to \\u0001", + "invalid string: control character U+0002 (STX) must be escaped to \\u0002", + "invalid string: control character U+0003 (ETX) must be escaped to \\u0003", + "invalid string: control character U+0004 (EOT) must be escaped to \\u0004", + "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005", + "invalid string: control character U+0006 (ACK) must be escaped to \\u0006", + "invalid string: control character U+0007 (BEL) must be escaped to \\u0007", + "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b", + "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t", + "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n", + "invalid string: control character U+000B (VT) must be escaped to \\u000B", + "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f", + "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r", + "invalid string: control character U+000E (SO) must be escaped to \\u000E", + "invalid string: control character U+000F (SI) must be escaped to \\u000F", + "invalid string: control character U+0010 (DLE) must be escaped to \\u0010", + "invalid string: control character U+0011 (DC1) must be escaped to \\u0011", + "invalid string: control character U+0012 (DC2) must be escaped to \\u0012", + "invalid string: control character U+0013 (DC3) must be escaped to \\u0013", + "invalid string: control character U+0014 (DC4) must be escaped to \\u0014", + "invalid string: control character U+0015 (NAK) must be escaped to \\u0015", + "invalid string: control character U+0016 (SYN) must be escaped to \\u0016", + "invalid string: control character U+0017 (ETB) must be escaped to \\u0017", + "invalid string: control character U+0018 (CAN) must be escaped to \\u0018", + "invalid string: control character U+0019 (EM) must be escaped to \\u0019", + "invalid string: control character U+001A (SUB) must be escaped to \\u001A", + "invalid string: control character U+001B (ESC) must be escaped to \\u001B", + "invalid string: control character U+001C (FS) must be escaped to \\u001C", + "invalid string: control character U+001D (GS) must be escaped to \\u001D", + "invalid string: control character U+001E (RS) must be escaped to \\u001E", + "invalid string: control character U+001F (US) must be escaped to \\u001F" + } + }; + error_message = control_character_messages[static_cast(current)]; return token_type::parse_error; } diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 3017a29fd..665052d73 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -165,23 +165,10 @@ class parser if (result) { - if (strict) - { - // strict mode: next byte must be EOF - if (get_token() != token_type::end_of_input) - { - return syntax_error(*sax, exception_message(token_type::end_of_input, "value")); - } - } - else - { - // the caller keeps using the input: position it right after - // the value by leaving the character that terminated it - m_lexer.release_lookahead(); - } + return finish_input(*sax, strict); } - return result; + return false; } private: @@ -202,6 +189,39 @@ class parser return m_lexer.get_string(); } + /*! + @brief apply the end-of-input handling after a value was parsed + + In strict mode, the next token must be the end of the input; otherwise + parse_error.101 is reported through @a sax. In non-strict mode, the + lookahead is released so the caller can keep reading the input right after + the parsed value. + + @param[in,out] sax the SAX parser to report an error to + @param[in] strict whether to expect the last token to be EOF + @return true, or what @a sax returned for the error + */ + template + bool finish_input(SAX& sax, const bool strict) + { + if (strict) + { + // strict mode: next byte must be EOF + if (get_token() != token_type::end_of_input) + { + return syntax_error(sax, exception_message(token_type::end_of_input, "value")); + } + } + else + { + // the caller keeps using the input: position it right after + // the value by leaving the character that terminated it + m_lexer.release_lookahead(); + } + + return true; + } + /*! @brief run a DOM SAX parser to completion and position the lexer @@ -221,20 +241,7 @@ class parser { sax_parse_internal(&sdp); - if (strict) - { - // in strict mode, input must be completely read - if (get_token() != token_type::end_of_input) - { - syntax_error(sdp, exception_message(token_type::end_of_input, "value")); - } - } - else - { - // the caller keeps using the input: position it right after - // the value by leaving the character that terminated it - m_lexer.release_lookahead(); - } + static_cast(finish_input(sdp, strict)); return !sdp.is_errored(); } @@ -411,7 +418,7 @@ class parser { if (JSON_HEDLEY_UNLIKELY(m_lexer.get_chars_read_total() == 1)) { - return syntax_error(*sax, "attempting to parse an empty input; check that your input string or stream contains the expected JSON"); + return syntax_error(*sax, empty_input_message()); } return syntax_error(*sax, exception_message(token_type::literal_or_value, "value")); diff --git a/include/nlohmann/detail/input/string_scan.hpp b/include/nlohmann/detail/input/string_scan.hpp index abe39e475..d01a2f1a4 100644 --- a/include/nlohmann/detail/input/string_scan.hpp +++ b/include/nlohmann/detail/input/string_scan.hpp @@ -96,7 +96,7 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d // printable ASCII except '"' and '\\' - is emitted unchanged. Note this differs // from is_string_special() only in that 0x7F is also a stop (it is escaped as // \u007f under ensure_ascii). -inline bool is_ascii_copyable(unsigned char c) noexcept +constexpr bool is_ascii_copyable(unsigned char c) noexcept { return c >= 0x20u && c < 0x7Fu && c != '"' && c != '\\'; } diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 120c1b893..84a850356 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -108,33 +108,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } + m_it.object_iterator = typename object_t::iterator(); + } + else if (m_object->is_array()) + { + m_it.array_iterator = typename array_t::iterator(); + } + else + { + // primitive (is_primitive(), including discarded) + m_it.primitive_iterator = primitive_iterator_t(); } } @@ -249,33 +234,28 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - { - m_it.object_iterator = m_object->m_data.m_value.object->end(); - break; - } + m_it.object_iterator = m_object->m_data.m_value.object->end(); + } + else if (m_object->is_array()) + { + m_it.array_iterator = m_object->m_data.m_value.array->end(); + } + else + { + // primitive (is_primitive(), including discarded) + m_it.primitive_iterator.set_end(); + } + } - case value_t::array: - { - m_it.array_iterator = m_object->m_data.m_value.array->end(); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator.set_end(); - break; - } + /// @throw invalid_iterator.212 if @a other points to another container + template + JSON_HEDLEY_ALWAYS_INLINE void check_same_container(const IterImpl& other) const + { + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + throw_literal(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object); } } @@ -424,10 +404,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci bool operator==(const IterImpl& other) const { // if objects are not the same, the comparison is undefined - if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) - { - throw_literal(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object); - } + check_same_container(other); // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 if (m_object == nullptr) @@ -466,10 +443,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined - if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) - { - throw_literal(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object); - } + check_same_container(other); // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 if (m_object == nullptr) @@ -478,25 +452,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return false; } - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - throw_literal(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object); - - case value_t::array: - return (m_it.array_iterator < other.m_it.array_iterator); - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return (m_it.primitive_iterator < other.m_it.primitive_iterator); + throw_literal(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object); } + + if (m_object->is_array()) + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + // primitive (is_primitive(), including discarded) + return (m_it.primitive_iterator < other.m_it.primitive_iterator); } /*! @@ -534,30 +501,19 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); + throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); + } - case value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator += i; - break; - } + if (m_object->is_array()) + { + std::advance(m_it.array_iterator, i); + } + else + { + // primitive (is_primitive(), including discarded) + m_it.primitive_iterator += i; } return *this; @@ -613,25 +569,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); - - case value_t::array: - return m_it.array_iterator - other.m_it.array_iterator; - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return m_it.primitive_iterator - other.m_it.primitive_iterator; + throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); } + + if (m_object->is_array()) + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + // primitive (is_primitive(), including discarded) + return m_it.primitive_iterator - other.m_it.primitive_iterator; } /*! diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index c66a5c0fd..23a9e3b0d 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -109,35 +109,25 @@ template class iteration_proxy_value { JSON_ASSERT(anchor.m_object != nullptr); - switch (anchor.m_object->type()) + if (anchor.m_object->is_array()) { // use integer array index as key - case value_t::array: + if (array_index != array_index_last) { - if (array_index != array_index_last) - { - int_to_string( array_index_str, array_index ); - array_index_last = array_index; - } - return array_index_str; + int_to_string( array_index_str, array_index ); + array_index_last = array_index; } - - // use key from the object - case value_t::object: - return anchor.key(); - - // use an empty key for all primitive types - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return empty_str; + return array_index_str; } + + if (anchor.m_object->is_object()) + { + // use key from the object + return anchor.key(); + } + + // use an empty key for all primitive types + return empty_str; } /// return value of the iterator diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 50fe7f53b..4560714ab 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -38,6 +38,102 @@ NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { struct json_pointer_access; + +/*! +@brief result of parse_array_index() + +json_pointer::array_index() maps each value to the corresponding +parse_error/out_of_range exception; json_pointer::contains() and +get_checked_or_null(), which must not throw for an out-of-range or +unrepresentable index, and the json_view pointer resolution switch on it +directly instead. +*/ +enum class array_index_status +{ + ok, ///< @a s is a valid, representable array index + leading_zero, ///< @a s begins with '0' but has more than one character + 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 + exceeds_size_type ///< @a s converts to an integer that exceeds SizeType +}; + +/*! +@brief convert a JSON Pointer reference token into an array index (RFC 6901, Sect. 4) + +@param[in] s reference token to be converted into an array index; its data() + must be NUL-terminated +@param[out] idx the integer representation of @a s if array_index_status::ok + is returned; left unchanged otherwise + +@return whether @a s is a valid array index, and if not, why + +@note this function never throws; json_pointer::array_index(), the callers that + must not throw, and the json_view pointer resolution build on it instead + of each re-implementing the RFC 6901 digit rules and the range check +@note force-inlined: as a free function template it is no longer inlined into + the pointer resolution, which makes JsonPointerAt up to 17% slower (#5891) +*/ +template +JSON_HEDLEY_ALWAYS_INLINE array_index_status parse_array_index(const StringType& s, SizeType& idx) noexcept +{ + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + return array_index_status::leading_zero; + } + + // error condition (cf. RFC 6901, Sect. 4); this also covers single- + // 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.size() == 1 && s[0] == '-') && !(s[0] >= '0' && s[0] <= '9'))) + { + return array_index_status::not_a_number; + } + + // fast path: up to 18 decimal digits cannot overflow unsigned long long or + // SizeType (9 digits for a 32-bit SizeType), so strtoull and errno are not needed + constexpr std::size_t max_fast_digits = sizeof(SizeType) >= 8 ? 18 : 9; + if (!s.empty() && s.size() <= max_fast_digits) + { + SizeType acc = 0; + for (std::size_t i = 0; i < s.size(); ++i) + { + const char c = s[i]; + if (JSON_HEDLEY_UNLIKELY(c < '0' || c > '9')) + { + break; // not a plain number: let strtoull decide + } + acc = static_cast((acc * 10) + static_cast(c - '0')); + if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1)) + { + idx = acc; + return array_index_status::ok; + } + } + } + + const char* p = s.data(); + char* p_end = nullptr; // NOLINT(misc-const-correctness) + errno = 0; // strtoull doesn't reset errno + const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) + if (p == p_end // invalid input or empty string + || errno == ERANGE // out of range + || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read + { + return array_index_status::unresolved; + } + + // the index does not fit into SizeType; on 64-bit platforms this is + // only SIZE_MAX itself (see #2203 and #5395) + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + return array_index_status::exceeds_size_type; + } + + idx = static_cast(res); + return array_index_status::ok; +} + } // namespace detail /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document @@ -302,6 +398,28 @@ class json_pointer JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr)); } + /// whether the '~' at @a pos of @a s is not followed by '0' or '1' + JSON_HEDLEY_ALWAYS_INLINE static bool is_invalid_escape(const string_t& s, std::size_t pos) + { + return pos == s.size() - 1 || (s[pos + 1] != '0' && s[pos + 1] != '1'); + } + + /// @throw parse_error.107 always + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_missing_slash(const string_t& pointer) + { + static_cast(pointer); // unused when JSON_NOEXCEPTION is defined + JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", pointer, "'"), nullptr)); + } + + /// @throw parse_error.107 if a nonempty @a pointer does not begin with '/' + JSON_HEDLEY_ALWAYS_INLINE static void check_begins_with_slash(const string_t& pointer) + { + if (JSON_HEDLEY_UNLIKELY(pointer[0] != '/')) + { + throw_missing_slash(pointer); + } + } + /// @throw out_of_range.404 always template JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_unresolved(const string_t& reference_token, BasicJsonContext context) @@ -311,21 +429,7 @@ class json_pointer 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 - - @ref array_index maps each value to the corresponding parse_error/out_of_range - exception; @ref contains and @ref get_checked_or_null, which must not throw for - an out-of-range or unrepresentable index, switch on it directly instead. - */ - enum class array_index_status - { - ok, ///< @a s is a valid, representable array index - leading_zero, ///< @a s begins with '0' but has more than one character - 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 - exceeds_size_type ///< @a s converts to an integer that exceeds size_type - }; + using array_index_status = detail::array_index_status; /// whether a reference token is "-" (a plain comparison with a string literal is not always inlined) static bool is_dash(const string_t& s) noexcept @@ -333,78 +437,11 @@ class json_pointer return s.size() == 1 && s[0] == '-'; } - /*! - @param[in] s reference token to be converted into an array index - @param[out] idx the integer representation of @a s if @ref array_index_status::ok - is returned; left unchanged otherwise - - @return whether @a s is a valid array index, and if not, why - - @note this function never throws; @ref array_index and the callers that must not - throw (@ref contains, @ref get_checked_or_null) build on it instead of each - re-implementing the RFC 6901 digit rules and the @a size_type range check - */ + /// detail::parse_array_index() for the size_type of @a BasicJsonType template static array_index_status parse_array_index(const string_t& s, typename BasicJsonType::size_type& idx) noexcept { - using size_type = typename BasicJsonType::size_type; - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) - { - return array_index_status::leading_zero; - } - - // error condition (cf. RFC 6901, Sect. 4); this also covers single- - // 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() && !is_dash(s) && !(s[0] >= '0' && s[0] <= '9'))) - { - return array_index_status::not_a_number; - } - - // fast path: up to 18 decimal digits cannot overflow unsigned long long or - // size_type (9 digits for a 32-bit size_type), so strtoull and errno are not needed - constexpr std::size_t max_fast_digits = sizeof(size_type) >= 8 ? 18 : 9; - if (!s.empty() && s.size() <= max_fast_digits) - { - size_type acc = 0; - for (std::size_t i = 0; i < s.size(); ++i) - { - const char c = s[i]; - if (JSON_HEDLEY_UNLIKELY(c < '0' || c > '9')) - { - break; // not a plain number: let strtoull decide - } - acc = static_cast((acc * 10) + static_cast(c - '0')); - if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1)) - { - idx = acc; - return array_index_status::ok; - } - } - } - - const char* p = s.data(); - char* p_end = nullptr; // NOLINT(misc-const-correctness) - errno = 0; // strtoull doesn't reset errno - const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) - if (p == p_end // invalid input or empty string - || errno == ERANGE // out of range - || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read - { - return array_index_status::unresolved; - } - - // the index does not fit into size_type; on 64-bit platforms this is - // only SIZE_MAX itself (see #2203 and #5395) - if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) - { - return array_index_status::exceeds_size_type; - } - - idx = static_cast(res); - return array_index_status::ok; + return detail::parse_array_index(s, idx); } /*! @@ -454,10 +491,7 @@ class json_pointer return reference_tokens.empty(); } - if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) - { - 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)); - } + check_begins_with_slash(reference_string); // the whole string is validated, whether or not it matches const std::size_t size = reference_string.size(); @@ -465,7 +499,7 @@ class json_pointer pos != string_t::npos; pos = reference_string.find_first_of('~', pos + 1)) { - if (JSON_HEDLEY_UNLIKELY(pos == size - 1 || (reference_string[pos + 1] != '0' && reference_string[pos + 1] != '1'))) + if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(reference_string, pos))) { JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } @@ -743,10 +777,7 @@ class json_pointer } const char* const data = key.data(); - if (JSON_HEDLEY_UNLIKELY(data[0] != '/')) - { - JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", key, "'"), nullptr)); - } + check_begins_with_slash(key); // The beginning of the key that it has in common with the previous // one, which was checked already, is skipped: the scan starts with @@ -779,7 +810,7 @@ class json_pointer else if (data[i] == '~') { // ~ must be followed by 0 or 1 - if (JSON_HEDLEY_UNLIKELY(i + 1 == size || (data[i + 1] != '0' && data[i + 1] != '1'))) + if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(key, i))) { JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } @@ -878,58 +909,46 @@ class json_pointer ++end; } - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + token.clear(); + token.append(begin, static_cast(end - begin)); + const auto it = ptr->m_data.m_value.object->find(token); + if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) { - token.clear(); - token.append(begin, static_cast(end - begin)); - const auto it = ptr->m_data.m_value.object->find(token); - if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) - { - return nullptr; - } - ptr = &it->second; - break; - } - - case detail::value_t::array: - { - // an index of up to 18 digits without a leading zero fits into size_type - // (9 digits for a 32-bit size_type); everything else is left to json_pointer - constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9; - const auto digits = static_cast(end - begin); - if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0'))) - { - return nullptr; - } - typename BasicJsonType::size_type idx = 0; - for (const char* c = begin; c != end; ++c) - { - if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9')) - { - return nullptr; - } - idx = static_cast((idx * 10) + static_cast(*c - '0')); - } - if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) - { - return nullptr; - } - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: return nullptr; + } + ptr = &it->second; + } + else if (ptr->is_array()) + { + // an index of up to 18 digits without a leading zero fits into size_type + // (9 digits for a 32-bit size_type); everything else is left to json_pointer + constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9; + const auto digits = static_cast(end - begin); + if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0'))) + { + return nullptr; + } + typename BasicJsonType::size_type idx = 0; + for (const char* c = begin; c != end; ++c) + { + if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9')) + { + return nullptr; + } + idx = static_cast((idx * 10) + static_cast(*c - '0')); + } + if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) + { + return nullptr; + } + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + return nullptr; } pos = static_cast(end - path.data()); steps.emplace_back(pos, ptr); @@ -979,40 +998,28 @@ class json_pointer : detail::value_t::object; } - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + // use unchecked object access + ptr = &object_member(*ptr, reference_token); + } + else if (ptr->is_array()) + { + if (reference_token == "-") { - // use unchecked object access - ptr = &object_member(*ptr, reference_token); - break; + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); } - - case detail::value_t::array: + else { - if (reference_token == "-") - { - // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - ptr = &ptr->operator[](array_index(reference_token)); - } - break; + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - throw_unresolved(reference_token, ptr); + } + else + { + // primitive value with a reference token left + throw_unresolved(reference_token, ptr); } } @@ -1030,51 +1037,39 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + const auto it = ptr->m_data.m_value.object->find(reference_token); + if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) { - const auto it = ptr->m_data.m_value.object->find(reference_token); - if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) - { - // at() throws out_of_range.403 with the usual message - ptr->at(reference_token); - } - ptr = &it->second; - break; + // at() throws out_of_range.403 with the usual message + ptr->at(reference_token); + } + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) + { + // "-" always fails the range check + 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)); } - case detail::value_t::array: + const auto idx = array_index(reference_token); + // 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(is_dash(reference_token))) - { - // "-" always fails the range check - 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)); - } - - const auto idx = array_index(reference_token); - // Bounds check before access to avoid exception with JSON_NOEXCEPTION - if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) - { - 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)); - } - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; + 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)); } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - throw_unresolved(reference_token, ptr); + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + throw_unresolved(reference_token, ptr); } } @@ -1103,44 +1098,32 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + // use unchecked object access; the const operator[] + // asserts that the key exists + const auto it = ptr->m_data.m_value.object->find(reference_token); + JSON_ASSERT(it != ptr->m_data.m_value.object->end()); + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) { - // use unchecked object access; the const operator[] - // asserts that the key exists - const auto it = ptr->m_data.m_value.object->find(reference_token); - JSON_ASSERT(it != ptr->m_data.m_value.object->end()); - ptr = &it->second; - break; + // "-" cannot be used for const access + 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)); } - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) - { - // "-" cannot be used for const access - 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[] - // asserts that the index exists - const auto idx = array_index(reference_token); - JSON_ASSERT(idx < ptr->m_data.m_value.array->size()); - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - throw_unresolved(reference_token, ptr); + // use unchecked array access; the const operator[] + // asserts that the index exists + const auto idx = array_index(reference_token); + JSON_ASSERT(idx < ptr->m_data.m_value.array->size()); + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + throw_unresolved(reference_token, ptr); } } @@ -1164,66 +1147,54 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + const auto it = ptr->m_data.m_value.object->find(reference_token); + if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) { - const auto it = ptr->m_data.m_value.object->find(reference_token); - if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) - { - return nullptr; - } - ptr = &it->second; - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) - { - // "-" always fails the range check - return nullptr; - } - - // a malformed index throws parse_error.106/109; an - // index that is syntactically valid but cannot be - // represented (out_of_range.404/410) is treated like an - // out-of-range index below - typename BasicJsonType::size_type idx{}; - switch (parse_array_index(reference_token, idx)) - { - // the branches differ in their messages, not after JSON_THROW's expansion - // NOLINTNEXTLINE(bugprone-branch-clone) - case array_index_status::leading_zero: - 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: - 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::exceeds_size_type: - return nullptr; - case array_index_status::ok: - default: - break; - } - - if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) - { - return nullptr; - } - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: return nullptr; + } + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) + { + // "-" always fails the range check + return nullptr; + } + + // a malformed index throws parse_error.106/109; an + // index that is syntactically valid but cannot be + // represented (out_of_range.404/410) is treated like an + // out-of-range index below + typename BasicJsonType::size_type idx{}; + switch (parse_array_index(reference_token, idx)) + { + // the branches differ in their messages, not after JSON_THROW's expansion + // NOLINTNEXTLINE(bugprone-branch-clone) + case array_index_status::leading_zero: + 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: + 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::exceeds_size_type: + return nullptr; + case array_index_status::ok: + default: + break; + } + + if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) + { + return nullptr; + } + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + return nullptr; } } @@ -1239,63 +1210,48 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + const auto it = ptr->m_data.m_value.object->find(reference_token); + if (it == ptr->m_data.m_value.object->end()) { - const auto it = ptr->m_data.m_value.object->find(reference_token); - if (it == ptr->m_data.m_value.object->end()) - { - // we did not find the key in the object - return false; - } - - ptr = &it->second; - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) - { - // "-" always fails the range check - return false; - } - - // any parse failure (malformed index, or one that is syntactically - // valid but not representable as size_type) means the reference - // token cannot denote an existing array element -- contains() must - // not throw (see #5395), so it is treated as "not found" - typename BasicJsonType::size_type idx{}; - if (JSON_HEDLEY_UNLIKELY(parse_array_index(reference_token, idx) != array_index_status::ok)) - { - return false; - } - - if (idx >= ptr->m_data.m_value.array->size()) - { - // index out of range - return false; - } - - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - { - // we do not expect primitive values if there is still a - // reference token to process + // we did not find the key in the object return false; } + + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) + { + // "-" always fails the range check + return false; + } + + // any parse failure (malformed index, or one that is syntactically + // valid but not representable as size_type) means the reference + // token cannot denote an existing array element -- contains() must + // not throw (see #5395), so it is treated as "not found" + typename BasicJsonType::size_type idx{}; + if (JSON_HEDLEY_UNLIKELY(parse_array_index(reference_token, idx) != array_index_status::ok)) + { + return false; + } + + if (idx >= ptr->m_data.m_value.array->size()) + { + // index out of range + return false; + } + + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // we do not expect primitive values if there is still a + // reference token to process + return false; } } @@ -1323,10 +1279,7 @@ class json_pointer } // check if a nonempty reference string begins with slash - if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) - { - 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)); - } + check_begins_with_slash(reference_string); // every slash starts a reference token const std::size_t size = reference_string.size(); @@ -1367,9 +1320,7 @@ class json_pointer JSON_ASSERT(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 - if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || - (reference_token[pos + 1] != '0' && - reference_token[pos + 1] != '1'))) + if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(reference_token, pos))) { JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } @@ -1526,50 +1477,34 @@ class json_pointer auto hint = object.end(); const auto enter = [&stack, &path, &object, &hint](const BasicJsonType & v) { - switch (v.type()) + if (v.is_array()) { - case detail::value_t::array: + if (v.m_data.m_value.array->empty()) { - if (v.m_data.m_value.array->empty()) - { - // flatten empty array as null - add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); - } - else - { - stack.emplace_back(&v, object_const_iterator(), path.size()); - } - return; + // flatten empty array as null + add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); } - - case detail::value_t::object: + else { - if (v.m_data.m_value.object->empty()) - { - // flatten empty object as null - add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); - } - else - { - stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size()); - } - return; + stack.emplace_back(&v, object_const_iterator(), path.size()); } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: + } + else if (v.is_object()) + { + if (v.m_data.m_value.object->empty()) { - // add a primitive value with its reference string - add_flattened(object, hint, path, v, detail::priority_tag<2> {}); - return; + // flatten empty object as null + add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); } + else + { + stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size()); + } + } + else + { + // add a primitive value with its reference string + add_flattened(object, hint, path, v, detail::priority_tag<2> {}); } }; diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index f75c84a48..7ff828074 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -311,9 +312,7 @@ class binary_writer write_cbor_head(0x40, N); // step 2: write each element - oa.write_characters( - reinterpret_cast(j.m_data.m_value.binary->data()), - N); + write_bytes(j.m_data.m_value.binary->data(), N); break; } @@ -326,7 +325,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, "CBOR"); + throw_on_discarded(j, input_format_t::cbor); } } @@ -526,9 +525,7 @@ class binary_writer } // step 2: write the byte string - oa.write_characters( - reinterpret_cast(j.m_data.m_value.binary->data()), - N); + write_bytes(j.m_data.m_value.binary->data(), N); break; } @@ -542,7 +539,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, "MessagePack"); + throw_on_discarded(j, input_format_t::msgpack); } } @@ -626,7 +623,7 @@ class binary_writer case value_t::array: { bool prefix_required = true; - const bool write_closer = write_ubjson_start_array(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + const bool write_closer = write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); { const auto& array = *j.m_data.m_value.array; @@ -676,9 +673,7 @@ class binary_writer if (use_type) { - oa.write_characters( - reinterpret_cast(j.m_data.m_value.binary->data()), - j.m_data.m_value.binary->size()); + write_bytes(j.m_data.m_value.binary->data(), j.m_data.m_value.binary->size()); } else { @@ -704,7 +699,7 @@ class binary_writer } bool prefix_required = true; - const bool write_closer = write_ubjson_start_object(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + const bool write_closer = write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); write_ubjson_members(j, use_count, use_type, prefix_required, use_bjdata, bjdata_version, depth); @@ -718,7 +713,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, use_bjdata ? "BJData" : "UBJSON"); + throw_on_discarded(j, use_bjdata ? input_format_t::bjdata : input_format_t::ubjson); } } @@ -744,11 +739,11 @@ class binary_writer @brief throws because @a j is discarded and cannot be serialized @throw type_error.321 always */ - JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const char* format_name) + JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const input_format_t format) { static_cast(j); // unused when JSON_NOEXCEPTION is defined - static_cast(format_name); - JSON_THROW(type_error::create(exception_id::discarded_value_used, concat("cannot serialize discarded value to ", format_name), &j)); + static_cast(format); + JSON_THROW(type_error::create(exception_id::discarded_value_used, concat("cannot serialize discarded value to ", input_format_name(format)), &j)); } /// how the range of integer type @a NumberType relates to that of @a TargetType @@ -868,11 +863,11 @@ class binary_writer } /*! - @brief throws because the integer @a j is too large for @a format_name + @brief throws because the integer @a j is too large for @a format @throw out_of_range.407 always */ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE - static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range) + static void throw_integer_out_of_range(const BasicJsonType& j, const input_format_t format, const char* range) { // neither std::to_string(), which has no overload for integer types // wider than 64 bits, nor dump(), which would also instantiate the @@ -883,9 +878,9 @@ class binary_writer ? integer_to_string(j.m_data.m_value.number_unsigned, std::integral_constant()) : integer_to_string(j.m_data.m_value.number_integer, std::integral_constant()); static_cast(number); // unused when JSON_NOEXCEPTION is defined - static_cast(format_name); + static_cast(format); static_cast(range); - JSON_THROW(out_of_range::create(exception_id::integer_too_large, concat("integer number ", number, " cannot be represented by ", format_name, " as it does not fit ", range), &j)); + JSON_THROW(out_of_range::create(exception_id::integer_too_large, concat("integer number ", number, " cannot be represented by ", input_format_name(format), " as it does not fit ", range), &j)); } // The exceptions below are thrown from functions of their own, out of line: @@ -928,53 +923,56 @@ class binary_writer JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j)); } + /// @brief write @a size bytes starting at @a data to the output adapter + JSON_HEDLEY_ALWAYS_INLINE + void write_bytes(const void* data, const std::size_t size) + { + oa.write_characters(static_cast(data), size); + } + + /// @brief write the MessagePack prefix of an array or object with @a N + /// elements: the fix marker @a fix_marker | n, or @a marker_16 or + /// @a marker_32 followed by the length + JSON_HEDLEY_ALWAYS_INLINE + void write_msgpack_container_prefix(const std::size_t N, const BasicJsonType& j, + const std::uint8_t fix_marker, const std::uint8_t marker_16, const std::uint8_t marker_32) + { + const auto n = to_msgpack_length(N, j); + if (n <= 15) + { + // fixarray, fixmap + oa.write_character(to_char_type(static_cast(fix_marker | n))); + } + else if (n <= (std::numeric_limits::max)()) + { + // array 16, map 16 + oa.write_character(to_char_type(marker_16)); + write_number(static_cast(n)); + } + else + { + // array 32, map 32 + oa.write_character(to_char_type(marker_32)); + write_number(static_cast(n)); + } + } + + JSON_HEDLEY_ALWAYS_INLINE void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j) { - const auto n = to_msgpack_length(N, j); - if (n <= 15) - { - // fixarray - oa.write_character(to_char_type(static_cast(0x90 | n))); - } - else if (n <= (std::numeric_limits::max)()) - { - // array 16 - oa.write_character(to_char_type(0xDC)); - write_number(static_cast(n)); - } - else - { - // array 32 - oa.write_character(to_char_type(0xDD)); - write_number(static_cast(n)); - } + write_msgpack_container_prefix(N, j, 0x90, 0xDC, 0xDD); } + JSON_HEDLEY_ALWAYS_INLINE void write_msgpack_object_prefix(const std::size_t N, const BasicJsonType& j) { - const auto n = to_msgpack_length(N, j); - if (n <= 15) - { - // fixmap - oa.write_character(to_char_type(static_cast(0x80 | (n & 0xF)))); - } - else if (n <= (std::numeric_limits::max)()) - { - // map 16 - oa.write_character(to_char_type(0xDE)); - write_number(static_cast(n)); - } - else - { - // map 32 - oa.write_character(to_char_type(0xDF)); - write_number(static_cast(n)); - } + write_msgpack_container_prefix(N, j, 0x80, 0xDE, 0xDF); } - /// @brief a CBOR, MessagePack, or BON8 array or object whose elements - /// @ref write_cbor_iterative, @ref write_msgpack_iterative, or - /// @ref write_bon8_iterative is still writing + /// @brief a CBOR, MessagePack, BON8, or (as the base of @ref ubjson_frame) + /// UBJSON array or object whose elements @ref write_container_iterative, + /// @ref write_bon8_iterative, or @ref write_ubjson_iterative is still + /// writing struct binary_container_frame { explicit binary_container_frame(const BasicJsonType* value_) noexcept @@ -1010,19 +1008,82 @@ class binary_writer typename BasicJsonType::array_t::const_iterator array_it{}; }; + /// @brief the CBOR specifics of @ref write_container_iterative + struct cbor_walk + { + static void array_head(binary_writer& w, const BasicJsonType& j) + { + w.write_cbor_head(0x80, j.m_data.m_value.array->size()); + } + + static void object_head(binary_writer& w, const BasicJsonType& j) + { + w.write_cbor_head(0xA0, j.m_data.m_value.object->size()); + } + + static void scalar(binary_writer& w, const BasicJsonType& j) + { + w.write_cbor(j); + } + + static void string(binary_writer& w, const string_t& key, const BasicJsonType& context) + { + w.write_cbor_string(key, context); + } + + template + static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object) + { + w.write_cbor_key(k, object); + } + }; + + /// @brief the MessagePack specifics of @ref write_container_iterative + struct msgpack_walk + { + static void array_head(binary_writer& w, const BasicJsonType& j) + { + w.write_msgpack_array_prefix(j.m_data.m_value.array->size(), j); + } + + static void object_head(binary_writer& w, const BasicJsonType& j) + { + w.write_msgpack_object_prefix(j.m_data.m_value.object->size(), j); + } + + static void scalar(binary_writer& w, const BasicJsonType& j) + { + w.write_msgpack(j); + } + + static void string(binary_writer& w, const string_t& key, const BasicJsonType& context) + { + w.write_msgpack_string(key, context); + } + + template + static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object) + { + w.write_msgpack_key(k, object); + } + }; + /*! - @brief write @a j with @ref write_cbor, or write its header and push a - frame for @ref write_cbor_iterative to continue with its elements + @brief write @a j with @a Walk::scalar, or write its header and push a + frame for @ref write_container_iterative to continue with its + elements A scalar, and an empty array or object, are written out in full: there is - nothing below them for @ref write_cbor_iterative to come back to, so + nothing below them for @ref write_container_iterative to come back to, so nothing is pushed for them. */ - void write_cbor_value_or_push(const BasicJsonType& j, std::vector& stack) + template + JSON_HEDLEY_ALWAYS_INLINE + void write_container_value_or_push(const BasicJsonType& j, std::vector& stack) { if (j.is_array()) { - write_cbor_head(0x80, j.m_data.m_value.array->size()); + Walk::array_head(*this, j); if (!j.m_data.m_value.array->empty()) { stack.emplace_back(&j); @@ -1032,7 +1093,7 @@ class binary_writer if (j.is_object()) { - write_cbor_head(0xA0, j.m_data.m_value.object->size()); + Walk::object_head(*this, j); if (!j.m_data.m_value.object->empty()) { stack.emplace_back(&j); @@ -1040,22 +1101,26 @@ class binary_writer return; } - write_cbor(j); + Walk::scalar(*this, j); } /*! @brief write out @a root and everything below it without the call stack - Emits the same bytes as @ref write_cbor, keeping the containers it has + Emits the same bytes as the recursive writer, keeping the containers it has entered on an explicit stack instead of descending into them. Only reached for values nested deeper than @ref recursion_depth_limit, which is why it - is not written for speed. + is not written for speed. Shared by CBOR and MessagePack, which differ only + in the @a Walk policy (@ref cbor_walk, @ref msgpack_walk); BON8 has its own + walker, as it tracks an open string run and closes a container with a + marker. */ - void write_cbor_iterative(const BasicJsonType& root) + template + void write_container_iterative(const BasicJsonType& root) { - // only a container with elements is ever pushed; see write_cbor_value_or_push + // only a container with elements is ever pushed; see write_container_value_or_push std::vector stack; - write_cbor_value_or_push(root, stack); + write_container_value_or_push(root, stack); while (!stack.empty()) { @@ -1073,7 +1138,7 @@ class binary_writer // read the child before pushing: entering it can move every frame const BasicJsonType* child = &(*current.array_it); ++stack.back().array_it; - write_cbor_value_or_push(*child, stack); + write_container_value_or_push(*child, stack); } else { @@ -1085,110 +1150,53 @@ class binary_writer } // the key is written directly (not via a temporary basic_json), - // with the object as diagnostics context, as in write_cbor - write_cbor_key(current.object_it->first, *current.value); + // with the object as diagnostics context, as in the recursive writer + Walk::key(*this, current.object_it->first, *current.value); const BasicJsonType* child = &(current.object_it->second); ++stack.back().object_it; - write_cbor_value_or_push(*child, stack); + write_container_value_or_push(*child, stack); } } } - /*! - @brief write @a j with @ref write_msgpack, or write its header and push a - frame for @ref write_msgpack_iterative to continue with its elements - - @sa @ref write_cbor_value_or_push - */ - void write_msgpack_value_or_push(const BasicJsonType& j, std::vector& stack) + void write_cbor_iterative(const BasicJsonType& root) { - if (j.is_array()) - { - write_msgpack_array_prefix(j.m_data.m_value.array->size(), j); - if (!j.m_data.m_value.array->empty()) - { - stack.emplace_back(&j); - } - return; - } - - if (j.is_object()) - { - write_msgpack_object_prefix(j.m_data.m_value.object->size(), j); - if (!j.m_data.m_value.object->empty()) - { - stack.emplace_back(&j); - } - return; - } - - write_msgpack(j); + write_container_iterative(root); } - /*! - @brief write out @a root and everything below it without the call stack - - @sa @ref write_cbor_iterative - */ void write_msgpack_iterative(const BasicJsonType& root) { - std::vector stack; - write_msgpack_value_or_push(root, stack); - - while (!stack.empty()) - { - const binary_container_frame current = stack.back(); - - if (current.value->is_array()) - { - const auto& array = *current.value->m_data.m_value.array; - if (current.array_it == array.cend()) - { - stack.pop_back(); - continue; - } - - const BasicJsonType* child = &(*current.array_it); - ++stack.back().array_it; - write_msgpack_value_or_push(*child, stack); - } - else - { - const auto& object = *current.value->m_data.m_value.object; - if (current.object_it == object.cend()) - { - stack.pop_back(); - continue; - } - - // as in write_cbor_iterative, the key is written directly with - // the object as diagnostics context - write_msgpack_key(current.object_it->first, *current.value); - const BasicJsonType* child = &(current.object_it->second); - ++stack.back().object_it; - write_msgpack_value_or_push(*child, stack); - } - } + write_container_iterative(root); } - /// @return true when a closing ']' still has to be written after the elements - bool write_ubjson_start_array(const BasicJsonType& j, const bool use_count, const bool use_type, - const bool add_prefix, const bool use_bjdata, bool& prefix_required) + /*! + @brief write the start of an array (@a IsArray) or object: its opening + marker, the optimized type, and the count + + @return true when a closing ']' or '}' still has to be written after the + elements + */ + template + bool write_ubjson_start_container(const BasicJsonType& j, const bool use_count, const bool use_type, + const bool add_prefix, const bool use_bjdata, bool& prefix_required) { prefix_required = true; if (add_prefix) { - oa.write_character(to_char_type('[')); + oa.write_character(to_char_type(IsArray ? '[' : '{')); } - if (use_type && !j.m_data.m_value.array->empty()) + const std::size_t size = IsArray ? j.m_data.m_value.array->size() : j.m_data.m_value.object->size(); + + if (use_type && size != 0) { if (!use_count) { throw_size_marker_required(j); } const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); - const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + // the first element of an array has been compared with itself + const bool same_prefix = std::all_of(IsArray ? j.begin() + 1 : j.begin(), j.end(), [this, first_prefix, use_bjdata](const BasicJsonType & v) { return ubjson_prefix(v, use_bjdata) == first_prefix; @@ -1200,8 +1208,9 @@ class binary_writer // those, at one byte per element, so the result can be read back. // Objects are not affected: every element is preceded by its key. const bool valueless_type = (first_prefix == 'Z' || first_prefix == 'T' || first_prefix == 'F'); - const bool excessive_valueless = valueless_type - && j.m_data.m_value.array->size() > detail::max_valueless_container_size; + const bool excessive_valueless = IsArray + && valueless_type + && size > detail::max_valueless_container_size; if (same_prefix && !excessive_valueless && !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix))) @@ -1214,46 +1223,7 @@ class binary_writer if (use_count) { - write_ubjson_count(j.m_data.m_value.array->size(), use_bjdata); - } - - return !use_count; - } - - /// @return true when a closing '}' still has to be written after the elements - bool write_ubjson_start_object(const BasicJsonType& j, const bool use_count, const bool use_type, - const bool add_prefix, const bool use_bjdata, bool& prefix_required) - { - prefix_required = true; - if (add_prefix) - { - oa.write_character(to_char_type('{')); - } - - if (use_type && !j.m_data.m_value.object->empty()) - { - if (!use_count) - { - throw_size_marker_required(j); - } - const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); - const bool same_prefix = std::all_of(j.begin(), j.end(), - [this, first_prefix, use_bjdata](const BasicJsonType & v) - { - return ubjson_prefix(v, use_bjdata) == first_prefix; - }); - - if (same_prefix && !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix))) - { - prefix_required = false; - oa.write_character(to_char_type('$')); - oa.write_character(first_prefix); - } - } - - if (use_count) - { - write_ubjson_count(j.m_data.m_value.object->size(), use_bjdata); + write_ubjson_count(size, use_bjdata); } return !use_count; @@ -1281,21 +1251,12 @@ class binary_writer /// @brief an object or array @ref write_ubjson_iterative is still writing /// the elements of - struct ubjson_frame + struct ubjson_frame : binary_container_frame { ubjson_frame(const BasicJsonType* value_, const bool prefix_required_) noexcept - : value(value_) + : binary_container_frame(value_) , prefix_required(prefix_required_) - { - if (value->is_object()) - { - object_it = value->m_data.m_value.object->cbegin(); - } - else - { - array_it = value->m_data.m_value.array->cbegin(); - } - } + {} // declared for GCC's -Weffc++, which asks for them in a class with // pointer members and a non-trivial destructor; the exception @@ -1307,13 +1268,9 @@ class binary_writer ubjson_frame& operator=(ubjson_frame&&) = default; ~ubjson_frame() = default; - /// the array or object being written - const BasicJsonType* value; /// whether value's elements each carry their own type marker; an /// optimized ($type) container writes it once for all of them instead bool prefix_required; - typename BasicJsonType::object_t::const_iterator object_it{}; - typename BasicJsonType::array_t::const_iterator array_it{}; }; /*! @@ -1324,7 +1281,7 @@ class binary_writer elements of an optimized container, and everything below the top level, never repeat it) - @sa @ref write_cbor_value_or_push + @sa @ref write_container_value_or_push */ void write_ubjson_value_or_push(const BasicJsonType& j, const bool add_prefix, const bool use_count, const bool use_type, const bool use_bjdata, const bjdata_version_t bjdata_version, @@ -1347,11 +1304,11 @@ class binary_writer bool prefix_required = true; if (is_array) { - write_ubjson_start_array(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); } else { - write_ubjson_start_object(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); } const bool empty = is_array ? j.m_data.m_value.array->empty() : j.m_data.m_value.object->empty(); @@ -1361,7 +1318,7 @@ class binary_writer return; } - // write_ubjson_start_array/_object return !use_count, i.e. whether a + // write_ubjson_start_container returns !use_count, i.e. whether a // closer still has to be written; use_count is constant for the whole // document, so that is recomputed here instead of being carried along if (!use_count) @@ -1550,14 +1507,15 @@ class binary_writer { oa.write_character(to_char_type(element_type)); - if (error_handler == error_handler_t::keep || error_handler == error_handler_t::strict || is_valid_utf8(name)) + if (JSON_HEDLEY_LIKELY(error_handler == error_handler_t::keep || error_handler == error_handler_t::strict)) { - oa.write_characters(reinterpret_cast(name.data()), name.size()); + write_bytes(name.data(), name.size()); } else { - const string_t sanitized = sanitize_utf8(name, error_handler); - oa.write_characters(reinterpret_cast(sanitized.data()), sanitized.size()); + string_t storage; + const string_t& written = sanitize_utf8_for_bson_write(name, storage); + write_bytes(written.data(), written.size()); } // the terminating null byte is written explicitly rather than taken @@ -1633,16 +1591,11 @@ class binary_writer { write_bson_entry_header(name, 0x02); - const bool sanitize = error_handler != error_handler_t::keep - && error_handler != error_handler_t::strict - && !is_valid_utf8(value); - const string_t sanitized = sanitize ? sanitize_utf8(value, error_handler) : string_t{}; - const string_t& written = sanitize ? sanitized : value; + string_t storage; + const string_t& written = sanitize_utf8_for_bson_write(value, storage); write_number(to_bson_length(written.size() + 1ul), true); - oa.write_characters( - reinterpret_cast(written.data()), - written.size()); + write_bytes(written.data(), written.size()); // the terminating null byte is written explicitly rather than taken // from the buffer, so that string_t::data() need not be null-terminated oa.write_character(to_char_type(0x00)); @@ -1748,7 +1701,7 @@ class binary_writer write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); - oa.write_characters(reinterpret_cast(value.data()), value.size()); + write_bytes(value.data(), value.size()); } /*! @@ -1778,14 +1731,14 @@ class binary_writer case value_t::number_integer: if (JSON_HEDLEY_UNLIKELY(!integer_fits(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "BSON", "int64"); + throw_integer_out_of_range(j, input_format_t::bson, "int64"); } return calc_bson_integer_size(static_cast(j.m_data.m_value.number_integer)); case value_t::number_unsigned: if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "BSON", "uint64"); + throw_integer_out_of_range(j, input_format_t::bson, "uint64"); } return calc_bson_unsigned_size(conditional_static_cast(j.m_data.m_value.number_unsigned)); @@ -1796,7 +1749,7 @@ class binary_writer return 0ul; case value_t::discarded: - throw_on_discarded(j, "BSON"); + throw_on_discarded(j, input_format_t::bson); // LCOV_EXCL_START case value_t::object: @@ -1960,7 +1913,7 @@ class binary_writer // the terminating null byte is written explicitly rather than // taken from the buffer, so that string_t::data() need not be // null-terminated - oa.write_characters(reinterpret_cast(value.data()), value.size()); + write_bytes(value.data(), value.size()); oa.write_character(to_char_type(0x00)); return true; } @@ -1977,7 +1930,7 @@ class binary_writer p += sizeof(std::int32_t); *p++ = value.has_subtype() ? to_char_type(static_cast(value.subtype())) : to_char_type(0x00); oa.commit(static_cast(p - out)); - oa.write_characters(reinterpret_cast(value.data()), value.size()); + write_bytes(value.data(), value.size()); return true; } @@ -2025,7 +1978,7 @@ class binary_writer return write_bson_null(name); case value_t::discarded: - throw_on_discarded(j, "BSON"); + throw_on_discarded(j, input_format_t::bson); // LCOV_EXCL_START case value_t::object: @@ -2806,7 +2759,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "CBOR", "uint64"); + throw_integer_out_of_range(j, input_format_t::cbor, "uint64"); } write_cbor_head(0x00, conditional_static_cast(j.m_data.m_value.number_unsigned)); return; @@ -2816,7 +2769,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::cbor, "[-2^64, 2^64-1]"); } // CBOR does not differentiate between positive signed // integers and unsigned integers @@ -2827,7 +2780,7 @@ class binary_writer // a negative integer n is encoded as -1 - n if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(-1 - j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::cbor, "[-2^64, 2^64-1]"); } write_cbor_head(0x20, static_cast(-1 - j.m_data.m_value.number_integer)); } @@ -2903,7 +2856,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "MessagePack", "uint64"); + throw_integer_out_of_range(j, input_format_t::msgpack, "uint64"); } write_msgpack_unsigned(static_cast(j.m_data.m_value.number_unsigned)); return; @@ -2913,7 +2866,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::msgpack, "[-2^63, 2^64-1]"); } // MessagePack does not differentiate between positive // signed integers and unsigned integers. @@ -2923,7 +2876,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_min(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::msgpack, "[-2^63, 2^64-1]"); } if (j.m_data.m_value.number_integer >= -32) { @@ -3057,40 +3010,37 @@ class binary_writer } /*! - @brief write an object key as a CBOR text string + @brief write an object key as a CBOR or MessagePack string (@a Walk) 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::value, int> = 0> + void write_object_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context) + { + Walk::string(*this, key, context); + } + + template>::value, int> = 0> + void write_object_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/) + { + Walk::scalar(*this, BasicJsonType(key)); + } + + /// @brief write an object key as a CBOR text string void write_cbor_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context) { - write_cbor_string(key, context); + write_object_key(key, context); } - template < typename Key = typename BasicJsonType::object_t::key_type, - enable_if_t < !std::is_convertible::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::value, int> = 0> + /// @brief write an object key as a MessagePack str 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::value, int > = 0 > - void write_msgpack_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/) - { - write_msgpack(BasicJsonType(key)); + write_object_key(key, context); } /// the longest string that is written, with its head of one or two @@ -3165,9 +3115,7 @@ class binary_writer write_cbor_head(0x60, sanitized.size()); // step 2: write the string - oa.write_characters( - reinterpret_cast(sanitized.data()), - sanitized.size()); + write_bytes(sanitized.data(), sanitized.size()); } ///////////// @@ -3248,9 +3196,7 @@ class binary_writer } // step 2: write the string - oa.write_characters( - reinterpret_cast(sanitized.data()), - sanitized.size()); + write_bytes(sanitized.data(), sanitized.size()); } //////////// @@ -3533,9 +3479,7 @@ class binary_writer oa.write_character(to_char_type('S')); } write_number_with_ubjson_prefix(sanitized.size(), true, use_bjdata); - oa.write_characters( - reinterpret_cast(sanitized.data()), - sanitized.size()); + write_bytes(sanitized.data(), sanitized.size()); } /*! @@ -4335,7 +4279,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "BON8", "int64"); + throw_integer_out_of_range(j, input_format_t::bon8, "int64"); } write_bon8_integer(static_cast(j.m_data.m_value.number_unsigned)); string_open = false; @@ -4346,7 +4290,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "BON8", "int64"); + throw_integer_out_of_range(j, input_format_t::bon8, "int64"); } write_bon8_integer(static_cast(j.m_data.m_value.number_integer)); string_open = false; @@ -4414,7 +4358,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, "BON8"); + throw_on_discarded(j, input_format_t::bon8); } } @@ -4425,7 +4369,7 @@ class binary_writer A scalar, and an empty array or object, are written out in full and not pushed. - @sa @ref write_cbor_value_or_push + @sa @ref write_container_value_or_push */ void write_bon8_value_or_push(const BasicJsonType& j, bool& string_open, std::vector& stack) { @@ -4811,7 +4755,7 @@ class binary_writer } else { - oa.write_characters(reinterpret_cast(s.data()), s.size()); + write_bytes(s.data(), s.size()); string_open = true; } } @@ -4902,6 +4846,31 @@ class binary_writer } } + /*! + @brief the write-pass counterpart of @ref sanitize_utf8_for_write for BSON + + The size pass (@ref calc_bson_entry_header_size, @ref + calc_bson_string_size) has already validated @a s, and thrown for @ref + error_handler_t::strict if it was ill-formed. Here, @ref + error_handler_t::strict therefore behaves like @ref error_handler_t::keep, + and @ref error_handler_t::replace / @ref error_handler_t::ignore produce + exactly the bytes the size pass measured. Hence no context is needed. + + @param[in] s the string (value or object key) to write + @param[out] storage backing storage for a sanitized copy + + @return a reference to @a s, or to @a storage once it holds a sanitized copy + */ + const string_t& sanitize_utf8_for_bson_write(const string_t& s, string_t& storage) const + { + if (error_handler == error_handler_t::keep || error_handler == error_handler_t::strict || is_valid_utf8(s)) + { + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call + } + storage = sanitize_utf8(s, error_handler); + return storage; + } + /// deleted: anything but a string_t would bind a temporary that dies before the returned reference is used template < typename T, enable_if_t < !std::is_same::value, int > = 0 > const string_t& sanitize_utf8_for_write(const T& /*s*/, const BasicJsonType& /*context*/, string_t& /*storage*/) const = delete; // NOLINT(hicpp-use-equals-delete,modernize-use-equals-delete): a private helper's guard, not part of the interface diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 5eb9906a1..598fda98a 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -966,6 +966,28 @@ class serializer } } + /// write out the bytes pending in @ref string_buffer and reset @a bytes + JSON_HEDLEY_ALWAYS_INLINE void emit_pending(std::size_t& bytes) + { + if (bytes != 0) + { + put_buffer(string_buffer, bytes); + bytes = 0; + } + } + + /// write buffer and reset index if fewer than 13 bytes are left, as this + /// is the maximal number of bytes to be written ("\uxxxx\uxxxx\0") for one + /// code point + JSON_HEDLEY_ALWAYS_INLINE void reserve_string_buffer(std::size_t& bytes) + { + if (string_buffer.size() - bytes < 13) + { + put_buffer(string_buffer, bytes); + bytes = 0; + } + } + /*! @brief worker for @ref dump_escaped @@ -1024,11 +1046,7 @@ class serializer { // emit any bytes still pending in string_buffer first to // preserve output order, then write the run directly - if (bytes != 0) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + emit_pending(bytes); put_run(s, i, run); bytes_after_last_accept = 0; undumped_chars = 0; @@ -1049,11 +1067,10 @@ class serializer { // emit any bytes still pending in string_buffer first to // preserve output order - if (bytes != 0) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + emit_pending(bytes); + // deliberately not put_buffer(): a fixed 8-byte memcpy + // has no asserts and no write-through branch, which + // matters on the hot path const auto& escape = ascii_escapes()[data[i]]; if (JSON_HEDLEY_UNLIKELY(write_buffer.size() - write_buffer_pos < escape.size())) { @@ -1128,14 +1145,7 @@ class serializer string_buffer[bytes++] = s[i]; } - // write buffer and reset index; there must be 13 bytes - // left, as this is the maximal number of bytes to be - // written ("\uxxxx\uxxxx\0") for one code point - if (string_buffer.size() - bytes < 13) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + reserve_string_buffer(bytes); // remember the byte position of this accept bytes_after_last_accept = bytes; @@ -1187,14 +1197,7 @@ class serializer string_buffer[bytes++] = '\xBD'; } - // write buffer and reset index; there must be 13 bytes - // left, as this is the maximal number of bytes to be - // written ("\uxxxx\uxxxx\0") for one code point - if (string_buffer.size() - bytes < 13) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + reserve_string_buffer(bytes); bytes_after_last_accept = bytes; } @@ -1227,14 +1230,7 @@ class serializer string_buffer[bytes++] = s[i]; } - // write buffer and reset index; there must be 13 bytes - // left, as this is the maximal number of bytes to be - // written ("\uxxxx\uxxxx\0") for one code point - if (string_buffer.size() - bytes < 13) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + reserve_string_buffer(bytes); bytes_after_last_accept = bytes; undumped_chars = 0; @@ -1600,30 +1596,17 @@ class serializer */ static const std::array& ascii_copyable() noexcept { - static const std::array copyable = - { - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 0x50 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 0x70 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0 - } - }; + // constant-initialized from the predicate: no runtime loop, no guard + static constexpr std::array copyable = make_ascii_copyable(make_index_sequence<256> {}); return copyable; } + template + static constexpr std::array make_ascii_copyable(index_sequence /*unused*/) noexcept + { + return {{ static_cast(is_ascii_copyable(static_cast(Is)) ? 1 : 0)... }}; + } + /*! @brief the JSON form of each character below 0x60 @@ -1752,7 +1735,6 @@ class serializer */ JSON_HEDLEY_NEVER_INLINE std::size_t write_u_escapes(const unsigned char* data, std::size_t n) { - constexpr const char* nibble_to_hex = "0123456789abcdef"; std::size_t i = 0; while (i < n && data[i] >= 0x80) { @@ -1789,26 +1771,21 @@ class serializer char* d = write_buffer.data() + write_buffer_pos; if (codepoint > 0xFFFFu) { - d = put_u_escape(d, static_cast(0xD7C0u + (codepoint >> 10u)), nibble_to_hex); + d = put_u_escape(d, static_cast(0xD7C0u + (codepoint >> 10u))); codepoint = 0xDC00u + (codepoint & 0x3FFu); } - d = put_u_escape(d, static_cast(codepoint), nibble_to_hex); + d = put_u_escape(d, static_cast(codepoint)); write_buffer_pos = static_cast(d - write_buffer.data()); i += length; } return i; } - static char* put_u_escape(char* d, const std::uint16_t codeunit, const char* nibble_to_hex) noexcept + static char* put_u_escape(char* d, const std::uint16_t codeunit) noexcept { - const auto unit = static_cast(codeunit); d[0] = '\\'; d[1] = 'u'; - d[2] = nibble_to_hex[(unit >> 12u) & 0x0Fu]; - d[3] = nibble_to_hex[(unit >> 8u) & 0x0Fu]; - d[4] = nibble_to_hex[(unit >> 4u) & 0x0Fu]; - d[5] = nibble_to_hex[unit & 0x0Fu]; - return d + 6; + return hex4_lower(codeunit, d + 2); } /*! @@ -1817,7 +1794,7 @@ class serializer * Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the * string escaping hot path. It writes exactly six characters ('\\', 'u' and * four hex digits) at position @a pos of @a string_buffer via a nibble - * lookup table, avoiding the format-string parsing and locale machinery of + * lookup table (@ref hex4_lower), avoiding the format-string parsing and locale machinery of * `snprintf`. Advances @a pos by the number of bytes written (6). * * @param[in] pos position in @a string_buffer to write at; there must @@ -1827,13 +1804,9 @@ class serializer void write_u_escape(std::size_t& pos, std::uint16_t codeunit) noexcept { JSON_ASSERT(string_buffer.size() - pos >= 6); - constexpr const char* nibble_to_hex = "0123456789abcdef"; string_buffer[pos + 0] = '\\'; string_buffer[pos + 1] = 'u'; - string_buffer[pos + 2] = nibble_to_hex[(codeunit >> 12u) & 0x0Fu]; - string_buffer[pos + 3] = nibble_to_hex[(codeunit >> 8u) & 0x0Fu]; - string_buffer[pos + 4] = nibble_to_hex[(codeunit >> 4u) & 0x0Fu]; - string_buffer[pos + 5] = nibble_to_hex[codeunit & 0x0Fu]; + hex4_lower(codeunit, &string_buffer[pos + 2]); pos += 6; } diff --git a/include/nlohmann/detail/string_utils.hpp b/include/nlohmann/detail/string_utils.hpp index e54a56da6..9b44623a5 100644 --- a/include/nlohmann/detail/string_utils.hpp +++ b/include/nlohmann/detail/string_utils.hpp @@ -48,6 +48,24 @@ inline std::string hex_byte(const std::uint8_t byte) return result; } +/*! +@brief write a UTF-16 code unit as four lowercase hexadecimal digits + +The lowercase counterpart of hex_byte(), used for the `\\uXXXX` escapes of the +serializers, whose output is lowercase. Writes exactly four characters to +@a out and returns the pointer past them. +*/ +JSON_HEDLEY_ALWAYS_INLINE char* hex4_lower(const std::uint16_t codeunit, char* out) noexcept +{ + constexpr const char* nibble_to_hex = "0123456789abcdef"; + const unsigned int unit = codeunit; + out[0] = nibble_to_hex[(unit >> 12u) & 0x0Fu]; + out[1] = nibble_to_hex[(unit >> 8u) & 0x0Fu]; + out[2] = nibble_to_hex[(unit >> 4u) & 0x0Fu]; + out[3] = nibble_to_hex[unit & 0x0Fu]; + return out + 4; +} + /////////////////// // UTF-8 encoding // /////////////////// @@ -103,6 +121,66 @@ void encode_utf8(std::uint32_t cp, const Out& out) } } +/*! +@brief the shape of a well-formed UTF-8 sequence, by its lead byte + +@a length is 1 for ASCII, 2 to 4 for a valid lead byte, and 0 for a byte that +cannot start a well-formed sequence (a continuation byte, 0xC0, 0xC1, or 0xF5 +to 0xFF). For a multibyte sequence, the second byte must lie in +[@a second_lo, @a second_hi], and every later byte in [0x80, 0xBF] +(RFC 3629, section 4; Unicode Table 3-7). The narrower second-byte ranges +exclude overlong forms (0xE0, 0xF0), surrogates (0xED), and code points +beyond U+10FFFF (0xF4). +*/ +struct utf8_sequence_shape +{ + std::uint8_t length; + std::uint8_t second_lo; + std::uint8_t second_hi; +}; + +/// @return the shape of a UTF-8 sequence starting with @a lead, see utf8_sequence_shape +inline utf8_sequence_shape utf8_shape(const std::uint8_t lead) noexcept +{ + if (lead < 0x80u) + { + return {1, 0, 0}; + } + if (lead < 0xC2u) + { + return {0, 0, 0}; + } + if (lead <= 0xDFu) + { + return {2, 0x80u, 0xBFu}; + } + if (lead == 0xE0u) + { + return {3, 0xA0u, 0xBFu}; + } + if (lead == 0xEDu) + { + return {3, 0x80u, 0x9Fu}; + } + if (lead <= 0xEFu) + { + return {3, 0x80u, 0xBFu}; + } + if (lead == 0xF0u) + { + return {4, 0x90u, 0xBFu}; + } + if (lead <= 0xF3u) + { + return {4, 0x80u, 0xBFu}; + } + if (lead == 0xF4u) + { + return {4, 0x80u, 0x8Fu}; + } + return {0, 0, 0}; +} + /////////////////// // UTF-8 decoding // /////////////////// @@ -118,7 +196,7 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally written by Björn Hoehrmann. See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. -The library checks UTF-8 well-formedness (RFC 3629, section 4) in three +The library checks UTF-8 well-formedness (RFC 3629, section 4) in five places, which differ in speed, diagnostics, and how they read the input: - decode() below: the serializer, to escape and, in strict mode, reject @@ -132,10 +210,15 @@ places, which differ in speed, diagnostics, and how they read the input: bulk string scan, the bulk path of the BON8 reader, and the BON8 writer. They must accept exactly what the lexer's switch accepts. - the byte path of binary_reader::get_bon8_string(): BON8 input without bulk - access, and the bytes the bulk path leaves to it. + access, and the bytes the bulk path leaves to it. It keeps its own ranges: + taking them from utf8_shape() made BON8 decoding 2-3% slower (#5891). +- check_utf8() in detail/view/edit.hpp: strings stored in an editable + json_document, with the index of the offending byte. It also uses + utf8_shape(). -All four must accept the same set of sequences, so a change to one needs a -matching change to the others. +All five must accept the same set of sequences, which the test "UTF-8 +validators accept the same sequences" in unit-class_lexer.cpp checks +exhaustively against decode() and utf8_shape(). @param[in,out] state the current decoder state @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) diff --git a/include/nlohmann/detail/value_t.hpp b/include/nlohmann/detail/value_t.hpp index 9f64cb90b..6cdff395b 100644 --- a/include/nlohmann/detail/value_t.hpp +++ b/include/nlohmann/detail/value_t.hpp @@ -177,6 +177,36 @@ inline bool owns_memory(const value_t t) noexcept } } +/*! +@brief whether a value of the given type is neither an object nor an array + +Unlike basic_json::is_primitive(), this includes value_t::discarded: it names +the types that iterators walk with a primitive_iterator_t (a single value, or no +value for null and discarded) and that JSON Pointer resolution and the +serializer treat as leaves. Every enumerator is listed so that -Wswitch-enum +flags this function when a new type is added. +*/ +inline bool is_primitive(const value_t t) noexcept +{ + switch (t) + { + case value_t::object: + case value_t::array: + return false; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return true; + } +} + /*! @brief compare an integer with a floating point number without precision loss diff --git a/include/nlohmann/detail/view/edit.hpp b/include/nlohmann/detail/view/edit.hpp index ec18793c9..bad8d9da3 100644 --- a/include/nlohmann/detail/view/edit.hpp +++ b/include/nlohmann/detail/view/edit.hpp @@ -51,11 +51,6 @@ struct first_true : std::integral_constant < int, 1 + firs inline void check_utf8(const char* s, std::size_t n) { const auto* const p = reinterpret_cast(s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) - const auto hex = [](unsigned char c) - { - constexpr const char* digits = "0123456789ABCDEF"; - return std::string{digits[c >> 4u], digits[c & 0xFu]}; - }; for (std::size_t i = 0; i < n;) { const unsigned char c = p[i]; @@ -64,39 +59,22 @@ inline void check_utf8(const char* s, std::size_t n) ++i; continue; } - std::size_t len = 0; - unsigned char lo = 0x80; - unsigned char hi = 0xBF; - if (c >= 0xC2 && c <= 0xDF) + const utf8_sequence_shape shape = utf8_shape(c); + const std::size_t len = shape.length; + if (len == 0) { - len = 2; - } - else if (c >= 0xE0 && c <= 0xEF) - { - len = 3; - lo = c == 0xE0 ? 0xA0 : 0x80; - hi = c == 0xED ? 0x9F : 0xBF; - } - else if (c >= 0xF0 && c <= 0xF4) - { - len = 4; - lo = c == 0xF0 ? 0x90 : 0x80; - hi = c == 0xF4 ? 0x8F : 0xBF; - } - else - { - throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex(c))); + throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_byte(c))); } for (std::size_t k = 1; k < len; ++k) { if (i + k == n) { - throw_type_error(316, concat("incomplete UTF-8 string; last byte: 0x", hex(p[n - 1]))); + throw_type_error(exception_id::invalid_utf8, concat("incomplete UTF-8 string; last byte: 0x", hex_byte(p[n - 1]))); } const unsigned char b = p[i + k]; - if (b < (k == 1 ? lo : 0x80) || b > (k == 1 ? hi : 0xBF)) + if (b < (k == 1 ? shape.second_lo : 0x80) || b > (k == 1 ? shape.second_hi : 0xBF)) { - throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex(b))); + throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex_byte(b))); } } i += len; @@ -144,7 +122,7 @@ class editor node* const o = own(object); if (o->kind != static_cast(value_t::object) && o->kind != static_cast(value_t::null)) { - throw_type_error(305, "cannot use operator[] with a string argument with ", object.type_name()); + throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name()); } check_utf8(key.data(), key.size()); const encoded e = encode(std::forward(value)); @@ -195,7 +173,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array)) { - throw_type_error(305, "cannot use operator[] with a numeric argument with ", array.type_name()); + throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name()); } check_index(idx, a->len); const encoded e = encode(std::forward(value)); @@ -212,7 +190,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array) && a->kind != static_cast(value_t::null)) { - throw_type_error(308, "cannot use push_back() with ", array.type_name()); + throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name()); } const encoded e = encode(std::forward(value)); if (a->kind == static_cast(value_t::null)) @@ -236,7 +214,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array)) { - throw_type_error(309, "cannot use insert() with ", array.type_name()); + throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name()); } check_index(idx, a->len + 1); const encoded e = encode(std::forward(value)); @@ -256,7 +234,7 @@ class editor node* const o = own(object); if (o->kind != static_cast(value_t::object)) { - throw_type_error(307, "cannot use erase() with ", object.type_name()); + throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name()); } for (const node* k = nav::first(m_doc, o), *end = nav::end(m_doc, o); k != end; k = document_data::after(k + 1)) { @@ -274,7 +252,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array)) { - throw_type_error(307, "cannot use erase() with ", array.type_name()); + throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name()); } check_index(idx, a->len); node* const h = block_of(m_doc, a, 0); @@ -297,7 +275,7 @@ class editor { if (NLOHMANN_VIEW_UNLIKELY(v.m_doc != &m_doc || v.m_node == nullptr)) { - throw_invalid_iterator(202, "view does not belong to this document"); + throw_invalid_iterator(exception_id::iterator_from_other_value, "view does not belong to this document"); } edit_state_of(m_doc); return const_cast(v.m_node); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to this document @@ -307,7 +285,7 @@ class editor { if (idx >= limit) { - throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range")); + throw_array_index_out_of_range(idx); } } @@ -448,7 +426,7 @@ class editor { if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr)) { - throw_type_error(302, "type must be a value, but is ", "discarded"); + throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded"); } encoded r; if (!is_container(*v.m_node)) @@ -598,7 +576,7 @@ class editor { if (NLOHMANN_VIEW_UNLIKELY(len >= 0xFFFFFFFFu)) { - throw_out_of_range(416, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE + throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE } node n = plain_node(value_t::string); n.flags = static_cast(n.flags | node_flags::edited); @@ -673,12 +651,12 @@ class editor return string_node(s.data(), s.size()); } case value_t::binary: - throw_type_error(319, "cannot store a binary value in a json_document", ""); + throw_type_error(exception_id::document_binary_value, "cannot store a binary value in a json_document", ""); case value_t::discarded: case value_t::object: case value_t::array: default: - throw_type_error(302, "type must be a value, but is ", "discarded"); + throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded"); } } diff --git a/include/nlohmann/detail/view/edit_storage.hpp b/include/nlohmann/detail/view/edit_storage.hpp index 614a0fb0d..9a45bfebd 100644 --- a/include/nlohmann/detail/view/edit_storage.hpp +++ b/include/nlohmann/detail/view/edit_storage.hpp @@ -84,7 +84,7 @@ inline std::size_t text_capacity(std::size_t cap, std::size_t used, std::size_t constexpr std::size_t limit = 0xFFFFFFFFu; if (NLOHMANN_VIEW_UNLIKELY(used > limit || n > limit - used)) { - throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document"); + throw_out_of_range(exception_id::document_too_large, "edits of 4 GiB or more are not supported by json_document"); } const std::size_t needed = used + n; const std::size_t wanted = needed + (std::min)(limit - needed, std::size_t{256}); @@ -135,7 +135,7 @@ inline void reserve_moved(document_data& d) document_data::edit_state& e = edit_state_of(d); if (e.moved.size() >= 0xFFFFFFFFu) { - throw_out_of_range(416, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE + throw_out_of_range(exception_id::document_too_large, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE } if (e.moved.size() == e.moved.capacity() || e.moved_cap.size() == e.moved_cap.capacity()) { diff --git a/include/nlohmann/detail/view/errors.hpp b/include/nlohmann/detail/view/errors.hpp index ca790a13d..590bb2edb 100644 --- a/include/nlohmann/detail/view/errors.hpp +++ b/include/nlohmann/detail/view/errors.hpp @@ -25,32 +25,52 @@ namespace view // Exceptions are thrown out of line, so that the accessors that may throw stay // small enough to be inlined. -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const char* prefix, const char* type) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const char* prefix, const char* type) { NLOHMANN_VIEW_THROW(type_error::create(id, concat(prefix, type), nullptr)); } -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const std::string& msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const std::string& msg) { NLOHMANN_VIEW_THROW(type_error::create(id, msg, nullptr)); } -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(int id, const std::string& msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(exception_id id, const std::string& msg) { NLOHMANN_VIEW_THROW(out_of_range::create(id, msg, nullptr)); } -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(int id, const char* msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(exception_id id, const char* msg) { NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr)); } /// a parse error without a position (as those of json_pointer) -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(int id, const std::string& msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(exception_id id, const std::string& msg) { NLOHMANN_VIEW_THROW(parse_error::create(id, 0, msg, nullptr)); } +/// out_of_range.401: the array index @a idx is not below the size +template +[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_out_of_range(const Index idx) +{ + throw_out_of_range(exception_id::array_index_out_of_range, concat("array index ", std::to_string(idx), " is out of range")); +} + +/// out_of_range.402: the JSON Pointer token '-' of an array of @a size elements +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_array_index_past_end(std::size_t size) +{ + throw_out_of_range(exception_id::pointer_past_the_end_index, concat("array index '-' (", std::to_string(size), ") is out of range")); +} + +/// out_of_range.403: the object has no member @a key +template +[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_key_not_found(const Key& key) +{ + throw_out_of_range(exception_id::key_not_found, concat("key '", key, "' not found")); +} + /// SAX handler of the error replay: accepts every event without storing it, /// and throws the exception the parser reports template @@ -149,7 +169,7 @@ template if (f.code == error_code::input_too_large) { // (the limit is detail::view::max_input_size(): 4 GiB minus 16 bytes) - NLOHMANN_VIEW_THROW(out_of_range::create(416, "input of 4294967280 bytes or more is not supported by json_document", nullptr)); + NLOHMANN_VIEW_THROW(out_of_range::create(exception_id::document_too_large, "input of 4294967280 bytes or more is not supported by json_document", nullptr)); } throwing_sax sax; const bool accepted = BasicJsonType::sax_parse(src, src + size, &sax, input_format_t::json, true, ignore_comments, ignore_trailing_commas); @@ -169,7 +189,7 @@ template } } pos.chars_read_current_line = off + 1 - line_start; - NLOHMANN_VIEW_THROW(parse_error::create(101, pos, "syntax error while parsing value", nullptr)); + NLOHMANN_VIEW_THROW(parse_error::create(exception_id::syntax_error, pos, "syntax error while parsing value", nullptr)); // LCOV_EXCL_STOP } diff --git a/include/nlohmann/detail/view/image.hpp b/include/nlohmann/detail/view/image.hpp index 702a55530..af9994712 100644 --- a/include/nlohmann/detail/view/image.hpp +++ b/include/nlohmann/detail/view/image.hpp @@ -186,7 +186,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v inline std::vector save_image(const document_data& d) { #if !NLOHMANN_VIEW_LITTLE_ENDIAN - throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE + throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE #endif const std::size_t arena_size = d.arena_size; std::vector compacted; @@ -203,7 +203,7 @@ inline std::vector save_image(const document_data& d) if (NLOHMANN_VIEW_UNLIKELY(text_size >= image_limit || total_arena >= image_limit || count >= image_limit)) { // LCOV_EXCL_START (4 GiB) - throw_out_of_range(416, "images of 4 GiB or more are not supported by json_document"); + throw_out_of_range(exception_id::document_too_large, "images of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_STOP } image_header h{}; @@ -660,7 +660,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha [[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_image(const char* what) { - throw_parse_error(116, concat("invalid json_document image: ", what)); + throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what)); } /// Read an image into d. The text and the decoded strings stay in the image; @@ -668,7 +668,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check) { #if !NLOHMANN_VIEW_LITTLE_ENDIAN - throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE + throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE #endif if (image == nullptr || size < sizeof(image_header)) { diff --git a/include/nlohmann/detail/view/iterator.hpp b/include/nlohmann/detail/view/iterator.hpp index 069f50fd1..9d8246210 100644 --- a/include/nlohmann/detail/view/iterator.hpp +++ b/include/nlohmann/detail/view/iterator.hpp @@ -110,7 +110,7 @@ class view_iterator { if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0)) { - throw_invalid_iterator(207, "cannot use key() for non-object iterators"); + throw_invalid_iterator(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators"); } return string_view_t(m_doc->str(*m_pos), m_pos->len); } diff --git a/include/nlohmann/detail/view/materialize.hpp b/include/nlohmann/detail/view/materialize.hpp index 006bdd0f3..62adb30a9 100644 --- a/include/nlohmann/detail/view/materialize.hpp +++ b/include/nlohmann/detail/view/materialize.hpp @@ -143,6 +143,8 @@ BasicJsonType materialize(const document_data& d, const node* n) case value_t::binary: case value_t::discarded: default: + // false positive: the parser's frame stack points into its own storage (never null) + // @infer-ignore NULLPTR_DEREFERENCE sax.null(); break; } diff --git a/include/nlohmann/detail/view/pointer.hpp b/include/nlohmann/detail/view/pointer.hpp index fb957b604..be5aa14de 100644 --- a/include/nlohmann/detail/view/pointer.hpp +++ b/include/nlohmann/detail/view/pointer.hpp @@ -32,51 +32,15 @@ enum class pointer_mode contains, ///< as basic_json::contains(): no exceptions at all }; -/// the outcome of reading an array index from a reference token -enum class index_status -{ - ok, - leading_zero, ///< parse_error.106 - not_number, ///< parse_error.109 - unresolved, ///< out_of_range.404 - too_large, ///< out_of_range.410 -}; +/// the outcome of reading an array index: detail::parse_array_index(), shared with json_pointer +using index_status = detail::array_index_status; /// reads an array index like json_pointer::array_index (RFC 6901, Sect. 4), /// but reports errors instead of throwing them template index_status array_index(const StringType& s, std::size_t& idx) noexcept { - // as basic_json: the empty token and "-" (past the last element) are - // unresolved rather than not a number - if (s.empty() || (s.size() == 1 && s[0] == '-')) - { - return index_status::unresolved; - } - if (s[0] < '0' || s[0] > '9') - { - return index_status::not_number; - } - if (s.size() > 1 && s[0] == '0') - { - return index_status::leading_zero; - } - std::uint64_t v = 0; - for (const auto c : s) - { - const auto d = static_cast(static_cast(c)) - '0'; - if (d > 9 || v > ((std::numeric_limits::max)() - d) / 10) - { - return index_status::unresolved; // not a number, or beyond unsigned long long - } - v = (v * 10) + d; - } - if (v >= (std::numeric_limits::max)()) // (std::size_t converts to std::uint64_t implicitly) - { - return index_status::too_large; - } - idx = static_cast(v); - return index_status::ok; + return detail::parse_array_index(s, idx); } /// throws the exception json_pointer::array_index throws for this status @@ -86,15 +50,15 @@ template switch (status) { case index_status::leading_zero: - throw_parse_error(106, concat("array index '", s, "' must not begin with '0'")); - case index_status::not_number: - throw_parse_error(109, concat("array index '", s, "' is not a number")); - case index_status::too_large: - throw_out_of_range(410, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE + throw_parse_error(exception_id::pointer_index_leading_zero, concat("array index '", s, "' must not begin with '0'")); + case index_status::not_a_number: + throw_parse_error(exception_id::pointer_index_not_a_number, concat("array index '", s, "' is not a number")); + case index_status::exceeds_size_type: + throw_out_of_range(exception_id::value_out_of_range, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE case index_status::unresolved: case index_status::ok: default: - throw_out_of_range(404, concat("unresolved reference token '", s, "'")); + throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", s, "'")); } } @@ -119,7 +83,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (mode == pointer_mode::checked) { - throw_out_of_range(403, concat("key '", token, "' not found")); + throw_key_not_found(token); } return View(); } @@ -131,7 +95,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (throwing) { - throw_out_of_range(402, concat("array index '-' (", std::to_string(cur.size()), ") is out of range")); + throw_array_index_past_end(cur.size()); } return View(); } @@ -139,7 +103,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) const index_status status = array_index(token, idx); if (status != index_status::ok) { - const bool parse_error = status == index_status::leading_zero || status == index_status::not_number; + const bool parse_error = status == index_status::leading_zero || status == index_status::not_a_number; if (throwing || (mode == pointer_mode::value && parse_error)) { throw_array_index_error(status, token); @@ -150,7 +114,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (mode == pointer_mode::checked) { - throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range")); + throw_array_index_out_of_range(idx); } return View(); } @@ -160,7 +124,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (throwing) { - throw_out_of_range(404, concat("unresolved reference token '", token, "'")); + throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", token, "'")); } return View(); } diff --git a/include/nlohmann/detail/view/serializer.hpp b/include/nlohmann/detail/view/serializer.hpp index dbaca6171..76df8f89b 100644 --- a/include/nlohmann/detail/view/serializer.hpp +++ b/include/nlohmann/detail/view/serializer.hpp @@ -1073,21 +1073,17 @@ class view_serializer m_out.put("null"); return; } - static constexpr bool is_ieee_single_or_double - = std::numeric_limits::is_iec559 - && ((std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) - || (std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024)); - write_float(x, std::integral_constant()); + write_float(x, std::integral_constant::value>()); } - void write_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + void write_float(number_float_t x, std::true_type /*has_native_float_format*/) { std::array buf{}; const char* const end = ::nlohmann::detail::to_chars(buf.data(), buf.data() + buf.size(), x); m_out.put(buf.data(), static_cast(end - buf.data())); } - void write_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + void write_float(number_float_t x, std::false_type /*has_native_float_format*/) { // other types (e.g. long double) are rare: the library writes them const string_t s = BasicJsonType(x).dump(); @@ -1148,8 +1144,8 @@ class view_serializer void write_u_escape(std::uint32_t u) { - static constexpr const char* hex = "0123456789abcdef"; - const std::array e = {{'\\', 'u', hex[(u >> 12u) & 0xFu], hex[(u >> 8u) & 0xFu], hex[(u >> 4u) & 0xFu], hex[u & 0xFu]}}; + std::array e = {{'\\', 'u', 0, 0, 0, 0}}; + hex4_lower(static_cast(u), &e[2]); m_out.put(e.data(), e.size()); } diff --git a/include/nlohmann/detail/view/value.hpp b/include/nlohmann/detail/view/value.hpp index 1f1eafde4..945071705 100644 --- a/include/nlohmann/detail/view/value.hpp +++ b/include/nlohmann/detail/view/value.hpp @@ -60,7 +60,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const nod case value_t::binary: case value_t::discarded: default: - throw_type_error(302, "type must be number, but is ", value_type_name(static_cast(n.kind))); + throw_type_error(exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast(n.kind))); } } @@ -70,7 +70,7 @@ std::vector vector_value(const View& v) { if (NLOHMANN_VIEW_UNLIKELY(!v.is_array())) { - throw_type_error(302, "type must be array, but is ", v.type_name()); + throw_type_error(exception_id::type_mismatch, "type must be array, but is ", v.type_name()); } std::vector r; r.reserve(v.size()); @@ -88,7 +88,7 @@ Map map_value(const View& v) { if (NLOHMANN_VIEW_UNLIKELY(!v.is_object())) { - throw_type_error(302, "type must be object, but is ", v.type_name()); + throw_type_error(exception_id::type_mismatch, "type must be object, but is ", v.type_name()); } Map r; for (auto it = v.begin(); it != v.end(); ++it) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index ff501460a..3d40682d9 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -55,7 +55,7 @@ #include // IWYU pragma: export #include // IWYU pragma: keep #include // IWYU pragma: keep -#include +#include // IWYU pragma: export #include #include #include @@ -376,9 +376,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = - detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.', - std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.', - std::to_string(NLOHMANN_JSON_VERSION_PATCH)); + detail::concat(detail::to_string(static_cast(NLOHMANN_JSON_VERSION_MAJOR)), '.', + detail::to_string(static_cast(NLOHMANN_JSON_VERSION_MINOR)), '.', + detail::to_string(static_cast(NLOHMANN_JSON_VERSION_PATCH))); result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; @@ -400,10 +400,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #elif defined(__clang__) result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( - std::to_string(__GNUC__), '.', - std::to_string(__GNUC_MINOR__), '.', - std::to_string(__GNUC_PATCHLEVEL__)) + result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( + detail::to_string(static_cast(__GNUC__)), '.', + detail::to_string(static_cast(__GNUC_MINOR__)), '.', + detail::to_string(static_cast(__GNUC_PATCHLEVEL__))) } }; #elif defined(__HP_aCC) @@ -421,9 +421,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #endif #if defined(_MSVC_LANG) - result["compiler"]["c++"] = std::to_string(_MSVC_LANG); + result["compiler"]["c++"] = detail::to_string(static_cast(_MSVC_LANG)); #elif defined(__cplusplus) - result["compiler"]["c++"] = std::to_string(__cplusplus); + result["compiler"]["c++"] = detail::to_string(static_cast(__cplusplus)); #else result["compiler"]["c++"] = "unknown"; #endif @@ -734,23 +734,15 @@ private: // true if v is not an array/object, or is an already-empty one static bool has_no_children(const basic_json& v) noexcept { - switch (v.m_data.m_type) + if (v.m_data.m_type == value_t::array) { - case value_t::array: - return v.m_data.m_value.array->empty(); - case value_t::object: - return v.m_data.m_value.object->empty(); - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return true; + return v.m_data.m_value.array->empty(); } + if (v.m_data.m_type == value_t::object) + { + return v.m_data.m_value.object->empty(); + } + return true; } // true if no child of the non-empty array/object v has children of @@ -855,28 +847,17 @@ private: } public: - void destroy_string() noexcept + template + static void destroy_pointer(T*& ptr) noexcept { - if (string == nullptr) + if (ptr == nullptr) { // not initialized (e.g., due to exception in the ctor) return; } - AllocatorType alloc; - std::allocator_traits::destroy(alloc, string); - std::allocator_traits::deallocate(alloc, string, 1); - } - - void destroy_binary() noexcept - { - if (binary == nullptr) - { - // not initialized (e.g., due to exception in the ctor) - return; - } - AllocatorType alloc; - std::allocator_traits::destroy(alloc, binary); - std::allocator_traits::deallocate(alloc, binary, 1); + AllocatorType alloc; + std::allocator_traits::destroy(alloc, ptr); + std::allocator_traits::deallocate(alloc, ptr, 1); } // t must be value_t::array or value_t::object @@ -1042,11 +1023,11 @@ public: switch (t) { case value_t::string: - destroy_string(); + destroy_pointer(string); break; case value_t::binary: - destroy_binary(); + destroy_pointer(binary); break; case value_t::object: @@ -1108,36 +1089,19 @@ public: void set_parents() { #if JSON_DIAGNOSTICS - switch (m_data.m_type) + if (m_data.m_type == value_t::array) { - case value_t::array: + for (auto& element : *m_data.m_value.array) { - for (auto& element : *m_data.m_value.array) - { - element.m_parent = this; - } - break; + element.m_parent = this; } - - case value_t::object: + } + else if (m_data.m_type == value_t::object) + { + for (auto& element : *m_data.m_value.object) { - for (auto& element : *m_data.m_value.object) - { - element.second.m_parent = this; - } - break; + element.second.m_parent = this; } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - break; } #endif } @@ -2585,8 +2549,7 @@ public: case value_t::string: case value_t::binary: { - if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() - || !last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_UNLIKELY(!primitive_range_is_complete(first, last))) { detail::throw_literal(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object); } @@ -3060,6 +3023,134 @@ public: JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type"))); } + /// @throw type_error.309 always, as insert() is not defined for the type of @a j + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE + static void throw_insert_wrong_type(const basic_json& j) + { + detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", j); + } + + /// @throw invalid_iterator.202 always, as an iterator belongs to another value than @a obj + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE + static void throw_iterator_from_other_value(const basic_json* obj) + { + detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", obj); + } + + /// @throw type_error.310 always, as swap() with the given operand is not defined for the type of @a j + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE + static void throw_swap_wrong_type(const char* operation, const basic_json& j) + { + detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, operation, j); + } + + /// exchange the stored @a Stored value (if the value has that type) with @a other + template + void swap_impl(Other& other, const char* operation) + { + Stored* const stored = get_impl_ptr(static_cast(nullptr)); + if (JSON_HEDLEY_UNLIKELY(stored == nullptr)) + { + throw_swap_wrong_type(operation, *this); + } + + using std::swap; + swap(*stored, other); + } + + /// whether [first, last) spans the whole of a primitive value + template + JSON_HEDLEY_ALWAYS_INLINE + static bool primitive_range_is_complete(const IteratorType& first, const IteratorType& last) noexcept + { + return first.m_it.primitive_iterator.is_begin() && last.m_it.primitive_iterator.is_end(); + } + + /// destroy the current (primitive) value and become null + void reset_to_null_impl() + { + m_data.m_value.destroy(m_data.m_type); + m_data.m_value = {}; + m_data.m_type = value_t::null; + assert_invariant(); + } + + /// append @a val (moved or copied) to the array; a null value becomes an array + template + JSON_HEDLEY_ALWAYS_INLINE + void push_back_array_impl(ValueType&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); + } + + // transform a null object into an array + if (is_null()) + { + convert_null_to(); + } + + // add the element to the array + const auto old_capacity = container_capacity(); + m_data.m_value.array->push_back(std::forward(val)); + set_parent_after_insert(m_data.m_value.array->back(), old_capacity); + } + + /// parse the input behind an already created adapter + template + static basic_json parse_impl(InputAdapterType&& adapter, + parser_callback_t cb, + const bool allow_exceptions, + const bool ignore_comments, + const bool ignore_trailing_commas) + { + basic_json result; + parser(std::forward(adapter), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); + return result; + } + + /// check the input behind an already created adapter + template + static bool accept_impl(InputAdapterType&& adapter, + const bool ignore_comments, + const bool ignore_trailing_commas) + { + return parser(std::forward(adapter), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + } + + /// generate SAX events from the input behind an already created adapter + template + static bool sax_parse_impl(InputAdapterType ia, SAX* sax, + const input_format_t format, + const bool strict, + const bool ignore_comments, + const bool ignore_trailing_commas, + const cbor_tag_handler_t tag_handler) + { + if (format == input_format_t::json) + { + return parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict); + } + return detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + } + + /// serialize @a j into a new vector; @a write receives the vector-backed writer + template + static std::vector to_binary_vector(const basic_json& j, const error_handler_t error_handler, WriteFunction write) + { + std::vector result; + result.reserve(detail::binary_initial_capacity(j)); + { + // the sink sets the size of result when it is destroyed, which + // must happen before result is returned (without NRVO, it is moved) + auto writer = vector_writer(result, error_handler); + write(writer); + } + return result; + } + /// @throw out_of_range.401 always, as @a idx is out of range of @a j JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_array_index_out_of_range(size_type idx, const basic_json& j) @@ -4042,7 +4133,7 @@ public: // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } IteratorType result = end(); @@ -4061,10 +4152,7 @@ public: detail::throw_literal(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 = {}; - m_data.m_type = value_t::null; - assert_invariant(); + reset_to_null_impl(); break; } @@ -4114,16 +4202,12 @@ public: case value_t::string: case value_t::binary: { - if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() - || !last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_UNLIKELY(!primitive_range_is_complete(first, last))) { detail::throw_literal(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 = {}; - m_data.m_type = value_t::null; - assert_invariant(); + reset_to_null_impl(); break; } @@ -4604,34 +4688,20 @@ public: JSON_HEDLEY_WARN_UNUSED_RESULT size_type max_size() const noexcept { - switch (m_data.m_type) + if (m_data.m_type == value_t::array) { - case value_t::array: - { - // delegate call to array_t::max_size() - return m_data.m_value.array->max_size(); - } - - case value_t::object: - { - // delegate call to object_t::max_size() - return m_data.m_value.object->max_size(); - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - // all other types have max_size() == size() - return size(); - } + // delegate call to array_t::max_size() + return m_data.m_value.array->max_size(); } + + if (m_data.m_type == value_t::object) + { + // delegate call to object_t::max_size() + return m_data.m_value.object->max_size(); + } + + // all other types have max_size() == size() + return size(); } /// @} @@ -4709,22 +4779,7 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(basic_json&& val) { - // push_back only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) - { - detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); - } - - // transform a null object into an array - if (is_null()) - { - convert_null_to(); - } - - // add the element to the array (move semantics) - const auto old_capacity = container_capacity(); - m_data.m_value.array->push_back(std::move(val)); - set_parent_after_insert(m_data.m_value.array->back(), old_capacity); + push_back_array_impl(std::move(val)); // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor } @@ -4740,22 +4795,7 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(const basic_json& val) { - // push_back only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) - { - detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); - } - - // transform a null object into an array - if (is_null()) - { - convert_null_to(); - } - - // add the element to the array - const auto old_capacity = container_capacity(); - m_data.m_value.array->push_back(val); - set_parent_after_insert(m_data.m_value.array->back(), old_capacity); + push_back_array_impl(val); } /// @brief add an object to an array @@ -4901,13 +4941,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // insert to array and return iterator @@ -4921,13 +4961,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // moving into a local first keeps this safe even if val aliases @@ -4943,13 +4983,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // insert to array and return iterator @@ -4963,13 +5003,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // check if range iterators belong to the same JSON object @@ -5000,13 +5040,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // copy the values first: ilist may refer to elements of this array @@ -5028,7 +5068,7 @@ public: // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if range iterators belong to the same JSON object @@ -5270,82 +5310,37 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for arrays - if (JSON_HEDLEY_LIKELY(is_array())) - { - using std::swap; - swap(*(m_data.m_value.array), other); - set_parents(); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(array_t&)", *this); - } + swap_impl(other, "swap(array_t&)"); + set_parents(); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - using std::swap; - swap(*(m_data.m_value.object), other); - set_parents(); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(object_t&)", *this); - } + swap_impl(other, "swap(object_t&)"); + set_parents(); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for strings - if (JSON_HEDLEY_LIKELY(is_string())) - { - using std::swap; - swap(*(m_data.m_value.string), other); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(string_t&)", *this); - } + swap_impl(other, "swap(string_t&)"); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for binary values - if (JSON_HEDLEY_LIKELY(is_binary())) - { - using std::swap; - swap(*(m_data.m_value.binary), other); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t&)", *this); - } + swap_impl(other, "swap(binary_t&)"); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) { - // swap only works for binary values - if (JSON_HEDLEY_LIKELY(is_binary())) - { - using std::swap; - swap(*(m_data.m_value.binary), other); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t::container_type&)", *this); - } + swap_impl(other, "swap(binary_t::container_type&)"); } /// @} @@ -6026,10 +6021,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - basic_json result; - auto p = parser(detail::input_adapter(std::forward(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); - p.parse(true, result); - return result; + return parse_impl(detail::input_adapter(std::forward(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } /// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support) @@ -6044,10 +6036,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - basic_json result; - auto p = parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); - p.parse(true, result); - return result; + return parse_impl(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } JSON_HEDLEY_WARN_UNUSED_RESULT @@ -6061,10 +6050,7 @@ public: = delete; #else { - basic_json result; - auto p = parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); - p.parse(true, result); - return result; + return parse_impl(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } #endif @@ -6076,7 +6062,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + return accept_impl(detail::input_adapter(std::forward(i)), ignore_comments, ignore_trailing_commas); } /// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support) @@ -6088,7 +6074,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + return accept_impl(detail::input_adapter(std::move(first), std::move(last)), ignore_comments, ignore_trailing_commas); } JSON_HEDLEY_WARN_UNUSED_RESULT @@ -6100,7 +6086,7 @@ public: = delete; #else { - return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + return accept_impl(i.get(), ignore_comments, ignore_trailing_commas); } #endif @@ -6115,10 +6101,7 @@ public: const bool ignore_trailing_commas = false, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { - auto ia = detail::input_adapter(std::forward(i)); - return format == input_format_t::json - ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + return sax_parse_impl(detail::input_adapter(std::forward(i)), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler); } /// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support) @@ -6133,10 +6116,7 @@ public: const bool ignore_trailing_commas = false, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { - auto ia = detail::input_adapter(std::move(first), std::move(last)); - return format == input_format_t::json - ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + return sax_parse_impl(detail::input_adapter(std::move(first), std::move(last)), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler); } /// @brief generate SAX events @@ -6170,12 +6150,7 @@ public: = delete; #else { - auto ia = i.get(); - return format == input_format_t::json - // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) - // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - : detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + return sax_parse_impl(i.get(), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler); } #endif #if defined(__clang__) @@ -6323,10 +6298,10 @@ public: static std::vector to_cbor(const basic_json& j, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_cbor(j); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_cbor(j); + }); } /// @brief create a CBOR serialization of a given JSON value @@ -6354,10 +6329,10 @@ public: static std::vector to_msgpack(const basic_json& j, const error_handler_t error_handler = error_handler_t::keep) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_msgpack(j); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_msgpack(j); + }); } /// @brief create a MessagePack serialization of a given JSON value @@ -6387,10 +6362,10 @@ public: const bool use_type = false, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_ubjson(j, use_size, use_type); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_ubjson(j, use_size, use_type); + }); } /// @brief create a UBJSON serialization of a given JSON value @@ -6423,10 +6398,10 @@ public: const bjdata_version_t version = bjdata_version_t::draft2, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_ubjson(j, use_size, use_type, true, true, version); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_ubjson(j, use_size, use_type, true, true, version); + }); } /// @brief create a BJData serialization of a given JSON value @@ -6458,10 +6433,10 @@ public: static std::vector to_bson(const basic_json& j, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_bson(j); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_bson(j); + }); } /// @brief create a BSON serialization of a given JSON value @@ -6488,10 +6463,10 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/to_bon8/ static std::vector to_bon8(const basic_json& j) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result).write_bon8(j); - return result; + return to_binary_vector(j, detail::binary_writer_default_error_handler(), [&](vector_binary_writer& w) + { + w.write_bon8(j); + }); } /// @brief create a BON8 serialization of a given JSON value @@ -7827,82 +7802,68 @@ public: return; } - switch (source.type()) + if (detail::is_primitive(source.type())) { - case value_t::array: + // both primitive types: replace value + diff_replace(result, path, target); + return; + } + + if (source.is_array()) + { + const std::size_t path_length = path.size(); + + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) { + // recursive call to compare array values at index i + if (!(source[i] == target[i])) + { + diff_append_index(path, i); + diff_unequal(result, source[i], target[i], path, depth + 1); + path.resize(path_length); + } + ++i; + } + + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + diff_array_tails(result, source, target, path, i); + } + else + { + diff_target_values target_values; + std::vector added; + if (diff_object_keys(result, source, target, path, target_values, added)) + { + // fast path: target_values holds, for every member of + // source in order, its value in target -- so no further + // lookup is needed. Deleted keys are interleaved here + // too, in source's original order, to match the + // historical (pre-reordering-aware) output order. const std::size_t path_length = path.size(); - - // first pass: traverse common elements std::size_t i = 0; - while (i < source.size() && i < target.size()) + for (auto it = source.cbegin(); it != source.cend(); ++it, ++i) { - // recursive call to compare array values at index i - if (!(source[i] == target[i])) + if (target_values[i] != nullptr) { - diff_append_index(path, i); - diff_unequal(result, source[i], target[i], path, depth + 1); - path.resize(path_length); - } - ++i; - } - - // We now reached the end of at least one array - // in a second pass, traverse the remaining elements - diff_array_tails(result, source, target, path, i); - break; - } - - case value_t::object: - { - diff_target_values target_values; - std::vector added; - if (diff_object_keys(result, source, target, path, target_values, added)) - { - // fast path: target_values holds, for every member of - // source in order, its value in target -- so no further - // lookup is needed. Deleted keys are interleaved here - // too, in source's original order, to match the - // historical (pre-reordering-aware) output order. - const std::size_t path_length = path.size(); - std::size_t i = 0; - for (auto it = source.cbegin(); it != source.cend(); ++it, ++i) - { - if (target_values[i] != nullptr) + if (!(it.value() == *target_values[i])) { - if (!(it.value() == *target_values[i])) - { - diff_append_key(path, it.key()); - diff_unequal(result, it.value(), *target_values[i], path, depth + 1); - path.resize(path_length); - } - } - else - { - // found a key that is not in target -> remove it - diff_remove(result, diff_key_path(path, it.key())); + diff_append_key(path, it.key()); + diff_unequal(result, it.value(), *target_values[i], path, depth + 1); + path.resize(path_length); } } - - // append the "add" operations for brand-new keys - diff_added(result, added, path); + else + { + // found a key that is not in target -> remove it + diff_remove(result, diff_key_path(path, it.key())); + } } - break; - } - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - // both primitive types: replace value - diff_replace(result, path, target); - break; + // append the "add" operations for brand-new keys + diff_added(result, added, path); } } } @@ -7948,46 +7909,31 @@ public: return; } - switch (s.type()) + if (detail::is_primitive(s.type())) { - case value_t::array: - { - stack.emplace_back(&s, &t, current_path.size()); - return; - } - - case value_t::object: - { - std::vector target_values; - std::vector added; - if (diff_object_keys(result, s, t, current_path, target_values, added)) - { - // fast path: the frame walks source in lockstep with - // common_keys, as diff_recursively does, and appends - // added_ops once all members are done - stack.emplace_back(&s, &t, current_path.size()); - stack.back().member = s.cbegin(); - stack.back().target_values = std::move(target_values); - stack.back().added = std::move(added); - } - return; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - // both primitive types: replace value - diff_replace(result, current_path, t); - return; - } + // both primitive types: replace value + diff_replace(result, current_path, t); + return; } + + if (s.is_array()) + { + stack.emplace_back(&s, &t, current_path.size()); + return; + } + std::vector target_values; + std::vector added; + if (diff_object_keys(result, s, t, current_path, target_values, added)) + { + // fast path: the frame walks source in lockstep with + // common_keys, as diff_recursively does, and appends + // added_ops once all members are done + stack.emplace_back(&s, &t, current_path.size()); + stack.back().member = s.cbegin(); + stack.back().target_values = std::move(target_values); + stack.back().added = std::move(added); + } + return; }; enter(source, target); diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index 86481d884..85026008a 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -268,7 +268,7 @@ class basic_json_view { return {}; } - detail::view::throw_type_error(305, "cannot use operator[] with a string argument with ", type_name()); + detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", type_name()); } return lookup(key); } @@ -294,7 +294,7 @@ class basic_json_view { return {}; } - detail::view::throw_type_error(305, "cannot use operator[] with a numeric argument with ", type_name()); + detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", type_name()); } return idx < m_node->len ? element(idx) : basic_json_view(); } @@ -327,12 +327,12 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_object())) { - detail::view::throw_type_error(304, "cannot use at() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name()); } const basic_json_view r = lookup(key); if (NLOHMANN_VIEW_UNLIKELY(r.is_discarded())) { - detail::view::throw_out_of_range(403, detail::concat("key '", std::string(key.data(), key.size()), "' not found")); + detail::view::throw_key_not_found(std::string(key.data(), key.size())); } return r; } @@ -353,11 +353,11 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_array())) { - detail::view::throw_type_error(304, "cannot use at() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name()); } if (NLOHMANN_VIEW_UNLIKELY(idx >= m_node->len)) { - detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range")); + detail::view::throw_array_index_out_of_range(idx); } return element(idx); } @@ -385,7 +385,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_object())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } const basic_json_view r = lookup(key); return r.is_discarded() ? default_value : r.template get(); @@ -395,7 +395,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_object())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } // (the default is made only if there is no such member) const basic_json_view r = lookup(key); @@ -410,7 +410,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_structured())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value); return r.is_discarded() ? default_value : r.template get(); @@ -420,7 +420,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_structured())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value); return r.is_discarded() ? string_t(default_value) : r.template get(); @@ -433,7 +433,7 @@ class basic_json_view const iterator it = begin(); if (NLOHMANN_VIEW_UNLIKELY(it == end())) { - detail::view::throw_invalid_iterator(214, "cannot get value"); + detail::view::throw_invalid_iterator(detail::exception_id::iterator_value_unavailable, "cannot get value"); } return *it; } @@ -586,7 +586,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_string())) { - detail::view::throw_type_error(302, "type must be string, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be string, but is ", type_name()); } return {m_doc->str(*m_node), m_node->len}; } @@ -598,7 +598,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_number())) { - detail::view::throw_type_error(302, "type must be number, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name()); } return {m_doc->str(*m_node), detail::view::number_length(*m_node)}; } @@ -855,7 +855,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_boolean())) { - detail::view::throw_type_error(302, "type must be boolean, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be boolean, but is ", type_name()); } return (m_node->flags & detail::view::node_flags::is_true) != 0; } @@ -865,7 +865,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(m_node == nullptr)) { - detail::view::throw_type_error(302, "type must be number, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name()); } return detail::view::arithmetic_value(*m_doc, *m_node); } @@ -874,7 +874,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_null())) { - detail::view::throw_type_error(302, "type must be null, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be null, but is ", type_name()); } return nullptr; } @@ -1183,7 +1183,7 @@ class basic_json_document { if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded)) { - detail::view::throw_type_error(320, "cannot save a discarded json_document"); + detail::view::throw_type_error(detail::exception_id::document_image_unsupported, "cannot save a discarded json_document"); } return detail::view::save_image(*m_data); } @@ -1284,7 +1284,7 @@ class basic_json_document const std::size_t idx = pointer_index(token); if (idx >= 0xFFFFFFFFu) { - detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range")); + detail::view::throw_array_index_out_of_range(idx); } for (std::size_t i = 0; i < idx; ++i) { @@ -1340,7 +1340,7 @@ class basic_json_document { if (ptr.empty()) { - detail::view::throw_out_of_range(405, "JSON pointer has no parent"); + detail::view::throw_out_of_range(detail::exception_id::patch_on_root, "JSON pointer has no parent"); } const view_type parent = root().at(ptr.parent_pointer()); const auto& token = ptr.back(); @@ -1360,7 +1360,7 @@ class basic_json_document static_assert(Editable, "only an editable document can be edited: use basic_json_document (json_editable_document)"); if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded)) { - detail::view::throw_invalid_iterator(202, "view does not belong to this document"); + detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document"); } return detail::view::editor(*m_data); } @@ -1377,7 +1377,7 @@ class basic_json_document { if (idx < 0) { - detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range")); + detail::view::throw_array_index_out_of_range(idx); } return static_cast(idx); } diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index e5f9164f6..831261219 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -125,6 +125,19 @@ private: Container::pop_back(); } + /// @brief remove the entry with the given key; returns the number of removed entries + template + size_type erase_impl(const K& key) + { + const auto it = find_impl(*this, key); + if (it != this->end()) + { + erase_at(it); + return 1; + } + return 0; + } + public: template::value, int> = 0> @@ -214,26 +227,14 @@ public: size_type erase(const key_type& key) { - const auto it = find_impl(*this, key); - if (it != this->end()) - { - erase_at(it); - return 1; - } - return 0; + return erase_impl(key); } template::value, int> = 0> size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) { - const auto it = find_impl(*this, key); - if (it != this->end()) - { - erase_at(it); - return 1; - } - return 0; + return erase_impl(key); } iterator erase(iterator pos) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index c0a5b82e5..1beba149b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -284,7 +284,7 @@ -#include // transform +#include // min, transform #include // array #include // forward_list #include // inserter, front_inserter, end @@ -3459,6 +3459,36 @@ inline bool owns_memory(const value_t t) noexcept } } +/*! +@brief whether a value of the given type is neither an object nor an array + +Unlike basic_json::is_primitive(), this includes value_t::discarded: it names +the types that iterators walk with a primitive_iterator_t (a single value, or no +value for null and discarded) and that JSON Pointer resolution and the +serializer treat as leaves. Every enumerator is listed so that -Wswitch-enum +flags this function when a new type is added. +*/ +inline bool is_primitive(const value_t t) noexcept +{ + switch (t) + { + case value_t::object: + case value_t::array: + return false; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return true; + } +} + /*! @brief compare an integer with a floating point number without precision loss @@ -5499,6 +5529,7 @@ enum class exception_id : int 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_document_image = 116, ///< json_document::load() rejects an image // 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 @@ -5533,6 +5564,8 @@ enum class exception_id : int 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 + document_binary_value = 319, ///< a binary value is stored in a json_document + document_image_unsupported = 320, ///< a json_document image cannot be saved or loaded (discarded document, big-endian target) 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 @@ -5550,6 +5583,7 @@ enum class exception_id : int 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 + document_too_large = 416, ///< a json_document input, its edit storage, or an image reaches the 32-bit (4 GiB) limit // other_error internal_error = 500, ///< unreachable code was reached patch_test_failed = 501, ///< a JSON Patch 'test' operation failed @@ -5579,6 +5613,13 @@ class exception : public std::exception return concat("[json.exception.", ename, '.', std::to_string(id_), "] "); } + /// build the message "[json.exception..] " + template + static std::string what_impl(const std::string& ename, int id_, const std::string& what_arg, BasicJsonContext context) + { + return concat(name(ename, id_), diagnostics(context), what_arg); + } + static std::string diagnostics(std::nullptr_t /*leaf_element*/) { return ""; @@ -5591,48 +5632,33 @@ class exception : public std::exception std::vector tokens; for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent) { - switch (current->m_parent->type()) + const auto* parent = current->m_parent; + if (parent->is_array()) { - case value_t::array: + for (std::size_t i = 0; i < parent->m_data.m_value.array->size(); ++i) { - for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i) + if (&parent->m_data.m_value.array->operator[](i) == current) { - if (¤t->m_parent->m_data.m_value.array->operator[](i) == current) - { - tokens.emplace_back(std::to_string(i)); - break; - } + tokens.emplace_back(std::to_string(i)); + break; } - break; } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_data.m_value.object) - { - if (&element.second == current) - { - // data() is null-terminated, so a key containing - // a null byte is cut short here rather than - // truncating the whole message at what() - tokens.emplace_back(element.first.data()); - break; - } - } - break; - } - - case value_t::null: // LCOV_EXCL_LINE - case value_t::string: // LCOV_EXCL_LINE - case value_t::boolean: // LCOV_EXCL_LINE - case value_t::number_integer: // LCOV_EXCL_LINE - case value_t::number_unsigned: // LCOV_EXCL_LINE - case value_t::number_float: // LCOV_EXCL_LINE - case value_t::binary: // LCOV_EXCL_LINE - case value_t::discarded: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE } + else if (parent->is_object()) + { + for (const auto& element : *parent->m_data.m_value.object) + { + if (&element.second == current) + { + // data() is null-terminated, so a key containing + // a null byte is cut short here rather than + // truncating the whole message at what() + tokens.emplace_back(element.first.data()); + break; + } + } + } + // all other parent types (primitives) contribute no token } if (tokens.empty()) @@ -5748,7 +5774,7 @@ class invalid_iterator : public exception template::value, int> = 0> static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("invalid_iterator", id_, what_arg, context); return {id_, w.c_str()}; } @@ -5772,7 +5798,7 @@ class type_error : public exception template::value, int> = 0> static type_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("type_error", id_, what_arg, context); return {id_, w.c_str()}; } @@ -5795,7 +5821,7 @@ class out_of_range : public exception template::value, int> = 0> static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("out_of_range", id_, what_arg, context); return {id_, w.c_str()}; } @@ -5818,7 +5844,7 @@ class other_error : public exception template::value, int> = 0> static other_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg); + const std::string w = exception::what_impl("other_error", id_, what_arg, context); return {id_, w.c_str()}; } @@ -6067,12 +6093,10 @@ void from_json(const BasicJsonType& j, std::optional& opt) } #endif // JSON_HAS_CPP_17 -// overloads for basic_json template parameters -template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < std::is_arithmetic::value&& - !std::is_same::value, - int > = 0 > -void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +// read a number value of any of the three number types into val; +// returns false (leaving val untouched) if j is not a number +template +bool get_number_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) { @@ -6083,17 +6107,17 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) const auto* ptr = j.template get_ptr(); JSON_ASSERT(ptr != nullptr); val = static_cast(*ptr); - break; + return true; } case value_t::number_integer: { val = static_cast(*j.template get_ptr()); - break; + return true; } case value_t::number_float: { val = static_cast(*j.template get_ptr()); - break; + return true; } case value_t::null: @@ -6104,7 +6128,20 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) case value_t::binary: case value_t::discarded: default: - throw_type_must_be("number", j); + return false; + } +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + if (JSON_HEDLEY_UNLIKELY(!get_number_value(j, val))) + { + throw_type_must_be("number", j); } } @@ -6243,29 +6280,44 @@ inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType: arr = *j.template get_ptr(); } +// fill a std::array from the JSON array j: the size is checked once and the +// elements are read in order, so a short array converts the elements it has and +// then throws the out_of_range error at() would throw for the first missing one +template +void from_json_std_array(const BasicJsonType& j, std::array& arr) +{ + const auto& inner_array = *j.template get_ptr(); + const std::size_t size = (std::min)(N, inner_array.size()); + for (std::size_t i = 0; i < size; ++i) + { + arr[i] = inner_array[i].template get(); + } + if (JSON_HEDLEY_UNLIKELY(size < N)) + { + static_cast(j.at(size)); + } +} + template auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { - for (std::size_t i = 0; i < N; ++i) - { - arr[i] = j.at(i).template get(); - } + from_json_std_array(j, arr); } -// reserve() is called through this pair (modeled on from_json_object_reserve) -// so from_json_array_impl below has a single body for both ConstructibleArrayType -// that support reserve() and those that don't. -template -auto from_json_array_reserve(ConstructibleArrayType& arr, typename ConstructibleArrayType::size_type size, priority_tag<1> /*unused*/) --> decltype(arr.reserve(size), void()) +// reserve() is called through this pair (used for arrays and objects alike) +// so from_json_array_impl and the object from_json have a single body for both +// containers that support reserve() and those that don't. +template +auto reserve_if_possible(ConstructibleContainerType& c, typename ConstructibleContainerType::size_type size, priority_tag<1> /*unused*/) +-> decltype(c.reserve(size), void()) { - arr.reserve(size); + c.reserve(size); } -template -void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/) +template +void reserve_if_possible(ConstructibleContainerType& /*c*/, std::size_t /*size*/, priority_tag<0> /*unused*/) {} // append through push_back() where available; containers without it (such as @@ -6295,7 +6347,7 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p { ConstructibleArrayType ret; const auto* inner_array = j.template get_ptr(); - from_json_array_reserve(ret, inner_array->size(), priority_tag<1> {}); + reserve_if_possible(ret, inner_array->size(), priority_tag<1> {}); for (const auto& elem : *inner_array) { // get() returns *this, this won't call a from_json @@ -6334,6 +6386,37 @@ std::array from_json_inplace_array_impl(const BasicJsonType& return { { j.at(Idx).template get()... } }; } +// std::is_trivial is deprecated in C++26 (see binary_writer::to_char_type); +// a struct rather than an alias, as astyle indents an #ifdef block of aliases +template +struct is_trivial_element +#ifdef JSON_HAS_CPP_26 + : conjunction, std::is_trivially_default_constructible> +#else + : std::is_trivial +#endif +{}; + +// the element type may not be default constructible: construct every element +// in place (a pack expansion that is unrolled N times) +template +std::array from_json_make_array(const BasicJsonType& j, identity_tag> tag, + std::false_type /*is_trivial_element*/) +{ + return from_json_inplace_array_impl(j, tag, make_index_sequence {}); +} + +// a trivial element type cannot tell default construction plus assignment from +// construction in place, so use a loop instead of unrolling N conversions +template +std::array from_json_make_array(const BasicJsonType& j, identity_tag> /*unused*/, + std::true_type /*is_trivial_element*/) +{ + std::array arr; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init): every element is assigned or an exception is thrown + from_json_std_array(j, arr); + return arr; +} + template < typename BasicJsonType, typename T, std::size_t N > auto from_json(const BasicJsonType& j, identity_tag> tag) -> decltype(from_json_inplace_array_impl(j, tag, make_index_sequence {})) @@ -6343,7 +6426,7 @@ auto from_json(const BasicJsonType& j, identity_tag> tag) throw_type_must_be("array", j); } - return from_json_inplace_array_impl(j, tag, make_index_sequence {}); + return from_json_make_array(j, tag, is_trivial_element {}); } template @@ -6376,17 +6459,6 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } -template -auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/) --> decltype(obj.reserve(size), void()) -{ - obj.reserve(size); -} - -template -inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/) -{} - // the keys of the source are sorted for the usual object types, so hinting the // end makes every insertion constant-time (for containers that order their keys // the same way); containers without emplace_hint() (such as ordered_map) fall @@ -6415,7 +6487,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); - from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {}); + reserve_if_possible(ret, inner_object->size(), priority_tag<1> {}); for (const auto& p : *inner_object) { from_json_object_emplace(ret, p.first, p.second.template get(), priority_tag<1> {}); @@ -6435,42 +6507,17 @@ template < typename BasicJsonType, typename ArithmeticType, int > = 0 > inline void from_json(const BasicJsonType& j, ArithmeticType& val) { - switch (static_cast(j)) + if (get_number_value(j, val)) { - case value_t::number_unsigned: - { - // never null: a basic_json whose number_unsigned_t is number_integer_t - // stores no number_unsigned values (#2573) - const auto* ptr = j.template get_ptr(); - JSON_ASSERT(ptr != nullptr); - val = static_cast(*ptr); - break; - } - case value_t::number_integer: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_float: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::boolean: - { - val = static_cast(*j.template get_ptr()); - break; - } - - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::binary: - case value_t::discarded: - default: - throw_type_must_be("number", j); + return; } + + // unlike get_arithmetic_value(), a boolean is accepted here + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + throw_type_must_be("number", j); + } + val = static_cast(*j.template get_ptr()); } template @@ -6829,6 +6876,24 @@ inline std::string hex_byte(const std::uint8_t byte) return result; } +/*! +@brief write a UTF-16 code unit as four lowercase hexadecimal digits + +The lowercase counterpart of hex_byte(), used for the `\\uXXXX` escapes of the +serializers, whose output is lowercase. Writes exactly four characters to +@a out and returns the pointer past them. +*/ +JSON_HEDLEY_ALWAYS_INLINE char* hex4_lower(const std::uint16_t codeunit, char* out) noexcept +{ + constexpr const char* nibble_to_hex = "0123456789abcdef"; + const unsigned int unit = codeunit; + out[0] = nibble_to_hex[(unit >> 12u) & 0x0Fu]; + out[1] = nibble_to_hex[(unit >> 8u) & 0x0Fu]; + out[2] = nibble_to_hex[(unit >> 4u) & 0x0Fu]; + out[3] = nibble_to_hex[unit & 0x0Fu]; + return out + 4; +} + /////////////////// // UTF-8 encoding // /////////////////// @@ -6884,6 +6949,66 @@ void encode_utf8(std::uint32_t cp, const Out& out) } } +/*! +@brief the shape of a well-formed UTF-8 sequence, by its lead byte + +@a length is 1 for ASCII, 2 to 4 for a valid lead byte, and 0 for a byte that +cannot start a well-formed sequence (a continuation byte, 0xC0, 0xC1, or 0xF5 +to 0xFF). For a multibyte sequence, the second byte must lie in +[@a second_lo, @a second_hi], and every later byte in [0x80, 0xBF] +(RFC 3629, section 4; Unicode Table 3-7). The narrower second-byte ranges +exclude overlong forms (0xE0, 0xF0), surrogates (0xED), and code points +beyond U+10FFFF (0xF4). +*/ +struct utf8_sequence_shape +{ + std::uint8_t length; + std::uint8_t second_lo; + std::uint8_t second_hi; +}; + +/// @return the shape of a UTF-8 sequence starting with @a lead, see utf8_sequence_shape +inline utf8_sequence_shape utf8_shape(const std::uint8_t lead) noexcept +{ + if (lead < 0x80u) + { + return {1, 0, 0}; + } + if (lead < 0xC2u) + { + return {0, 0, 0}; + } + if (lead <= 0xDFu) + { + return {2, 0x80u, 0xBFu}; + } + if (lead == 0xE0u) + { + return {3, 0xA0u, 0xBFu}; + } + if (lead == 0xEDu) + { + return {3, 0x80u, 0x9Fu}; + } + if (lead <= 0xEFu) + { + return {3, 0x80u, 0xBFu}; + } + if (lead == 0xF0u) + { + return {4, 0x90u, 0xBFu}; + } + if (lead <= 0xF3u) + { + return {4, 0x80u, 0xBFu}; + } + if (lead == 0xF4u) + { + return {4, 0x80u, 0x8Fu}; + } + return {0, 0, 0}; +} + /////////////////// // UTF-8 decoding // /////////////////// @@ -6899,7 +7024,7 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally written by Björn Hoehrmann. See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. -The library checks UTF-8 well-formedness (RFC 3629, section 4) in three +The library checks UTF-8 well-formedness (RFC 3629, section 4) in five places, which differ in speed, diagnostics, and how they read the input: - decode() below: the serializer, to escape and, in strict mode, reject @@ -6913,10 +7038,15 @@ places, which differ in speed, diagnostics, and how they read the input: bulk string scan, the bulk path of the BON8 reader, and the BON8 writer. They must accept exactly what the lexer's switch accepts. - the byte path of binary_reader::get_bon8_string(): BON8 input without bulk - access, and the bytes the bulk path leaves to it. + access, and the bytes the bulk path leaves to it. It keeps its own ranges: + taking them from utf8_shape() made BON8 decoding 2-3% slower (#5891). +- check_utf8() in detail/view/edit.hpp: strings stored in an editable + json_document, with the index of the offending byte. It also uses + utf8_shape(). -All four must accept the same set of sequences, so a change to one needs a -matching change to the others. +All five must accept the same set of sequences, which the test "UTF-8 +validators accept the same sequences" in unit-class_lexer.cpp checks +exhaustively against decode() and utf8_shape(). @param[in,out] state the current decoder state @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) @@ -7182,35 +7312,25 @@ template class iteration_proxy_value { JSON_ASSERT(anchor.m_object != nullptr); - switch (anchor.m_object->type()) + if (anchor.m_object->is_array()) { // use integer array index as key - case value_t::array: + if (array_index != array_index_last) { - if (array_index != array_index_last) - { - int_to_string( array_index_str, array_index ); - array_index_last = array_index; - } - return array_index_str; + int_to_string( array_index_str, array_index ); + array_index_last = array_index; } - - // use key from the object - case value_t::object: - return anchor.key(); - - // use an empty key for all primitive types - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return empty_str; + return array_index_str; } + + if (anchor.m_object->is_object()) + { + // use key from the object + return anchor.key(); + } + + // use an empty key for all primitive types + return empty_str; } /// return value of the iterator @@ -8153,7 +8273,7 @@ NLOHMANN_JSON_NAMESPACE_END // #include // IWYU pragma: keep // #include - +// IWYU pragma: export // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ @@ -8533,6 +8653,36 @@ namespace detail /// the supported input formats enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 }; +/// @return the name of a binary input format, as used in exception messages +inline const char* input_format_name(const input_format_t format) noexcept +{ + switch (format) + { + case input_format_t::cbor: + return "CBOR"; + case input_format_t::msgpack: + return "MessagePack"; + case input_format_t::ubjson: + return "UBJSON"; + case input_format_t::bson: + return "BSON"; + case input_format_t::bjdata: + return "BJData"; + case input_format_t::bon8: + return "BON8"; + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return ""; // LCOV_EXCL_LINE + } +} + +/// @return the message of the parse_error thrown or reported for an empty input +inline const char* empty_input_message() noexcept +{ + return "attempting to parse an empty input; check that your input string or stream contains the expected JSON"; +} + //////////////////// // input adapters // //////////////////// @@ -9734,7 +9884,7 @@ inline file_input_adapter input_adapter(std::FILE* file) { if (file == 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)); + JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr)); } return file_input_adapter(file); } @@ -9743,7 +9893,7 @@ inline input_stream_adapter input_adapter(std::istream& stream) { if (stream.rdbuf() == 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)); + JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr)); } return input_stream_adapter(stream); } @@ -9768,7 +9918,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b) { if (b == 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)); + JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr)); } auto length = std::strlen(reinterpret_cast(b)); const auto* ptr = reinterpret_cast(b); @@ -12024,7 +12174,7 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d // printable ASCII except '"' and '\\' - is emitted unchanged. Note this differs // from is_string_special() only in that 0x7F is also a stop (it is escaped as // \u007f under ensure_ascii). -inline bool is_ascii_copyable(unsigned char c) noexcept +constexpr bool is_ascii_copyable(unsigned char c) noexcept { return c >= 0x20u && c < 0x7Fu && c != '"' && c != '\\'; } @@ -13073,194 +13223,74 @@ class lexer : public lexer_base // invalid control characters case 0x00: - { - error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; - return token_type::parse_error; - } - case 0x01: - { - error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; - return token_type::parse_error; - } - case 0x02: - { - error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; - return token_type::parse_error; - } - case 0x03: - { - error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; - return token_type::parse_error; - } - case 0x04: - { - error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; - return token_type::parse_error; - } - case 0x05: - { - error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; - return token_type::parse_error; - } - case 0x06: - { - error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; - return token_type::parse_error; - } - case 0x07: - { - error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; - return token_type::parse_error; - } - case 0x08: - { - error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; - return token_type::parse_error; - } - case 0x09: - { - error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; - return token_type::parse_error; - } - case 0x0A: - { - error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; - return token_type::parse_error; - } - case 0x0B: - { - error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; - return token_type::parse_error; - } - case 0x0C: - { - error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; - return token_type::parse_error; - } - case 0x0D: - { - error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; - return token_type::parse_error; - } - case 0x0E: - { - error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; - return token_type::parse_error; - } - case 0x0F: - { - error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; - return token_type::parse_error; - } - case 0x10: - { - error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; - return token_type::parse_error; - } - case 0x11: - { - error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; - return token_type::parse_error; - } - case 0x12: - { - error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; - return token_type::parse_error; - } - case 0x13: - { - error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; - return token_type::parse_error; - } - case 0x14: - { - error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; - return token_type::parse_error; - } - case 0x15: - { - error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; - return token_type::parse_error; - } - case 0x16: - { - error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; - return token_type::parse_error; - } - case 0x17: - { - error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; - return token_type::parse_error; - } - case 0x18: - { - error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; - return token_type::parse_error; - } - case 0x19: - { - error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; - return token_type::parse_error; - } - case 0x1A: - { - error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; - return token_type::parse_error; - } - case 0x1B: - { - error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; - return token_type::parse_error; - } - case 0x1C: - { - error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; - return token_type::parse_error; - } - case 0x1D: - { - error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; - return token_type::parse_error; - } - case 0x1E: - { - error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; - return token_type::parse_error; - } - case 0x1F: { - error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + static const std::array control_character_messages = {{ + "invalid string: control character U+0000 (NUL) must be escaped to \\u0000", + "invalid string: control character U+0001 (SOH) must be escaped to \\u0001", + "invalid string: control character U+0002 (STX) must be escaped to \\u0002", + "invalid string: control character U+0003 (ETX) must be escaped to \\u0003", + "invalid string: control character U+0004 (EOT) must be escaped to \\u0004", + "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005", + "invalid string: control character U+0006 (ACK) must be escaped to \\u0006", + "invalid string: control character U+0007 (BEL) must be escaped to \\u0007", + "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b", + "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t", + "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n", + "invalid string: control character U+000B (VT) must be escaped to \\u000B", + "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f", + "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r", + "invalid string: control character U+000E (SO) must be escaped to \\u000E", + "invalid string: control character U+000F (SI) must be escaped to \\u000F", + "invalid string: control character U+0010 (DLE) must be escaped to \\u0010", + "invalid string: control character U+0011 (DC1) must be escaped to \\u0011", + "invalid string: control character U+0012 (DC2) must be escaped to \\u0012", + "invalid string: control character U+0013 (DC3) must be escaped to \\u0013", + "invalid string: control character U+0014 (DC4) must be escaped to \\u0014", + "invalid string: control character U+0015 (NAK) must be escaped to \\u0015", + "invalid string: control character U+0016 (SYN) must be escaped to \\u0016", + "invalid string: control character U+0017 (ETB) must be escaped to \\u0017", + "invalid string: control character U+0018 (CAN) must be escaped to \\u0018", + "invalid string: control character U+0019 (EM) must be escaped to \\u0019", + "invalid string: control character U+001A (SUB) must be escaped to \\u001A", + "invalid string: control character U+001B (ESC) must be escaped to \\u001B", + "invalid string: control character U+001C (FS) must be escaped to \\u001C", + "invalid string: control character U+001D (GS) must be escaped to \\u001D", + "invalid string: control character U+001E (RS) must be escaped to \\u001E", + "invalid string: control character U+001F (US) must be escaped to \\u001F" + } + }; + error_message = control_character_messages[static_cast(current)]; return token_type::parse_error; } @@ -15414,6 +15444,52 @@ struct diagnostic_positions }; #endif +/*! +@brief error state shared by the DOM-building SAX parsers + +Holds whether a parse error occurred and whether errors are thrown, and +implements the parse_error() and is_errored() members of the SAX interface for +json_sax_dom_parser and json_sax_dom_callback_parser. +*/ +class json_sax_dom_error_state +{ + public: + explicit json_sax_dom_error_state(const bool allow_exceptions_) noexcept + : allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_error_state(const json_sax_dom_error_state&) = delete; + json_sax_dom_error_state(json_sax_dom_error_state&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_error_state& operator=(const json_sax_dom_error_state&) = delete; + json_sax_dom_error_state& operator=(json_sax_dom_error_state&&) = delete; // the const member prevents assignment + ~json_sax_dom_error_state() = default; + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + bool is_errored() const + { + return errored; + } + + private: + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + /*! @brief a hash table that finds the position of a key in an object whose elements are kept in a vector (ordered_map) @@ -15522,7 +15598,7 @@ read up to it. parse error) */ template -class json_sax_dom_parser +class json_sax_dom_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -15538,7 +15614,7 @@ class json_sax_dom_parser @param[in] allow_exceptions_ whether parse errors yield exceptions */ explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_) {} // make class move-only @@ -15689,24 +15765,6 @@ class json_sax_dom_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: using object_t = typename BasicJsonType::object_t; using array_t = typename BasicJsonType::array_t; @@ -15715,9 +15773,17 @@ class json_sax_dom_parser /// so that a big object can find the keys it has in a hash table instead of /// searching all of them template - using detect_indexable_keys = decltype(std::declval().emplace_new_key(std::declval(), std::declval()), - std::hash()(std::declval())); - static constexpr bool indexed_keys = is_detected::value; + using detect_emplace_new_key = decltype(std::declval().emplace_new_key(std::declval(), std::declval())); + template + using detect_hash = decltype(std::hash()(std::declval())); + // std::hash is only named once the object can hold the keys in a vector: + // VS 2015 static-asserts on std::hash of a type without one instead of + // reporting a substitution failure + template::value> + struct detect_indexable_keys : std::false_type {}; + template + struct detect_indexable_keys : is_detected {}; + static constexpr bool indexed_keys = detect_indexable_keys::value; /// what a frame holds for objects that are kept in a vector: the key index /// of an object with many keys, and for an object that is collected on the @@ -15839,7 +15905,10 @@ class json_sax_dom_parser { const std::size_t new_capacity = capacity * 2; std::unique_ptr new_heap(new frame[new_capacity]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - std::copy(data, data + count, new_heap.get()); + for (std::size_t i = 0; i < count; ++i) + { + new_heap[i] = data[i]; + } heap = std::move(new_heap); data = heap.get(); capacity = new_capacity; @@ -15857,7 +15926,10 @@ class json_sax_dom_parser { if (other.heap == nullptr) { - std::copy(other.local.begin(), other.local.begin() + static_cast(other.count), local.begin()); + for (std::size_t i = 0; i < other.count; ++i) + { + local[i] = other.local[i]; + } data = local.data(); capacity = local_capacity; } @@ -16002,6 +16074,8 @@ class json_sax_dom_parser /// directly if the value was the one too many. void place_container(const frame& f, frame* enclosing, BasicJsonType&& value, const bool enclosing_is_innermost) { + // only a container with a slot may have no enclosing container + JSON_ASSERT(f.slot != nullptr || enclosing != nullptr); if (f.slot != nullptr) { *f.slot = std::move(value); @@ -16418,10 +16492,6 @@ class json_sax_dom_parser /// the array that stores the elements of the innermost array as they /// arrive (frames.back().array), or nullptr array_t* stored_array = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// the lexer reference to obtain the current position lexer_t* m_lexer_ref = nullptr; }; @@ -16446,7 +16516,7 @@ constructor contains the parsed value. @tparam BasicJsonType the JSON type */ template -class json_sax_dom_binary_parser +class json_sax_dom_binary_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -16462,7 +16532,7 @@ class json_sax_dom_binary_parser @param[in] allow_exceptions_ whether parse errors yield exceptions */ explicit json_sax_dom_binary_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_) {} // make class move-only @@ -16591,24 +16661,6 @@ class json_sax_dom_binary_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: /*! @@ -16662,16 +16714,12 @@ class json_sax_dom_binary_parser std::vector ref_stack {}; /// helper to hold the reference for the next object element BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// the lexer reference to obtain the current position lexer_t* m_lexer_ref = nullptr; }; template -class json_sax_dom_callback_parser +class json_sax_dom_callback_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -16687,7 +16735,7 @@ class json_sax_dom_callback_parser parser_callback_t cb, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), callback(std::move(cb)), m_lexer_ref(lexer_) { keep_stack.push_back(true); } @@ -16951,24 +16999,6 @@ class json_sax_dom_callback_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: /*! @@ -17235,12 +17265,8 @@ class json_sax_dom_callback_parser /// value is later rejected by the callback, instead of erasing the /// member entirely std::vector> duplicate_key_stash {}; - /// whether a syntax error occurred - bool errored = false; /// callback function const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// a discarded value for the callback BasicJsonType discarded = BasicJsonType::value_t::discarded; /// the value of the key event for the callback (see key_value()) @@ -17734,6 +17760,40 @@ class binary_reader return is_object ? sax->end_object() : sax->end_array(); } + /*! + @brief close the innermost open container and report whether that + completed the value being read + + The value begun by the driver is complete once its container is not + inside another one. + + @param[out] value_complete whether no container is left open + @return whether the SAX parser accepted the end event + */ + bool leave_container_and_check(bool& value_complete) + { + if (JSON_HEDLEY_UNLIKELY(!leave_container())) + { + return false; + } + value_complete = container_stack.empty(); + return true; + } + + /*! + @brief read an object key and pass it to the SAX parser + + @tparam GetKey member function that reads the key into a string + @param[in,out] key string the key is read into; cleared first + @return whether reading the key and the SAX event succeeded + */ + template + bool read_object_key(string_t& key) + { + key.clear(); + return (this->*GetKey)(key) && sax->key(key); + } + ////////// // BSON // ////////// @@ -18505,6 +18565,34 @@ class binary_reader 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); } + /*! + @brief report an object key that is not a string + + @param[in] found description of what was found instead of a string key + @return false + */ + bool unsupported_key_type(const char* found) const + { + return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found); + } + + /*! + @brief read the length of a CBOR string or byte array and then its bytes + + @tparam NumberType the type of the length field + @tparam ContainerType the destination container (string_t or binary_t) + @param[in] context further context information (for diagnostics) + @param[out] result container the bytes are appended to + + @return whether reading completed + */ + template + bool get_cbor_sized_bytes(const char* context, ContainerType& result) + { + NumberType len{}; + return get_number(len) && get_bytes(len, context, result); + } + /*! @brief reads a definite-length CBOR string @@ -18555,26 +18643,22 @@ class binary_reader case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - std::uint8_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - std::uint16_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { - std::uint32_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { - std::uint64_t len{}; - return get_number(len) && get_string(len, result); + return get_cbor_sized_bytes("string", result); } default: @@ -18709,7 +18793,7 @@ class binary_reader break; } - return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found); + return unsupported_key_type(found); } /*! @@ -18762,30 +18846,22 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { - std::uint8_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { - std::uint16_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { - std::uint32_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { - std::uint64_t len{}; - return get_number(len) && - get_binary(len, result); + return get_cbor_sized_bytes("binary", result); } default: @@ -19000,12 +19076,12 @@ class binary_reader if (at_end) { - if (JSON_HEDLEY_UNLIKELY(!leave_container())) + bool value_complete = false; + if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete))) { return false; } - // the value begun here is complete once its container is - if (container_stack.empty()) + if (value_complete) { return true; } @@ -19014,8 +19090,7 @@ class binary_reader if (top.is_object) { - key.clear(); - if (JSON_HEDLEY_UNLIKELY(!get_cbor_object_key(key) || !sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_cbor_object_key>(key))) { return false; } @@ -19486,20 +19561,17 @@ class binary_reader case 0xD9: // str 8 { - std::uint8_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 0xDA: // str 16 { - std::uint16_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 0xDB: // str 32 { - std::uint32_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } default: @@ -19596,7 +19668,7 @@ class binary_reader break; } - return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found); + return unsupported_key_type(found); } /*! @@ -19741,12 +19813,12 @@ class binary_reader if (container_stack.back().remaining == 0) { - if (JSON_HEDLEY_UNLIKELY(!leave_container())) + bool value_complete = false; + if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete))) { return false; } - // the value begun here is complete once its container is - if (container_stack.empty()) + if (value_complete) { return true; } @@ -19759,8 +19831,7 @@ class binary_reader if (is_object) { get(); - key.clear(); - if (JSON_HEDLEY_UNLIKELY(!get_msgpack_object_key(key) || !sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_msgpack_object_key>(key))) { return false; } @@ -19944,8 +20015,7 @@ class binary_reader { case 'U': { - std::uint8_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 'i': @@ -19978,8 +20048,7 @@ class binary_reader { break; } - std::uint16_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 'm': @@ -19988,8 +20057,7 @@ class binary_reader { break; } - std::uint32_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } case 'M': @@ -19998,8 +20066,7 @@ class binary_reader { break; } - std::uint64_t len{}; - return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + return get_string_with_length(result, context); } default: @@ -20815,12 +20882,12 @@ class binary_reader if (at_end) { - if (JSON_HEDLEY_UNLIKELY(!leave_container())) + bool value_complete = false; + if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete))) { return false; } - // the value begun here is complete once its container is - if (container_stack.empty()) + if (value_complete) { return true; } @@ -20829,8 +20896,7 @@ class binary_reader if (top.is_object) { - key.clear(); - if (JSON_HEDLEY_UNLIKELY(!get_bon8_key(key) || !sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_bon8_key>(key))) { return false; } @@ -21570,6 +21636,22 @@ class binary_reader return true; } + /*! + @brief read a length of type @a NumberType, then a string of that many bytes + + @tparam NumberType the type of the length field + @param[out] result string the bytes are appended to + @param[in] context further context information (for diagnostics) + + @return whether reading and validating the string succeeded + */ + template + bool get_string_with_length(string_t& result, const char* context) + { + NumberType len{}; + return get_number(len) && get_string(len, result) && check_string_utf8(result, context); + } + /*! @brief create a byte array by reading bytes from the input @@ -21779,37 +21861,7 @@ class binary_reader const std::string& context) const { std::string error_msg = "syntax error while parsing "; - - switch (input_format) - { - case input_format_t::cbor: - error_msg += "CBOR"; - break; - - case input_format_t::msgpack: - error_msg += "MessagePack"; - break; - - case input_format_t::ubjson: - error_msg += "UBJSON"; - break; - - case input_format_t::bson: - error_msg += "BSON"; - break; - - case input_format_t::bjdata: - error_msg += "BJData"; - break; - - case input_format_t::bon8: - error_msg += "BON8"; - break; - - case input_format_t::json: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - } + error_msg += input_format_name(input_format); return concat(error_msg, ' ', context, ": ", detail); } @@ -22093,23 +22145,10 @@ class parser if (result) { - if (strict) - { - // strict mode: next byte must be EOF - if (get_token() != token_type::end_of_input) - { - return syntax_error(*sax, exception_message(token_type::end_of_input, "value")); - } - } - else - { - // the caller keeps using the input: position it right after - // the value by leaving the character that terminated it - m_lexer.release_lookahead(); - } + return finish_input(*sax, strict); } - return result; + return false; } private: @@ -22130,6 +22169,39 @@ class parser return m_lexer.get_string(); } + /*! + @brief apply the end-of-input handling after a value was parsed + + In strict mode, the next token must be the end of the input; otherwise + parse_error.101 is reported through @a sax. In non-strict mode, the + lookahead is released so the caller can keep reading the input right after + the parsed value. + + @param[in,out] sax the SAX parser to report an error to + @param[in] strict whether to expect the last token to be EOF + @return true, or what @a sax returned for the error + */ + template + bool finish_input(SAX& sax, const bool strict) + { + if (strict) + { + // strict mode: next byte must be EOF + if (get_token() != token_type::end_of_input) + { + return syntax_error(sax, exception_message(token_type::end_of_input, "value")); + } + } + else + { + // the caller keeps using the input: position it right after + // the value by leaving the character that terminated it + m_lexer.release_lookahead(); + } + + return true; + } + /*! @brief run a DOM SAX parser to completion and position the lexer @@ -22149,20 +22221,7 @@ class parser { sax_parse_internal(&sdp); - if (strict) - { - // in strict mode, input must be completely read - if (get_token() != token_type::end_of_input) - { - syntax_error(sdp, exception_message(token_type::end_of_input, "value")); - } - } - else - { - // the caller keeps using the input: position it right after - // the value by leaving the character that terminated it - m_lexer.release_lookahead(); - } + static_cast(finish_input(sdp, strict)); return !sdp.is_errored(); } @@ -22339,7 +22398,7 @@ class parser { if (JSON_HEDLEY_UNLIKELY(m_lexer.get_chars_read_total() == 1)) { - return syntax_error(*sax, "attempting to parse an empty input; check that your input string or stream contains the expected JSON"); + return syntax_error(*sax, empty_input_message()); } return syntax_error(*sax, exception_message(token_type::literal_or_value, "value")); @@ -22828,33 +22887,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } + m_it.object_iterator = typename object_t::iterator(); + } + else if (m_object->is_array()) + { + m_it.array_iterator = typename array_t::iterator(); + } + else + { + // primitive (is_primitive(), including discarded) + m_it.primitive_iterator = primitive_iterator_t(); } } @@ -22969,33 +23013,28 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - { - m_it.object_iterator = m_object->m_data.m_value.object->end(); - break; - } + m_it.object_iterator = m_object->m_data.m_value.object->end(); + } + else if (m_object->is_array()) + { + m_it.array_iterator = m_object->m_data.m_value.array->end(); + } + else + { + // primitive (is_primitive(), including discarded) + m_it.primitive_iterator.set_end(); + } + } - case value_t::array: - { - m_it.array_iterator = m_object->m_data.m_value.array->end(); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator.set_end(); - break; - } + /// @throw invalid_iterator.212 if @a other points to another container + template + JSON_HEDLEY_ALWAYS_INLINE void check_same_container(const IterImpl& other) const + { + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + throw_literal(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object); } } @@ -23144,10 +23183,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci bool operator==(const IterImpl& other) const { // if objects are not the same, the comparison is undefined - if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) - { - throw_literal(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object); - } + check_same_container(other); // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 if (m_object == nullptr) @@ -23186,10 +23222,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined - if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) - { - throw_literal(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object); - } + check_same_container(other); // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 if (m_object == nullptr) @@ -23198,25 +23231,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return false; } - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - throw_literal(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object); - - case value_t::array: - return (m_it.array_iterator < other.m_it.array_iterator); - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return (m_it.primitive_iterator < other.m_it.primitive_iterator); + throw_literal(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object); } + + if (m_object->is_array()) + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + // primitive (is_primitive(), including discarded) + return (m_it.primitive_iterator < other.m_it.primitive_iterator); } /*! @@ -23254,30 +23280,19 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); + throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); + } - case value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator += i; - break; - } + if (m_object->is_array()) + { + std::advance(m_it.array_iterator, i); + } + else + { + // primitive (is_primitive(), including discarded) + m_it.primitive_iterator += i; } return *this; @@ -23333,25 +23348,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + if (m_object->is_object()) { - case value_t::object: - throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); - - case value_t::array: - return m_it.array_iterator - other.m_it.array_iterator; - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return m_it.primitive_iterator - other.m_it.primitive_iterator; + throw_literal(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object); } + + if (m_object->is_array()) + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + // primitive (is_primitive(), including discarded) + return m_it.primitive_iterator - other.m_it.primitive_iterator; } /*! @@ -23615,6 +23623,102 @@ NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { struct json_pointer_access; + +/*! +@brief result of parse_array_index() + +json_pointer::array_index() maps each value to the corresponding +parse_error/out_of_range exception; json_pointer::contains() and +get_checked_or_null(), which must not throw for an out-of-range or +unrepresentable index, and the json_view pointer resolution switch on it +directly instead. +*/ +enum class array_index_status +{ + ok, ///< @a s is a valid, representable array index + leading_zero, ///< @a s begins with '0' but has more than one character + 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 + exceeds_size_type ///< @a s converts to an integer that exceeds SizeType +}; + +/*! +@brief convert a JSON Pointer reference token into an array index (RFC 6901, Sect. 4) + +@param[in] s reference token to be converted into an array index; its data() + must be NUL-terminated +@param[out] idx the integer representation of @a s if array_index_status::ok + is returned; left unchanged otherwise + +@return whether @a s is a valid array index, and if not, why + +@note this function never throws; json_pointer::array_index(), the callers that + must not throw, and the json_view pointer resolution build on it instead + of each re-implementing the RFC 6901 digit rules and the range check +@note force-inlined: as a free function template it is no longer inlined into + the pointer resolution, which makes JsonPointerAt up to 17% slower (#5891) +*/ +template +JSON_HEDLEY_ALWAYS_INLINE array_index_status parse_array_index(const StringType& s, SizeType& idx) noexcept +{ + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + return array_index_status::leading_zero; + } + + // error condition (cf. RFC 6901, Sect. 4); this also covers single- + // 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.size() == 1 && s[0] == '-') && !(s[0] >= '0' && s[0] <= '9'))) + { + return array_index_status::not_a_number; + } + + // fast path: up to 18 decimal digits cannot overflow unsigned long long or + // SizeType (9 digits for a 32-bit SizeType), so strtoull and errno are not needed + constexpr std::size_t max_fast_digits = sizeof(SizeType) >= 8 ? 18 : 9; + if (!s.empty() && s.size() <= max_fast_digits) + { + SizeType acc = 0; + for (std::size_t i = 0; i < s.size(); ++i) + { + const char c = s[i]; + if (JSON_HEDLEY_UNLIKELY(c < '0' || c > '9')) + { + break; // not a plain number: let strtoull decide + } + acc = static_cast((acc * 10) + static_cast(c - '0')); + if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1)) + { + idx = acc; + return array_index_status::ok; + } + } + } + + const char* p = s.data(); + char* p_end = nullptr; // NOLINT(misc-const-correctness) + errno = 0; // strtoull doesn't reset errno + const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) + if (p == p_end // invalid input or empty string + || errno == ERANGE // out of range + || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read + { + return array_index_status::unresolved; + } + + // the index does not fit into SizeType; on 64-bit platforms this is + // only SIZE_MAX itself (see #2203 and #5395) + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + return array_index_status::exceeds_size_type; + } + + idx = static_cast(res); + return array_index_status::ok; +} + } // namespace detail /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document @@ -23879,6 +23983,28 @@ class json_pointer JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr)); } + /// whether the '~' at @a pos of @a s is not followed by '0' or '1' + JSON_HEDLEY_ALWAYS_INLINE static bool is_invalid_escape(const string_t& s, std::size_t pos) + { + return pos == s.size() - 1 || (s[pos + 1] != '0' && s[pos + 1] != '1'); + } + + /// @throw parse_error.107 always + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_missing_slash(const string_t& pointer) + { + static_cast(pointer); // unused when JSON_NOEXCEPTION is defined + JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", pointer, "'"), nullptr)); + } + + /// @throw parse_error.107 if a nonempty @a pointer does not begin with '/' + JSON_HEDLEY_ALWAYS_INLINE static void check_begins_with_slash(const string_t& pointer) + { + if (JSON_HEDLEY_UNLIKELY(pointer[0] != '/')) + { + throw_missing_slash(pointer); + } + } + /// @throw out_of_range.404 always template JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_unresolved(const string_t& reference_token, BasicJsonContext context) @@ -23888,21 +24014,7 @@ class json_pointer 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 - - @ref array_index maps each value to the corresponding parse_error/out_of_range - exception; @ref contains and @ref get_checked_or_null, which must not throw for - an out-of-range or unrepresentable index, switch on it directly instead. - */ - enum class array_index_status - { - ok, ///< @a s is a valid, representable array index - leading_zero, ///< @a s begins with '0' but has more than one character - 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 - exceeds_size_type ///< @a s converts to an integer that exceeds size_type - }; + using array_index_status = detail::array_index_status; /// whether a reference token is "-" (a plain comparison with a string literal is not always inlined) static bool is_dash(const string_t& s) noexcept @@ -23910,78 +24022,11 @@ class json_pointer return s.size() == 1 && s[0] == '-'; } - /*! - @param[in] s reference token to be converted into an array index - @param[out] idx the integer representation of @a s if @ref array_index_status::ok - is returned; left unchanged otherwise - - @return whether @a s is a valid array index, and if not, why - - @note this function never throws; @ref array_index and the callers that must not - throw (@ref contains, @ref get_checked_or_null) build on it instead of each - re-implementing the RFC 6901 digit rules and the @a size_type range check - */ + /// detail::parse_array_index() for the size_type of @a BasicJsonType template static array_index_status parse_array_index(const string_t& s, typename BasicJsonType::size_type& idx) noexcept { - using size_type = typename BasicJsonType::size_type; - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) - { - return array_index_status::leading_zero; - } - - // error condition (cf. RFC 6901, Sect. 4); this also covers single- - // 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() && !is_dash(s) && !(s[0] >= '0' && s[0] <= '9'))) - { - return array_index_status::not_a_number; - } - - // fast path: up to 18 decimal digits cannot overflow unsigned long long or - // size_type (9 digits for a 32-bit size_type), so strtoull and errno are not needed - constexpr std::size_t max_fast_digits = sizeof(size_type) >= 8 ? 18 : 9; - if (!s.empty() && s.size() <= max_fast_digits) - { - size_type acc = 0; - for (std::size_t i = 0; i < s.size(); ++i) - { - const char c = s[i]; - if (JSON_HEDLEY_UNLIKELY(c < '0' || c > '9')) - { - break; // not a plain number: let strtoull decide - } - acc = static_cast((acc * 10) + static_cast(c - '0')); - if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1)) - { - idx = acc; - return array_index_status::ok; - } - } - } - - const char* p = s.data(); - char* p_end = nullptr; // NOLINT(misc-const-correctness) - errno = 0; // strtoull doesn't reset errno - const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) - if (p == p_end // invalid input or empty string - || errno == ERANGE // out of range - || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read - { - return array_index_status::unresolved; - } - - // the index does not fit into size_type; on 64-bit platforms this is - // only SIZE_MAX itself (see #2203 and #5395) - if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) - { - return array_index_status::exceeds_size_type; - } - - idx = static_cast(res); - return array_index_status::ok; + return detail::parse_array_index(s, idx); } /*! @@ -24031,10 +24076,7 @@ class json_pointer return reference_tokens.empty(); } - if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) - { - 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)); - } + check_begins_with_slash(reference_string); // the whole string is validated, whether or not it matches const std::size_t size = reference_string.size(); @@ -24042,7 +24084,7 @@ class json_pointer pos != string_t::npos; pos = reference_string.find_first_of('~', pos + 1)) { - if (JSON_HEDLEY_UNLIKELY(pos == size - 1 || (reference_string[pos + 1] != '0' && reference_string[pos + 1] != '1'))) + if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(reference_string, pos))) { JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } @@ -24320,10 +24362,7 @@ class json_pointer } const char* const data = key.data(); - if (JSON_HEDLEY_UNLIKELY(data[0] != '/')) - { - JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", key, "'"), nullptr)); - } + check_begins_with_slash(key); // The beginning of the key that it has in common with the previous // one, which was checked already, is skipped: the scan starts with @@ -24356,7 +24395,7 @@ class json_pointer else if (data[i] == '~') { // ~ must be followed by 0 or 1 - if (JSON_HEDLEY_UNLIKELY(i + 1 == size || (data[i + 1] != '0' && data[i + 1] != '1'))) + if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(key, i))) { JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } @@ -24455,58 +24494,46 @@ class json_pointer ++end; } - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + token.clear(); + token.append(begin, static_cast(end - begin)); + const auto it = ptr->m_data.m_value.object->find(token); + if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) { - token.clear(); - token.append(begin, static_cast(end - begin)); - const auto it = ptr->m_data.m_value.object->find(token); - if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) - { - return nullptr; - } - ptr = &it->second; - break; - } - - case detail::value_t::array: - { - // an index of up to 18 digits without a leading zero fits into size_type - // (9 digits for a 32-bit size_type); everything else is left to json_pointer - constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9; - const auto digits = static_cast(end - begin); - if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0'))) - { - return nullptr; - } - typename BasicJsonType::size_type idx = 0; - for (const char* c = begin; c != end; ++c) - { - if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9')) - { - return nullptr; - } - idx = static_cast((idx * 10) + static_cast(*c - '0')); - } - if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) - { - return nullptr; - } - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: return nullptr; + } + ptr = &it->second; + } + else if (ptr->is_array()) + { + // an index of up to 18 digits without a leading zero fits into size_type + // (9 digits for a 32-bit size_type); everything else is left to json_pointer + constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9; + const auto digits = static_cast(end - begin); + if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0'))) + { + return nullptr; + } + typename BasicJsonType::size_type idx = 0; + for (const char* c = begin; c != end; ++c) + { + if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9')) + { + return nullptr; + } + idx = static_cast((idx * 10) + static_cast(*c - '0')); + } + if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) + { + return nullptr; + } + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + return nullptr; } pos = static_cast(end - path.data()); steps.emplace_back(pos, ptr); @@ -24556,40 +24583,28 @@ class json_pointer : detail::value_t::object; } - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + // use unchecked object access + ptr = &object_member(*ptr, reference_token); + } + else if (ptr->is_array()) + { + if (reference_token == "-") { - // use unchecked object access - ptr = &object_member(*ptr, reference_token); - break; + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); } - - case detail::value_t::array: + else { - if (reference_token == "-") - { - // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - ptr = &ptr->operator[](array_index(reference_token)); - } - break; + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - throw_unresolved(reference_token, ptr); + } + else + { + // primitive value with a reference token left + throw_unresolved(reference_token, ptr); } } @@ -24607,51 +24622,39 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + const auto it = ptr->m_data.m_value.object->find(reference_token); + if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) { - const auto it = ptr->m_data.m_value.object->find(reference_token); - if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) - { - // at() throws out_of_range.403 with the usual message - ptr->at(reference_token); - } - ptr = &it->second; - break; + // at() throws out_of_range.403 with the usual message + ptr->at(reference_token); + } + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) + { + // "-" always fails the range check + 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)); } - case detail::value_t::array: + const auto idx = array_index(reference_token); + // 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(is_dash(reference_token))) - { - // "-" always fails the range check - 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)); - } - - const auto idx = array_index(reference_token); - // Bounds check before access to avoid exception with JSON_NOEXCEPTION - if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) - { - 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)); - } - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; + 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)); } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - throw_unresolved(reference_token, ptr); + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + throw_unresolved(reference_token, ptr); } } @@ -24680,44 +24683,32 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + // use unchecked object access; the const operator[] + // asserts that the key exists + const auto it = ptr->m_data.m_value.object->find(reference_token); + JSON_ASSERT(it != ptr->m_data.m_value.object->end()); + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) { - // use unchecked object access; the const operator[] - // asserts that the key exists - const auto it = ptr->m_data.m_value.object->find(reference_token); - JSON_ASSERT(it != ptr->m_data.m_value.object->end()); - ptr = &it->second; - break; + // "-" cannot be used for const access + 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)); } - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) - { - // "-" cannot be used for const access - 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[] - // asserts that the index exists - const auto idx = array_index(reference_token); - JSON_ASSERT(idx < ptr->m_data.m_value.array->size()); - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - throw_unresolved(reference_token, ptr); + // use unchecked array access; the const operator[] + // asserts that the index exists + const auto idx = array_index(reference_token); + JSON_ASSERT(idx < ptr->m_data.m_value.array->size()); + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + throw_unresolved(reference_token, ptr); } } @@ -24741,66 +24732,54 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + const auto it = ptr->m_data.m_value.object->find(reference_token); + if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) { - const auto it = ptr->m_data.m_value.object->find(reference_token); - if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end())) - { - return nullptr; - } - ptr = &it->second; - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) - { - // "-" always fails the range check - return nullptr; - } - - // a malformed index throws parse_error.106/109; an - // index that is syntactically valid but cannot be - // represented (out_of_range.404/410) is treated like an - // out-of-range index below - typename BasicJsonType::size_type idx{}; - switch (parse_array_index(reference_token, idx)) - { - // the branches differ in their messages, not after JSON_THROW's expansion - // NOLINTNEXTLINE(bugprone-branch-clone) - case array_index_status::leading_zero: - 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: - 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::exceeds_size_type: - return nullptr; - case array_index_status::ok: - default: - break; - } - - if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) - { - return nullptr; - } - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: return nullptr; + } + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) + { + // "-" always fails the range check + return nullptr; + } + + // a malformed index throws parse_error.106/109; an + // index that is syntactically valid but cannot be + // represented (out_of_range.404/410) is treated like an + // out-of-range index below + typename BasicJsonType::size_type idx{}; + switch (parse_array_index(reference_token, idx)) + { + // the branches differ in their messages, not after JSON_THROW's expansion + // NOLINTNEXTLINE(bugprone-branch-clone) + case array_index_status::leading_zero: + 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: + 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::exceeds_size_type: + return nullptr; + case array_index_status::ok: + default: + break; + } + + if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) + { + return nullptr; + } + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // primitive value with a reference token left + return nullptr; } } @@ -24816,63 +24795,48 @@ class json_pointer { for (const auto& reference_token : reference_tokens) { - switch (ptr->type()) + if (ptr->is_object()) { - case detail::value_t::object: + const auto it = ptr->m_data.m_value.object->find(reference_token); + if (it == ptr->m_data.m_value.object->end()) { - const auto it = ptr->m_data.m_value.object->find(reference_token); - if (it == ptr->m_data.m_value.object->end()) - { - // we did not find the key in the object - return false; - } - - ptr = &it->second; - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) - { - // "-" always fails the range check - return false; - } - - // any parse failure (malformed index, or one that is syntactically - // valid but not representable as size_type) means the reference - // token cannot denote an existing array element -- contains() must - // not throw (see #5395), so it is treated as "not found" - typename BasicJsonType::size_type idx{}; - if (JSON_HEDLEY_UNLIKELY(parse_array_index(reference_token, idx) != array_index_status::ok)) - { - return false; - } - - if (idx >= ptr->m_data.m_value.array->size()) - { - // index out of range - return false; - } - - ptr = &(*ptr->m_data.m_value.array)[idx]; - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - { - // we do not expect primitive values if there is still a - // reference token to process + // we did not find the key in the object return false; } + + ptr = &it->second; + } + else if (ptr->is_array()) + { + if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token))) + { + // "-" always fails the range check + return false; + } + + // any parse failure (malformed index, or one that is syntactically + // valid but not representable as size_type) means the reference + // token cannot denote an existing array element -- contains() must + // not throw (see #5395), so it is treated as "not found" + typename BasicJsonType::size_type idx{}; + if (JSON_HEDLEY_UNLIKELY(parse_array_index(reference_token, idx) != array_index_status::ok)) + { + return false; + } + + if (idx >= ptr->m_data.m_value.array->size()) + { + // index out of range + return false; + } + + ptr = &(*ptr->m_data.m_value.array)[idx]; + } + else + { + // we do not expect primitive values if there is still a + // reference token to process + return false; } } @@ -24900,10 +24864,7 @@ class json_pointer } // check if a nonempty reference string begins with slash - if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) - { - 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)); - } + check_begins_with_slash(reference_string); // every slash starts a reference token const std::size_t size = reference_string.size(); @@ -24944,9 +24905,7 @@ class json_pointer JSON_ASSERT(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 - if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || - (reference_token[pos + 1] != '0' && - reference_token[pos + 1] != '1'))) + if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(reference_token, pos))) { JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } @@ -25103,50 +25062,34 @@ class json_pointer auto hint = object.end(); const auto enter = [&stack, &path, &object, &hint](const BasicJsonType & v) { - switch (v.type()) + if (v.is_array()) { - case detail::value_t::array: + if (v.m_data.m_value.array->empty()) { - if (v.m_data.m_value.array->empty()) - { - // flatten empty array as null - add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); - } - else - { - stack.emplace_back(&v, object_const_iterator(), path.size()); - } - return; + // flatten empty array as null + add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); } - - case detail::value_t::object: + else { - if (v.m_data.m_value.object->empty()) - { - // flatten empty object as null - add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); - } - else - { - stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size()); - } - return; + stack.emplace_back(&v, object_const_iterator(), path.size()); } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: + } + else if (v.is_object()) + { + if (v.m_data.m_value.object->empty()) { - // add a primitive value with its reference string - add_flattened(object, hint, path, v, detail::priority_tag<2> {}); - return; + // flatten empty object as null + add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {}); } + else + { + stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size()); + } + } + else + { + // add a primitive value with its reference string + add_flattened(object, hint, path, v, detail::priority_tag<2> {}); } }; @@ -25709,6 +25652,8 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include + // #include // #include @@ -26614,9 +26559,7 @@ class binary_writer write_cbor_head(0x40, N); // step 2: write each element - oa.write_characters( - reinterpret_cast(j.m_data.m_value.binary->data()), - N); + write_bytes(j.m_data.m_value.binary->data(), N); break; } @@ -26629,7 +26572,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, "CBOR"); + throw_on_discarded(j, input_format_t::cbor); } } @@ -26829,9 +26772,7 @@ class binary_writer } // step 2: write the byte string - oa.write_characters( - reinterpret_cast(j.m_data.m_value.binary->data()), - N); + write_bytes(j.m_data.m_value.binary->data(), N); break; } @@ -26845,7 +26786,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, "MessagePack"); + throw_on_discarded(j, input_format_t::msgpack); } } @@ -26929,7 +26870,7 @@ class binary_writer case value_t::array: { bool prefix_required = true; - const bool write_closer = write_ubjson_start_array(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + const bool write_closer = write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); { const auto& array = *j.m_data.m_value.array; @@ -26979,9 +26920,7 @@ class binary_writer if (use_type) { - oa.write_characters( - reinterpret_cast(j.m_data.m_value.binary->data()), - j.m_data.m_value.binary->size()); + write_bytes(j.m_data.m_value.binary->data(), j.m_data.m_value.binary->size()); } else { @@ -27007,7 +26946,7 @@ class binary_writer } bool prefix_required = true; - const bool write_closer = write_ubjson_start_object(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + const bool write_closer = write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); write_ubjson_members(j, use_count, use_type, prefix_required, use_bjdata, bjdata_version, depth); @@ -27021,7 +26960,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, use_bjdata ? "BJData" : "UBJSON"); + throw_on_discarded(j, use_bjdata ? input_format_t::bjdata : input_format_t::ubjson); } } @@ -27047,11 +26986,11 @@ class binary_writer @brief throws because @a j is discarded and cannot be serialized @throw type_error.321 always */ - JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const char* format_name) + JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const input_format_t format) { static_cast(j); // unused when JSON_NOEXCEPTION is defined - static_cast(format_name); - JSON_THROW(type_error::create(exception_id::discarded_value_used, concat("cannot serialize discarded value to ", format_name), &j)); + static_cast(format); + JSON_THROW(type_error::create(exception_id::discarded_value_used, concat("cannot serialize discarded value to ", input_format_name(format)), &j)); } /// how the range of integer type @a NumberType relates to that of @a TargetType @@ -27171,11 +27110,11 @@ class binary_writer } /*! - @brief throws because the integer @a j is too large for @a format_name + @brief throws because the integer @a j is too large for @a format @throw out_of_range.407 always */ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE - static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range) + static void throw_integer_out_of_range(const BasicJsonType& j, const input_format_t format, const char* range) { // neither std::to_string(), which has no overload for integer types // wider than 64 bits, nor dump(), which would also instantiate the @@ -27186,9 +27125,9 @@ class binary_writer ? integer_to_string(j.m_data.m_value.number_unsigned, std::integral_constant()) : integer_to_string(j.m_data.m_value.number_integer, std::integral_constant()); static_cast(number); // unused when JSON_NOEXCEPTION is defined - static_cast(format_name); + static_cast(format); static_cast(range); - JSON_THROW(out_of_range::create(exception_id::integer_too_large, concat("integer number ", number, " cannot be represented by ", format_name, " as it does not fit ", range), &j)); + JSON_THROW(out_of_range::create(exception_id::integer_too_large, concat("integer number ", number, " cannot be represented by ", input_format_name(format), " as it does not fit ", range), &j)); } // The exceptions below are thrown from functions of their own, out of line: @@ -27231,53 +27170,56 @@ class binary_writer JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j)); } + /// @brief write @a size bytes starting at @a data to the output adapter + JSON_HEDLEY_ALWAYS_INLINE + void write_bytes(const void* data, const std::size_t size) + { + oa.write_characters(static_cast(data), size); + } + + /// @brief write the MessagePack prefix of an array or object with @a N + /// elements: the fix marker @a fix_marker | n, or @a marker_16 or + /// @a marker_32 followed by the length + JSON_HEDLEY_ALWAYS_INLINE + void write_msgpack_container_prefix(const std::size_t N, const BasicJsonType& j, + const std::uint8_t fix_marker, const std::uint8_t marker_16, const std::uint8_t marker_32) + { + const auto n = to_msgpack_length(N, j); + if (n <= 15) + { + // fixarray, fixmap + oa.write_character(to_char_type(static_cast(fix_marker | n))); + } + else if (n <= (std::numeric_limits::max)()) + { + // array 16, map 16 + oa.write_character(to_char_type(marker_16)); + write_number(static_cast(n)); + } + else + { + // array 32, map 32 + oa.write_character(to_char_type(marker_32)); + write_number(static_cast(n)); + } + } + + JSON_HEDLEY_ALWAYS_INLINE void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j) { - const auto n = to_msgpack_length(N, j); - if (n <= 15) - { - // fixarray - oa.write_character(to_char_type(static_cast(0x90 | n))); - } - else if (n <= (std::numeric_limits::max)()) - { - // array 16 - oa.write_character(to_char_type(0xDC)); - write_number(static_cast(n)); - } - else - { - // array 32 - oa.write_character(to_char_type(0xDD)); - write_number(static_cast(n)); - } + write_msgpack_container_prefix(N, j, 0x90, 0xDC, 0xDD); } + JSON_HEDLEY_ALWAYS_INLINE void write_msgpack_object_prefix(const std::size_t N, const BasicJsonType& j) { - const auto n = to_msgpack_length(N, j); - if (n <= 15) - { - // fixmap - oa.write_character(to_char_type(static_cast(0x80 | (n & 0xF)))); - } - else if (n <= (std::numeric_limits::max)()) - { - // map 16 - oa.write_character(to_char_type(0xDE)); - write_number(static_cast(n)); - } - else - { - // map 32 - oa.write_character(to_char_type(0xDF)); - write_number(static_cast(n)); - } + write_msgpack_container_prefix(N, j, 0x80, 0xDE, 0xDF); } - /// @brief a CBOR, MessagePack, or BON8 array or object whose elements - /// @ref write_cbor_iterative, @ref write_msgpack_iterative, or - /// @ref write_bon8_iterative is still writing + /// @brief a CBOR, MessagePack, BON8, or (as the base of @ref ubjson_frame) + /// UBJSON array or object whose elements @ref write_container_iterative, + /// @ref write_bon8_iterative, or @ref write_ubjson_iterative is still + /// writing struct binary_container_frame { explicit binary_container_frame(const BasicJsonType* value_) noexcept @@ -27313,19 +27255,82 @@ class binary_writer typename BasicJsonType::array_t::const_iterator array_it{}; }; + /// @brief the CBOR specifics of @ref write_container_iterative + struct cbor_walk + { + static void array_head(binary_writer& w, const BasicJsonType& j) + { + w.write_cbor_head(0x80, j.m_data.m_value.array->size()); + } + + static void object_head(binary_writer& w, const BasicJsonType& j) + { + w.write_cbor_head(0xA0, j.m_data.m_value.object->size()); + } + + static void scalar(binary_writer& w, const BasicJsonType& j) + { + w.write_cbor(j); + } + + static void string(binary_writer& w, const string_t& key, const BasicJsonType& context) + { + w.write_cbor_string(key, context); + } + + template + static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object) + { + w.write_cbor_key(k, object); + } + }; + + /// @brief the MessagePack specifics of @ref write_container_iterative + struct msgpack_walk + { + static void array_head(binary_writer& w, const BasicJsonType& j) + { + w.write_msgpack_array_prefix(j.m_data.m_value.array->size(), j); + } + + static void object_head(binary_writer& w, const BasicJsonType& j) + { + w.write_msgpack_object_prefix(j.m_data.m_value.object->size(), j); + } + + static void scalar(binary_writer& w, const BasicJsonType& j) + { + w.write_msgpack(j); + } + + static void string(binary_writer& w, const string_t& key, const BasicJsonType& context) + { + w.write_msgpack_string(key, context); + } + + template + static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object) + { + w.write_msgpack_key(k, object); + } + }; + /*! - @brief write @a j with @ref write_cbor, or write its header and push a - frame for @ref write_cbor_iterative to continue with its elements + @brief write @a j with @a Walk::scalar, or write its header and push a + frame for @ref write_container_iterative to continue with its + elements A scalar, and an empty array or object, are written out in full: there is - nothing below them for @ref write_cbor_iterative to come back to, so + nothing below them for @ref write_container_iterative to come back to, so nothing is pushed for them. */ - void write_cbor_value_or_push(const BasicJsonType& j, std::vector& stack) + template + JSON_HEDLEY_ALWAYS_INLINE + void write_container_value_or_push(const BasicJsonType& j, std::vector& stack) { if (j.is_array()) { - write_cbor_head(0x80, j.m_data.m_value.array->size()); + Walk::array_head(*this, j); if (!j.m_data.m_value.array->empty()) { stack.emplace_back(&j); @@ -27335,7 +27340,7 @@ class binary_writer if (j.is_object()) { - write_cbor_head(0xA0, j.m_data.m_value.object->size()); + Walk::object_head(*this, j); if (!j.m_data.m_value.object->empty()) { stack.emplace_back(&j); @@ -27343,22 +27348,26 @@ class binary_writer return; } - write_cbor(j); + Walk::scalar(*this, j); } /*! @brief write out @a root and everything below it without the call stack - Emits the same bytes as @ref write_cbor, keeping the containers it has + Emits the same bytes as the recursive writer, keeping the containers it has entered on an explicit stack instead of descending into them. Only reached for values nested deeper than @ref recursion_depth_limit, which is why it - is not written for speed. + is not written for speed. Shared by CBOR and MessagePack, which differ only + in the @a Walk policy (@ref cbor_walk, @ref msgpack_walk); BON8 has its own + walker, as it tracks an open string run and closes a container with a + marker. */ - void write_cbor_iterative(const BasicJsonType& root) + template + void write_container_iterative(const BasicJsonType& root) { - // only a container with elements is ever pushed; see write_cbor_value_or_push + // only a container with elements is ever pushed; see write_container_value_or_push std::vector stack; - write_cbor_value_or_push(root, stack); + write_container_value_or_push(root, stack); while (!stack.empty()) { @@ -27376,7 +27385,7 @@ class binary_writer // read the child before pushing: entering it can move every frame const BasicJsonType* child = &(*current.array_it); ++stack.back().array_it; - write_cbor_value_or_push(*child, stack); + write_container_value_or_push(*child, stack); } else { @@ -27388,110 +27397,53 @@ class binary_writer } // the key is written directly (not via a temporary basic_json), - // with the object as diagnostics context, as in write_cbor - write_cbor_key(current.object_it->first, *current.value); + // with the object as diagnostics context, as in the recursive writer + Walk::key(*this, current.object_it->first, *current.value); const BasicJsonType* child = &(current.object_it->second); ++stack.back().object_it; - write_cbor_value_or_push(*child, stack); + write_container_value_or_push(*child, stack); } } } - /*! - @brief write @a j with @ref write_msgpack, or write its header and push a - frame for @ref write_msgpack_iterative to continue with its elements - - @sa @ref write_cbor_value_or_push - */ - void write_msgpack_value_or_push(const BasicJsonType& j, std::vector& stack) + void write_cbor_iterative(const BasicJsonType& root) { - if (j.is_array()) - { - write_msgpack_array_prefix(j.m_data.m_value.array->size(), j); - if (!j.m_data.m_value.array->empty()) - { - stack.emplace_back(&j); - } - return; - } - - if (j.is_object()) - { - write_msgpack_object_prefix(j.m_data.m_value.object->size(), j); - if (!j.m_data.m_value.object->empty()) - { - stack.emplace_back(&j); - } - return; - } - - write_msgpack(j); + write_container_iterative(root); } - /*! - @brief write out @a root and everything below it without the call stack - - @sa @ref write_cbor_iterative - */ void write_msgpack_iterative(const BasicJsonType& root) { - std::vector stack; - write_msgpack_value_or_push(root, stack); - - while (!stack.empty()) - { - const binary_container_frame current = stack.back(); - - if (current.value->is_array()) - { - const auto& array = *current.value->m_data.m_value.array; - if (current.array_it == array.cend()) - { - stack.pop_back(); - continue; - } - - const BasicJsonType* child = &(*current.array_it); - ++stack.back().array_it; - write_msgpack_value_or_push(*child, stack); - } - else - { - const auto& object = *current.value->m_data.m_value.object; - if (current.object_it == object.cend()) - { - stack.pop_back(); - continue; - } - - // as in write_cbor_iterative, the key is written directly with - // the object as diagnostics context - write_msgpack_key(current.object_it->first, *current.value); - const BasicJsonType* child = &(current.object_it->second); - ++stack.back().object_it; - write_msgpack_value_or_push(*child, stack); - } - } + write_container_iterative(root); } - /// @return true when a closing ']' still has to be written after the elements - bool write_ubjson_start_array(const BasicJsonType& j, const bool use_count, const bool use_type, - const bool add_prefix, const bool use_bjdata, bool& prefix_required) + /*! + @brief write the start of an array (@a IsArray) or object: its opening + marker, the optimized type, and the count + + @return true when a closing ']' or '}' still has to be written after the + elements + */ + template + bool write_ubjson_start_container(const BasicJsonType& j, const bool use_count, const bool use_type, + const bool add_prefix, const bool use_bjdata, bool& prefix_required) { prefix_required = true; if (add_prefix) { - oa.write_character(to_char_type('[')); + oa.write_character(to_char_type(IsArray ? '[' : '{')); } - if (use_type && !j.m_data.m_value.array->empty()) + const std::size_t size = IsArray ? j.m_data.m_value.array->size() : j.m_data.m_value.object->size(); + + if (use_type && size != 0) { if (!use_count) { throw_size_marker_required(j); } const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); - const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + // the first element of an array has been compared with itself + const bool same_prefix = std::all_of(IsArray ? j.begin() + 1 : j.begin(), j.end(), [this, first_prefix, use_bjdata](const BasicJsonType & v) { return ubjson_prefix(v, use_bjdata) == first_prefix; @@ -27503,8 +27455,9 @@ class binary_writer // those, at one byte per element, so the result can be read back. // Objects are not affected: every element is preceded by its key. const bool valueless_type = (first_prefix == 'Z' || first_prefix == 'T' || first_prefix == 'F'); - const bool excessive_valueless = valueless_type - && j.m_data.m_value.array->size() > detail::max_valueless_container_size; + const bool excessive_valueless = IsArray + && valueless_type + && size > detail::max_valueless_container_size; if (same_prefix && !excessive_valueless && !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix))) @@ -27517,46 +27470,7 @@ class binary_writer if (use_count) { - write_ubjson_count(j.m_data.m_value.array->size(), use_bjdata); - } - - return !use_count; - } - - /// @return true when a closing '}' still has to be written after the elements - bool write_ubjson_start_object(const BasicJsonType& j, const bool use_count, const bool use_type, - const bool add_prefix, const bool use_bjdata, bool& prefix_required) - { - prefix_required = true; - if (add_prefix) - { - oa.write_character(to_char_type('{')); - } - - if (use_type && !j.m_data.m_value.object->empty()) - { - if (!use_count) - { - throw_size_marker_required(j); - } - const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); - const bool same_prefix = std::all_of(j.begin(), j.end(), - [this, first_prefix, use_bjdata](const BasicJsonType & v) - { - return ubjson_prefix(v, use_bjdata) == first_prefix; - }); - - if (same_prefix && !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix))) - { - prefix_required = false; - oa.write_character(to_char_type('$')); - oa.write_character(first_prefix); - } - } - - if (use_count) - { - write_ubjson_count(j.m_data.m_value.object->size(), use_bjdata); + write_ubjson_count(size, use_bjdata); } return !use_count; @@ -27584,21 +27498,12 @@ class binary_writer /// @brief an object or array @ref write_ubjson_iterative is still writing /// the elements of - struct ubjson_frame + struct ubjson_frame : binary_container_frame { ubjson_frame(const BasicJsonType* value_, const bool prefix_required_) noexcept - : value(value_) + : binary_container_frame(value_) , prefix_required(prefix_required_) - { - if (value->is_object()) - { - object_it = value->m_data.m_value.object->cbegin(); - } - else - { - array_it = value->m_data.m_value.array->cbegin(); - } - } + {} // declared for GCC's -Weffc++, which asks for them in a class with // pointer members and a non-trivial destructor; the exception @@ -27610,13 +27515,9 @@ class binary_writer ubjson_frame& operator=(ubjson_frame&&) = default; ~ubjson_frame() = default; - /// the array or object being written - const BasicJsonType* value; /// whether value's elements each carry their own type marker; an /// optimized ($type) container writes it once for all of them instead bool prefix_required; - typename BasicJsonType::object_t::const_iterator object_it{}; - typename BasicJsonType::array_t::const_iterator array_it{}; }; /*! @@ -27627,7 +27528,7 @@ class binary_writer elements of an optimized container, and everything below the top level, never repeat it) - @sa @ref write_cbor_value_or_push + @sa @ref write_container_value_or_push */ void write_ubjson_value_or_push(const BasicJsonType& j, const bool add_prefix, const bool use_count, const bool use_type, const bool use_bjdata, const bjdata_version_t bjdata_version, @@ -27650,11 +27551,11 @@ class binary_writer bool prefix_required = true; if (is_array) { - write_ubjson_start_array(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); } else { - write_ubjson_start_object(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); + write_ubjson_start_container(j, use_count, use_type, add_prefix, use_bjdata, prefix_required); } const bool empty = is_array ? j.m_data.m_value.array->empty() : j.m_data.m_value.object->empty(); @@ -27664,7 +27565,7 @@ class binary_writer return; } - // write_ubjson_start_array/_object return !use_count, i.e. whether a + // write_ubjson_start_container returns !use_count, i.e. whether a // closer still has to be written; use_count is constant for the whole // document, so that is recomputed here instead of being carried along if (!use_count) @@ -27853,14 +27754,15 @@ class binary_writer { oa.write_character(to_char_type(element_type)); - if (error_handler == error_handler_t::keep || error_handler == error_handler_t::strict || is_valid_utf8(name)) + if (JSON_HEDLEY_LIKELY(error_handler == error_handler_t::keep || error_handler == error_handler_t::strict)) { - oa.write_characters(reinterpret_cast(name.data()), name.size()); + write_bytes(name.data(), name.size()); } else { - const string_t sanitized = sanitize_utf8(name, error_handler); - oa.write_characters(reinterpret_cast(sanitized.data()), sanitized.size()); + string_t storage; + const string_t& written = sanitize_utf8_for_bson_write(name, storage); + write_bytes(written.data(), written.size()); } // the terminating null byte is written explicitly rather than taken @@ -27936,16 +27838,11 @@ class binary_writer { write_bson_entry_header(name, 0x02); - const bool sanitize = error_handler != error_handler_t::keep - && error_handler != error_handler_t::strict - && !is_valid_utf8(value); - const string_t sanitized = sanitize ? sanitize_utf8(value, error_handler) : string_t{}; - const string_t& written = sanitize ? sanitized : value; + string_t storage; + const string_t& written = sanitize_utf8_for_bson_write(value, storage); write_number(to_bson_length(written.size() + 1ul), true); - oa.write_characters( - reinterpret_cast(written.data()), - written.size()); + write_bytes(written.data(), written.size()); // the terminating null byte is written explicitly rather than taken // from the buffer, so that string_t::data() need not be null-terminated oa.write_character(to_char_type(0x00)); @@ -28051,7 +27948,7 @@ class binary_writer write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); - oa.write_characters(reinterpret_cast(value.data()), value.size()); + write_bytes(value.data(), value.size()); } /*! @@ -28081,14 +27978,14 @@ class binary_writer case value_t::number_integer: if (JSON_HEDLEY_UNLIKELY(!integer_fits(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "BSON", "int64"); + throw_integer_out_of_range(j, input_format_t::bson, "int64"); } return calc_bson_integer_size(static_cast(j.m_data.m_value.number_integer)); case value_t::number_unsigned: if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "BSON", "uint64"); + throw_integer_out_of_range(j, input_format_t::bson, "uint64"); } return calc_bson_unsigned_size(conditional_static_cast(j.m_data.m_value.number_unsigned)); @@ -28099,7 +27996,7 @@ class binary_writer return 0ul; case value_t::discarded: - throw_on_discarded(j, "BSON"); + throw_on_discarded(j, input_format_t::bson); // LCOV_EXCL_START case value_t::object: @@ -28263,7 +28160,7 @@ class binary_writer // the terminating null byte is written explicitly rather than // taken from the buffer, so that string_t::data() need not be // null-terminated - oa.write_characters(reinterpret_cast(value.data()), value.size()); + write_bytes(value.data(), value.size()); oa.write_character(to_char_type(0x00)); return true; } @@ -28280,7 +28177,7 @@ class binary_writer p += sizeof(std::int32_t); *p++ = value.has_subtype() ? to_char_type(static_cast(value.subtype())) : to_char_type(0x00); oa.commit(static_cast(p - out)); - oa.write_characters(reinterpret_cast(value.data()), value.size()); + write_bytes(value.data(), value.size()); return true; } @@ -28328,7 +28225,7 @@ class binary_writer return write_bson_null(name); case value_t::discarded: - throw_on_discarded(j, "BSON"); + throw_on_discarded(j, input_format_t::bson); // LCOV_EXCL_START case value_t::object: @@ -29109,7 +29006,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "CBOR", "uint64"); + throw_integer_out_of_range(j, input_format_t::cbor, "uint64"); } write_cbor_head(0x00, conditional_static_cast(j.m_data.m_value.number_unsigned)); return; @@ -29119,7 +29016,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::cbor, "[-2^64, 2^64-1]"); } // CBOR does not differentiate between positive signed // integers and unsigned integers @@ -29130,7 +29027,7 @@ class binary_writer // a negative integer n is encoded as -1 - n if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(-1 - j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::cbor, "[-2^64, 2^64-1]"); } write_cbor_head(0x20, static_cast(-1 - j.m_data.m_value.number_integer)); } @@ -29206,7 +29103,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "MessagePack", "uint64"); + throw_integer_out_of_range(j, input_format_t::msgpack, "uint64"); } write_msgpack_unsigned(static_cast(j.m_data.m_value.number_unsigned)); return; @@ -29216,7 +29113,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::msgpack, "[-2^63, 2^64-1]"); } // MessagePack does not differentiate between positive // signed integers and unsigned integers. @@ -29226,7 +29123,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_min(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]"); + throw_integer_out_of_range(j, input_format_t::msgpack, "[-2^63, 2^64-1]"); } if (j.m_data.m_value.number_integer >= -32) { @@ -29360,40 +29257,37 @@ class binary_writer } /*! - @brief write an object key as a CBOR text string + @brief write an object key as a CBOR or MessagePack string (@a Walk) 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::value, int> = 0> + void write_object_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context) + { + Walk::string(*this, key, context); + } + + template>::value, int> = 0> + void write_object_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/) + { + Walk::scalar(*this, BasicJsonType(key)); + } + + /// @brief write an object key as a CBOR text string void write_cbor_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context) { - write_cbor_string(key, context); + write_object_key(key, context); } - template < typename Key = typename BasicJsonType::object_t::key_type, - enable_if_t < !std::is_convertible::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::value, int> = 0> + /// @brief write an object key as a MessagePack str 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::value, int > = 0 > - void write_msgpack_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/) - { - write_msgpack(BasicJsonType(key)); + write_object_key(key, context); } /// the longest string that is written, with its head of one or two @@ -29468,9 +29362,7 @@ class binary_writer write_cbor_head(0x60, sanitized.size()); // step 2: write the string - oa.write_characters( - reinterpret_cast(sanitized.data()), - sanitized.size()); + write_bytes(sanitized.data(), sanitized.size()); } ///////////// @@ -29551,9 +29443,7 @@ class binary_writer } // step 2: write the string - oa.write_characters( - reinterpret_cast(sanitized.data()), - sanitized.size()); + write_bytes(sanitized.data(), sanitized.size()); } //////////// @@ -29836,9 +29726,7 @@ class binary_writer oa.write_character(to_char_type('S')); } write_number_with_ubjson_prefix(sanitized.size(), true, use_bjdata); - oa.write_characters( - reinterpret_cast(sanitized.data()), - sanitized.size()); + write_bytes(sanitized.data(), sanitized.size()); } /*! @@ -30638,7 +30526,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(j.m_data.m_value.number_unsigned))) { - throw_integer_out_of_range(j, "BON8", "int64"); + throw_integer_out_of_range(j, input_format_t::bon8, "int64"); } write_bon8_integer(static_cast(j.m_data.m_value.number_unsigned)); string_open = false; @@ -30649,7 +30537,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!integer_fits(j.m_data.m_value.number_integer))) { - throw_integer_out_of_range(j, "BON8", "int64"); + throw_integer_out_of_range(j, input_format_t::bon8, "int64"); } write_bon8_integer(static_cast(j.m_data.m_value.number_integer)); string_open = false; @@ -30717,7 +30605,7 @@ class binary_writer case value_t::discarded: default: - throw_on_discarded(j, "BON8"); + throw_on_discarded(j, input_format_t::bon8); } } @@ -30728,7 +30616,7 @@ class binary_writer A scalar, and an empty array or object, are written out in full and not pushed. - @sa @ref write_cbor_value_or_push + @sa @ref write_container_value_or_push */ void write_bon8_value_or_push(const BasicJsonType& j, bool& string_open, std::vector& stack) { @@ -31114,7 +31002,7 @@ class binary_writer } else { - oa.write_characters(reinterpret_cast(s.data()), s.size()); + write_bytes(s.data(), s.size()); string_open = true; } } @@ -31205,6 +31093,31 @@ class binary_writer } } + /*! + @brief the write-pass counterpart of @ref sanitize_utf8_for_write for BSON + + The size pass (@ref calc_bson_entry_header_size, @ref + calc_bson_string_size) has already validated @a s, and thrown for @ref + error_handler_t::strict if it was ill-formed. Here, @ref + error_handler_t::strict therefore behaves like @ref error_handler_t::keep, + and @ref error_handler_t::replace / @ref error_handler_t::ignore produce + exactly the bytes the size pass measured. Hence no context is needed. + + @param[in] s the string (value or object key) to write + @param[out] storage backing storage for a sanitized copy + + @return a reference to @a s, or to @a storage once it holds a sanitized copy + */ + const string_t& sanitize_utf8_for_bson_write(const string_t& s, string_t& storage) const + { + if (error_handler == error_handler_t::keep || error_handler == error_handler_t::strict || is_valid_utf8(s)) + { + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call + } + storage = sanitize_utf8(s, error_handler); + return storage; + } + /// deleted: anything but a string_t would bind a temporary that dies before the returned reference is used template < typename T, enable_if_t < !std::is_same::value, int > = 0 > const string_t& sanitize_utf8_for_write(const T& /*s*/, const BasicJsonType& /*context*/, string_t& /*storage*/) const = delete; // NOLINT(hicpp-use-equals-delete,modernize-use-equals-delete): a private helper's guard, not part of the interface @@ -34294,6 +34207,28 @@ class serializer } } + /// write out the bytes pending in @ref string_buffer and reset @a bytes + JSON_HEDLEY_ALWAYS_INLINE void emit_pending(std::size_t& bytes) + { + if (bytes != 0) + { + put_buffer(string_buffer, bytes); + bytes = 0; + } + } + + /// write buffer and reset index if fewer than 13 bytes are left, as this + /// is the maximal number of bytes to be written ("\uxxxx\uxxxx\0") for one + /// code point + JSON_HEDLEY_ALWAYS_INLINE void reserve_string_buffer(std::size_t& bytes) + { + if (string_buffer.size() - bytes < 13) + { + put_buffer(string_buffer, bytes); + bytes = 0; + } + } + /*! @brief worker for @ref dump_escaped @@ -34352,11 +34287,7 @@ class serializer { // emit any bytes still pending in string_buffer first to // preserve output order, then write the run directly - if (bytes != 0) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + emit_pending(bytes); put_run(s, i, run); bytes_after_last_accept = 0; undumped_chars = 0; @@ -34377,11 +34308,10 @@ class serializer { // emit any bytes still pending in string_buffer first to // preserve output order - if (bytes != 0) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + emit_pending(bytes); + // deliberately not put_buffer(): a fixed 8-byte memcpy + // has no asserts and no write-through branch, which + // matters on the hot path const auto& escape = ascii_escapes()[data[i]]; if (JSON_HEDLEY_UNLIKELY(write_buffer.size() - write_buffer_pos < escape.size())) { @@ -34456,14 +34386,7 @@ class serializer string_buffer[bytes++] = s[i]; } - // write buffer and reset index; there must be 13 bytes - // left, as this is the maximal number of bytes to be - // written ("\uxxxx\uxxxx\0") for one code point - if (string_buffer.size() - bytes < 13) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + reserve_string_buffer(bytes); // remember the byte position of this accept bytes_after_last_accept = bytes; @@ -34515,14 +34438,7 @@ class serializer string_buffer[bytes++] = '\xBD'; } - // write buffer and reset index; there must be 13 bytes - // left, as this is the maximal number of bytes to be - // written ("\uxxxx\uxxxx\0") for one code point - if (string_buffer.size() - bytes < 13) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + reserve_string_buffer(bytes); bytes_after_last_accept = bytes; } @@ -34555,14 +34471,7 @@ class serializer string_buffer[bytes++] = s[i]; } - // write buffer and reset index; there must be 13 bytes - // left, as this is the maximal number of bytes to be - // written ("\uxxxx\uxxxx\0") for one code point - if (string_buffer.size() - bytes < 13) - { - put_buffer(string_buffer, bytes); - bytes = 0; - } + reserve_string_buffer(bytes); bytes_after_last_accept = bytes; undumped_chars = 0; @@ -34928,30 +34837,17 @@ class serializer */ static const std::array& ascii_copyable() noexcept { - static const std::array copyable = - { - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 0x50 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 0x70 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0 - } - }; + // constant-initialized from the predicate: no runtime loop, no guard + static constexpr std::array copyable = make_ascii_copyable(make_index_sequence<256> {}); return copyable; } + template + static constexpr std::array make_ascii_copyable(index_sequence /*unused*/) noexcept + { + return {{ static_cast(is_ascii_copyable(static_cast(Is)) ? 1 : 0)... }}; + } + /*! @brief the JSON form of each character below 0x60 @@ -35080,7 +34976,6 @@ class serializer */ JSON_HEDLEY_NEVER_INLINE std::size_t write_u_escapes(const unsigned char* data, std::size_t n) { - constexpr const char* nibble_to_hex = "0123456789abcdef"; std::size_t i = 0; while (i < n && data[i] >= 0x80) { @@ -35117,26 +35012,21 @@ class serializer char* d = write_buffer.data() + write_buffer_pos; if (codepoint > 0xFFFFu) { - d = put_u_escape(d, static_cast(0xD7C0u + (codepoint >> 10u)), nibble_to_hex); + d = put_u_escape(d, static_cast(0xD7C0u + (codepoint >> 10u))); codepoint = 0xDC00u + (codepoint & 0x3FFu); } - d = put_u_escape(d, static_cast(codepoint), nibble_to_hex); + d = put_u_escape(d, static_cast(codepoint)); write_buffer_pos = static_cast(d - write_buffer.data()); i += length; } return i; } - static char* put_u_escape(char* d, const std::uint16_t codeunit, const char* nibble_to_hex) noexcept + static char* put_u_escape(char* d, const std::uint16_t codeunit) noexcept { - const auto unit = static_cast(codeunit); d[0] = '\\'; d[1] = 'u'; - d[2] = nibble_to_hex[(unit >> 12u) & 0x0Fu]; - d[3] = nibble_to_hex[(unit >> 8u) & 0x0Fu]; - d[4] = nibble_to_hex[(unit >> 4u) & 0x0Fu]; - d[5] = nibble_to_hex[unit & 0x0Fu]; - return d + 6; + return hex4_lower(codeunit, d + 2); } /*! @@ -35145,7 +35035,7 @@ class serializer * Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the * string escaping hot path. It writes exactly six characters ('\\', 'u' and * four hex digits) at position @a pos of @a string_buffer via a nibble - * lookup table, avoiding the format-string parsing and locale machinery of + * lookup table (@ref hex4_lower), avoiding the format-string parsing and locale machinery of * `snprintf`. Advances @a pos by the number of bytes written (6). * * @param[in] pos position in @a string_buffer to write at; there must @@ -35155,13 +35045,9 @@ class serializer void write_u_escape(std::size_t& pos, std::uint16_t codeunit) noexcept { JSON_ASSERT(string_buffer.size() - pos >= 6); - constexpr const char* nibble_to_hex = "0123456789abcdef"; string_buffer[pos + 0] = '\\'; string_buffer[pos + 1] = 'u'; - string_buffer[pos + 2] = nibble_to_hex[(codeunit >> 12u) & 0x0Fu]; - string_buffer[pos + 3] = nibble_to_hex[(codeunit >> 8u) & 0x0Fu]; - string_buffer[pos + 4] = nibble_to_hex[(codeunit >> 4u) & 0x0Fu]; - string_buffer[pos + 5] = nibble_to_hex[codeunit & 0x0Fu]; + hex4_lower(codeunit, &string_buffer[pos + 2]); pos += 6; } @@ -35677,6 +35563,19 @@ private: Container::pop_back(); } + /// @brief remove the entry with the given key; returns the number of removed entries + template + size_type erase_impl(const K& key) + { + const auto it = find_impl(*this, key); + if (it != this->end()) + { + erase_at(it); + return 1; + } + return 0; + } + public: template::value, int> = 0> @@ -35766,26 +35665,14 @@ public: size_type erase(const key_type& key) { - const auto it = find_impl(*this, key); - if (it != this->end()) - { - erase_at(it); - return 1; - } - return 0; + return erase_impl(key); } template::value, int> = 0> size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) { - const auto it = find_impl(*this, key); - if (it != this->end()) - { - erase_at(it); - return 1; - } - return 0; + return erase_impl(key); } iterator erase(iterator pos) @@ -36279,9 +36166,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = - detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.', - std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.', - std::to_string(NLOHMANN_JSON_VERSION_PATCH)); + detail::concat(detail::to_string(static_cast(NLOHMANN_JSON_VERSION_MAJOR)), '.', + detail::to_string(static_cast(NLOHMANN_JSON_VERSION_MINOR)), '.', + detail::to_string(static_cast(NLOHMANN_JSON_VERSION_PATCH))); result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; @@ -36303,10 +36190,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #elif defined(__clang__) result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( - std::to_string(__GNUC__), '.', - std::to_string(__GNUC_MINOR__), '.', - std::to_string(__GNUC_PATCHLEVEL__)) + result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( + detail::to_string(static_cast(__GNUC__)), '.', + detail::to_string(static_cast(__GNUC_MINOR__)), '.', + detail::to_string(static_cast(__GNUC_PATCHLEVEL__))) } }; #elif defined(__HP_aCC) @@ -36324,9 +36211,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #endif #if defined(_MSVC_LANG) - result["compiler"]["c++"] = std::to_string(_MSVC_LANG); + result["compiler"]["c++"] = detail::to_string(static_cast(_MSVC_LANG)); #elif defined(__cplusplus) - result["compiler"]["c++"] = std::to_string(__cplusplus); + result["compiler"]["c++"] = detail::to_string(static_cast(__cplusplus)); #else result["compiler"]["c++"] = "unknown"; #endif @@ -36637,23 +36524,15 @@ private: // true if v is not an array/object, or is an already-empty one static bool has_no_children(const basic_json& v) noexcept { - switch (v.m_data.m_type) + if (v.m_data.m_type == value_t::array) { - case value_t::array: - return v.m_data.m_value.array->empty(); - case value_t::object: - return v.m_data.m_value.object->empty(); - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return true; + return v.m_data.m_value.array->empty(); } + if (v.m_data.m_type == value_t::object) + { + return v.m_data.m_value.object->empty(); + } + return true; } // true if no child of the non-empty array/object v has children of @@ -36758,28 +36637,17 @@ private: } public: - void destroy_string() noexcept + template + static void destroy_pointer(T*& ptr) noexcept { - if (string == nullptr) + if (ptr == nullptr) { // not initialized (e.g., due to exception in the ctor) return; } - AllocatorType alloc; - std::allocator_traits::destroy(alloc, string); - std::allocator_traits::deallocate(alloc, string, 1); - } - - void destroy_binary() noexcept - { - if (binary == nullptr) - { - // not initialized (e.g., due to exception in the ctor) - return; - } - AllocatorType alloc; - std::allocator_traits::destroy(alloc, binary); - std::allocator_traits::deallocate(alloc, binary, 1); + AllocatorType alloc; + std::allocator_traits::destroy(alloc, ptr); + std::allocator_traits::deallocate(alloc, ptr, 1); } // t must be value_t::array or value_t::object @@ -36945,11 +36813,11 @@ public: switch (t) { case value_t::string: - destroy_string(); + destroy_pointer(string); break; case value_t::binary: - destroy_binary(); + destroy_pointer(binary); break; case value_t::object: @@ -37011,36 +36879,19 @@ public: void set_parents() { #if JSON_DIAGNOSTICS - switch (m_data.m_type) + if (m_data.m_type == value_t::array) { - case value_t::array: + for (auto& element : *m_data.m_value.array) { - for (auto& element : *m_data.m_value.array) - { - element.m_parent = this; - } - break; + element.m_parent = this; } - - case value_t::object: + } + else if (m_data.m_type == value_t::object) + { + for (auto& element : *m_data.m_value.object) { - for (auto& element : *m_data.m_value.object) - { - element.second.m_parent = this; - } - break; + element.second.m_parent = this; } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - break; } #endif } @@ -38488,8 +38339,7 @@ public: case value_t::string: case value_t::binary: { - if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() - || !last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_UNLIKELY(!primitive_range_is_complete(first, last))) { detail::throw_literal(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object); } @@ -38963,6 +38813,134 @@ public: JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type"))); } + /// @throw type_error.309 always, as insert() is not defined for the type of @a j + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE + static void throw_insert_wrong_type(const basic_json& j) + { + detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", j); + } + + /// @throw invalid_iterator.202 always, as an iterator belongs to another value than @a obj + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE + static void throw_iterator_from_other_value(const basic_json* obj) + { + detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", obj); + } + + /// @throw type_error.310 always, as swap() with the given operand is not defined for the type of @a j + JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE + static void throw_swap_wrong_type(const char* operation, const basic_json& j) + { + detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, operation, j); + } + + /// exchange the stored @a Stored value (if the value has that type) with @a other + template + void swap_impl(Other& other, const char* operation) + { + Stored* const stored = get_impl_ptr(static_cast(nullptr)); + if (JSON_HEDLEY_UNLIKELY(stored == nullptr)) + { + throw_swap_wrong_type(operation, *this); + } + + using std::swap; + swap(*stored, other); + } + + /// whether [first, last) spans the whole of a primitive value + template + JSON_HEDLEY_ALWAYS_INLINE + static bool primitive_range_is_complete(const IteratorType& first, const IteratorType& last) noexcept + { + return first.m_it.primitive_iterator.is_begin() && last.m_it.primitive_iterator.is_end(); + } + + /// destroy the current (primitive) value and become null + void reset_to_null_impl() + { + m_data.m_value.destroy(m_data.m_type); + m_data.m_value = {}; + m_data.m_type = value_t::null; + assert_invariant(); + } + + /// append @a val (moved or copied) to the array; a null value becomes an array + template + JSON_HEDLEY_ALWAYS_INLINE + void push_back_array_impl(ValueType&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); + } + + // transform a null object into an array + if (is_null()) + { + convert_null_to(); + } + + // add the element to the array + const auto old_capacity = container_capacity(); + m_data.m_value.array->push_back(std::forward(val)); + set_parent_after_insert(m_data.m_value.array->back(), old_capacity); + } + + /// parse the input behind an already created adapter + template + static basic_json parse_impl(InputAdapterType&& adapter, + parser_callback_t cb, + const bool allow_exceptions, + const bool ignore_comments, + const bool ignore_trailing_commas) + { + basic_json result; + parser(std::forward(adapter), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); + return result; + } + + /// check the input behind an already created adapter + template + static bool accept_impl(InputAdapterType&& adapter, + const bool ignore_comments, + const bool ignore_trailing_commas) + { + return parser(std::forward(adapter), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + } + + /// generate SAX events from the input behind an already created adapter + template + static bool sax_parse_impl(InputAdapterType ia, SAX* sax, + const input_format_t format, + const bool strict, + const bool ignore_comments, + const bool ignore_trailing_commas, + const cbor_tag_handler_t tag_handler) + { + if (format == input_format_t::json) + { + return parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict); + } + return detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + } + + /// serialize @a j into a new vector; @a write receives the vector-backed writer + template + static std::vector to_binary_vector(const basic_json& j, const error_handler_t error_handler, WriteFunction write) + { + std::vector result; + result.reserve(detail::binary_initial_capacity(j)); + { + // the sink sets the size of result when it is destroyed, which + // must happen before result is returned (without NRVO, it is moved) + auto writer = vector_writer(result, error_handler); + write(writer); + } + return result; + } + /// @throw out_of_range.401 always, as @a idx is out of range of @a j JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_array_index_out_of_range(size_type idx, const basic_json& j) @@ -39945,7 +39923,7 @@ public: // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } IteratorType result = end(); @@ -39964,10 +39942,7 @@ public: detail::throw_literal(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 = {}; - m_data.m_type = value_t::null; - assert_invariant(); + reset_to_null_impl(); break; } @@ -40017,16 +39992,12 @@ public: case value_t::string: case value_t::binary: { - if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() - || !last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_UNLIKELY(!primitive_range_is_complete(first, last))) { detail::throw_literal(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 = {}; - m_data.m_type = value_t::null; - assert_invariant(); + reset_to_null_impl(); break; } @@ -40507,34 +40478,20 @@ public: JSON_HEDLEY_WARN_UNUSED_RESULT size_type max_size() const noexcept { - switch (m_data.m_type) + if (m_data.m_type == value_t::array) { - case value_t::array: - { - // delegate call to array_t::max_size() - return m_data.m_value.array->max_size(); - } - - case value_t::object: - { - // delegate call to object_t::max_size() - return m_data.m_value.object->max_size(); - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - // all other types have max_size() == size() - return size(); - } + // delegate call to array_t::max_size() + return m_data.m_value.array->max_size(); } + + if (m_data.m_type == value_t::object) + { + // delegate call to object_t::max_size() + return m_data.m_value.object->max_size(); + } + + // all other types have max_size() == size() + return size(); } /// @} @@ -40612,22 +40569,7 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(basic_json&& val) { - // push_back only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) - { - detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); - } - - // transform a null object into an array - if (is_null()) - { - convert_null_to(); - } - - // add the element to the array (move semantics) - const auto old_capacity = container_capacity(); - m_data.m_value.array->push_back(std::move(val)); - set_parent_after_insert(m_data.m_value.array->back(), old_capacity); + push_back_array_impl(std::move(val)); // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor } @@ -40643,22 +40585,7 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(const basic_json& val) { - // push_back only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) - { - detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); - } - - // transform a null object into an array - if (is_null()) - { - convert_null_to(); - } - - // add the element to the array - const auto old_capacity = container_capacity(); - m_data.m_value.array->push_back(val); - set_parent_after_insert(m_data.m_value.array->back(), old_capacity); + push_back_array_impl(val); } /// @brief add an object to an array @@ -40804,13 +40731,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // insert to array and return iterator @@ -40824,13 +40751,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // moving into a local first keeps this safe even if val aliases @@ -40846,13 +40773,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // insert to array and return iterator @@ -40866,13 +40793,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // check if range iterators belong to the same JSON object @@ -40903,13 +40830,13 @@ public: // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - detail::throw_literal(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this); + throw_iterator_from_other_value(this); } // copy the values first: ilist may refer to elements of this array @@ -40931,7 +40858,7 @@ public: // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); + throw_insert_wrong_type(*this); } // check if range iterators belong to the same JSON object @@ -41173,82 +41100,37 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for arrays - if (JSON_HEDLEY_LIKELY(is_array())) - { - using std::swap; - swap(*(m_data.m_value.array), other); - set_parents(); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(array_t&)", *this); - } + swap_impl(other, "swap(array_t&)"); + set_parents(); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - using std::swap; - swap(*(m_data.m_value.object), other); - set_parents(); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(object_t&)", *this); - } + swap_impl(other, "swap(object_t&)"); + set_parents(); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for strings - if (JSON_HEDLEY_LIKELY(is_string())) - { - using std::swap; - swap(*(m_data.m_value.string), other); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(string_t&)", *this); - } + swap_impl(other, "swap(string_t&)"); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { - // swap only works for binary values - if (JSON_HEDLEY_LIKELY(is_binary())) - { - using std::swap; - swap(*(m_data.m_value.binary), other); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t&)", *this); - } + swap_impl(other, "swap(binary_t&)"); } /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) { - // swap only works for binary values - if (JSON_HEDLEY_LIKELY(is_binary())) - { - using std::swap; - swap(*(m_data.m_value.binary), other); - } - else - { - detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t::container_type&)", *this); - } + swap_impl(other, "swap(binary_t::container_type&)"); } /// @} @@ -41929,10 +41811,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - basic_json result; - auto p = parser(detail::input_adapter(std::forward(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); - p.parse(true, result); - return result; + return parse_impl(detail::input_adapter(std::forward(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } /// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support) @@ -41947,10 +41826,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - basic_json result; - auto p = parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); - p.parse(true, result); - return result; + return parse_impl(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } JSON_HEDLEY_WARN_UNUSED_RESULT @@ -41964,10 +41840,7 @@ public: = delete; #else { - basic_json result; - auto p = parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); - p.parse(true, result); - return result; + return parse_impl(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } #endif @@ -41979,7 +41852,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + return accept_impl(detail::input_adapter(std::forward(i)), ignore_comments, ignore_trailing_commas); } /// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support) @@ -41991,7 +41864,7 @@ public: const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + return accept_impl(detail::input_adapter(std::move(first), std::move(last)), ignore_comments, ignore_trailing_commas); } JSON_HEDLEY_WARN_UNUSED_RESULT @@ -42003,7 +41876,7 @@ public: = delete; #else { - return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); + return accept_impl(i.get(), ignore_comments, ignore_trailing_commas); } #endif @@ -42018,10 +41891,7 @@ public: const bool ignore_trailing_commas = false, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { - auto ia = detail::input_adapter(std::forward(i)); - return format == input_format_t::json - ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + return sax_parse_impl(detail::input_adapter(std::forward(i)), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler); } /// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support) @@ -42036,10 +41906,7 @@ public: const bool ignore_trailing_commas = false, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { - auto ia = detail::input_adapter(std::move(first), std::move(last)); - return format == input_format_t::json - ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + return sax_parse_impl(detail::input_adapter(std::move(first), std::move(last)), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler); } /// @brief generate SAX events @@ -42073,12 +41940,7 @@ public: = delete; #else { - auto ia = i.get(); - return format == input_format_t::json - // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) - // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - : detail::binary_reader(std::move(ia), format).sax_parse(sax, strict, tag_handler); + return sax_parse_impl(i.get(), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler); } #endif #if defined(__clang__) @@ -42226,10 +42088,10 @@ public: static std::vector to_cbor(const basic_json& j, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_cbor(j); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_cbor(j); + }); } /// @brief create a CBOR serialization of a given JSON value @@ -42257,10 +42119,10 @@ public: static std::vector to_msgpack(const basic_json& j, const error_handler_t error_handler = error_handler_t::keep) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_msgpack(j); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_msgpack(j); + }); } /// @brief create a MessagePack serialization of a given JSON value @@ -42290,10 +42152,10 @@ public: const bool use_type = false, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_ubjson(j, use_size, use_type); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_ubjson(j, use_size, use_type); + }); } /// @brief create a UBJSON serialization of a given JSON value @@ -42326,10 +42188,10 @@ public: const bjdata_version_t version = bjdata_version_t::draft2, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_ubjson(j, use_size, use_type, true, true, version); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_ubjson(j, use_size, use_type, true, true, version); + }); } /// @brief create a BJData serialization of a given JSON value @@ -42361,10 +42223,10 @@ public: static std::vector to_bson(const basic_json& j, const error_handler_t error_handler = detail::binary_writer_default_error_handler()) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result, error_handler).write_bson(j); - return result; + return to_binary_vector(j, error_handler, [&](vector_binary_writer& w) + { + w.write_bson(j); + }); } /// @brief create a BSON serialization of a given JSON value @@ -42391,10 +42253,10 @@ public: /// @sa https://json.nlohmann.me/api/basic_json/to_bon8/ static std::vector to_bon8(const basic_json& j) { - std::vector result; - result.reserve(detail::binary_initial_capacity(j)); - vector_writer(result).write_bon8(j); - return result; + return to_binary_vector(j, detail::binary_writer_default_error_handler(), [&](vector_binary_writer& w) + { + w.write_bon8(j); + }); } /// @brief create a BON8 serialization of a given JSON value @@ -43730,82 +43592,68 @@ public: return; } - switch (source.type()) + if (detail::is_primitive(source.type())) { - case value_t::array: + // both primitive types: replace value + diff_replace(result, path, target); + return; + } + + if (source.is_array()) + { + const std::size_t path_length = path.size(); + + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) { + // recursive call to compare array values at index i + if (!(source[i] == target[i])) + { + diff_append_index(path, i); + diff_unequal(result, source[i], target[i], path, depth + 1); + path.resize(path_length); + } + ++i; + } + + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + diff_array_tails(result, source, target, path, i); + } + else + { + diff_target_values target_values; + std::vector added; + if (diff_object_keys(result, source, target, path, target_values, added)) + { + // fast path: target_values holds, for every member of + // source in order, its value in target -- so no further + // lookup is needed. Deleted keys are interleaved here + // too, in source's original order, to match the + // historical (pre-reordering-aware) output order. const std::size_t path_length = path.size(); - - // first pass: traverse common elements std::size_t i = 0; - while (i < source.size() && i < target.size()) + for (auto it = source.cbegin(); it != source.cend(); ++it, ++i) { - // recursive call to compare array values at index i - if (!(source[i] == target[i])) + if (target_values[i] != nullptr) { - diff_append_index(path, i); - diff_unequal(result, source[i], target[i], path, depth + 1); - path.resize(path_length); - } - ++i; - } - - // We now reached the end of at least one array - // in a second pass, traverse the remaining elements - diff_array_tails(result, source, target, path, i); - break; - } - - case value_t::object: - { - diff_target_values target_values; - std::vector added; - if (diff_object_keys(result, source, target, path, target_values, added)) - { - // fast path: target_values holds, for every member of - // source in order, its value in target -- so no further - // lookup is needed. Deleted keys are interleaved here - // too, in source's original order, to match the - // historical (pre-reordering-aware) output order. - const std::size_t path_length = path.size(); - std::size_t i = 0; - for (auto it = source.cbegin(); it != source.cend(); ++it, ++i) - { - if (target_values[i] != nullptr) + if (!(it.value() == *target_values[i])) { - if (!(it.value() == *target_values[i])) - { - diff_append_key(path, it.key()); - diff_unequal(result, it.value(), *target_values[i], path, depth + 1); - path.resize(path_length); - } - } - else - { - // found a key that is not in target -> remove it - diff_remove(result, diff_key_path(path, it.key())); + diff_append_key(path, it.key()); + diff_unequal(result, it.value(), *target_values[i], path, depth + 1); + path.resize(path_length); } } - - // append the "add" operations for brand-new keys - diff_added(result, added, path); + else + { + // found a key that is not in target -> remove it + diff_remove(result, diff_key_path(path, it.key())); + } } - break; - } - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - // both primitive types: replace value - diff_replace(result, path, target); - break; + // append the "add" operations for brand-new keys + diff_added(result, added, path); } } } @@ -43851,46 +43699,31 @@ public: return; } - switch (s.type()) + if (detail::is_primitive(s.type())) { - case value_t::array: - { - stack.emplace_back(&s, &t, current_path.size()); - return; - } - - case value_t::object: - { - std::vector target_values; - std::vector added; - if (diff_object_keys(result, s, t, current_path, target_values, added)) - { - // fast path: the frame walks source in lockstep with - // common_keys, as diff_recursively does, and appends - // added_ops once all members are done - stack.emplace_back(&s, &t, current_path.size()); - stack.back().member = s.cbegin(); - stack.back().target_values = std::move(target_values); - stack.back().added = std::move(added); - } - return; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - // both primitive types: replace value - diff_replace(result, current_path, t); - return; - } + // both primitive types: replace value + diff_replace(result, current_path, t); + return; } + + if (s.is_array()) + { + stack.emplace_back(&s, &t, current_path.size()); + return; + } + std::vector target_values; + std::vector added; + if (diff_object_keys(result, s, t, current_path, target_values, added)) + { + // fast path: the frame walks source in lockstep with + // common_keys, as diff_recursively does, and appends + // added_ops once all members are done + stack.emplace_back(&s, &t, current_path.size()); + stack.back().member = s.cbegin(); + stack.back().target_values = std::move(target_values); + stack.back().added = std::move(added); + } + return; }; enter(source, target); diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index 1e2f8f64c..338b55492 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -2819,32 +2819,52 @@ namespace view // Exceptions are thrown out of line, so that the accessors that may throw stay // small enough to be inlined. -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const char* prefix, const char* type) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const char* prefix, const char* type) { NLOHMANN_VIEW_THROW(type_error::create(id, concat(prefix, type), nullptr)); } -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const std::string& msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const std::string& msg) { NLOHMANN_VIEW_THROW(type_error::create(id, msg, nullptr)); } -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(int id, const std::string& msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(exception_id id, const std::string& msg) { NLOHMANN_VIEW_THROW(out_of_range::create(id, msg, nullptr)); } -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(int id, const char* msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(exception_id id, const char* msg) { NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr)); } /// a parse error without a position (as those of json_pointer) -[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(int id, const std::string& msg) +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(exception_id id, const std::string& msg) { NLOHMANN_VIEW_THROW(parse_error::create(id, 0, msg, nullptr)); } +/// out_of_range.401: the array index @a idx is not below the size +template +[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_out_of_range(const Index idx) +{ + throw_out_of_range(exception_id::array_index_out_of_range, concat("array index ", std::to_string(idx), " is out of range")); +} + +/// out_of_range.402: the JSON Pointer token '-' of an array of @a size elements +[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_array_index_past_end(std::size_t size) +{ + throw_out_of_range(exception_id::pointer_past_the_end_index, concat("array index '-' (", std::to_string(size), ") is out of range")); +} + +/// out_of_range.403: the object has no member @a key +template +[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_key_not_found(const Key& key) +{ + throw_out_of_range(exception_id::key_not_found, concat("key '", key, "' not found")); +} + /// SAX handler of the error replay: accepts every event without storing it, /// and throws the exception the parser reports template @@ -2943,7 +2963,7 @@ template if (f.code == error_code::input_too_large) { // (the limit is detail::view::max_input_size(): 4 GiB minus 16 bytes) - NLOHMANN_VIEW_THROW(out_of_range::create(416, "input of 4294967280 bytes or more is not supported by json_document", nullptr)); + NLOHMANN_VIEW_THROW(out_of_range::create(exception_id::document_too_large, "input of 4294967280 bytes or more is not supported by json_document", nullptr)); } throwing_sax sax; const bool accepted = BasicJsonType::sax_parse(src, src + size, &sax, input_format_t::json, true, ignore_comments, ignore_trailing_commas); @@ -2963,7 +2983,7 @@ template } } pos.chars_read_current_line = off + 1 - line_start; - NLOHMANN_VIEW_THROW(parse_error::create(101, pos, "syntax error while parsing value", nullptr)); + NLOHMANN_VIEW_THROW(parse_error::create(exception_id::syntax_error, pos, "syntax error while parsing value", nullptr)); // LCOV_EXCL_STOP } @@ -3038,7 +3058,7 @@ inline std::size_t text_capacity(std::size_t cap, std::size_t used, std::size_t constexpr std::size_t limit = 0xFFFFFFFFu; if (NLOHMANN_VIEW_UNLIKELY(used > limit || n > limit - used)) { - throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document"); + throw_out_of_range(exception_id::document_too_large, "edits of 4 GiB or more are not supported by json_document"); } const std::size_t needed = used + n; const std::size_t wanted = needed + (std::min)(limit - needed, std::size_t{256}); @@ -3089,7 +3109,7 @@ inline void reserve_moved(document_data& d) document_data::edit_state& e = edit_state_of(d); if (e.moved.size() >= 0xFFFFFFFFu) { - throw_out_of_range(416, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE + throw_out_of_range(exception_id::document_too_large, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE } if (e.moved.size() == e.moved.capacity() || e.moved_cap.size() == e.moved_cap.capacity()) { @@ -3719,11 +3739,6 @@ struct first_true : std::integral_constant < int, 1 + firs inline void check_utf8(const char* s, std::size_t n) { const auto* const p = reinterpret_cast(s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) - const auto hex = [](unsigned char c) - { - constexpr const char* digits = "0123456789ABCDEF"; - return std::string{digits[c >> 4u], digits[c & 0xFu]}; - }; for (std::size_t i = 0; i < n;) { const unsigned char c = p[i]; @@ -3732,39 +3747,22 @@ inline void check_utf8(const char* s, std::size_t n) ++i; continue; } - std::size_t len = 0; - unsigned char lo = 0x80; - unsigned char hi = 0xBF; - if (c >= 0xC2 && c <= 0xDF) + const utf8_sequence_shape shape = utf8_shape(c); + const std::size_t len = shape.length; + if (len == 0) { - len = 2; - } - else if (c >= 0xE0 && c <= 0xEF) - { - len = 3; - lo = c == 0xE0 ? 0xA0 : 0x80; - hi = c == 0xED ? 0x9F : 0xBF; - } - else if (c >= 0xF0 && c <= 0xF4) - { - len = 4; - lo = c == 0xF0 ? 0x90 : 0x80; - hi = c == 0xF4 ? 0x8F : 0xBF; - } - else - { - throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex(c))); + throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_byte(c))); } for (std::size_t k = 1; k < len; ++k) { if (i + k == n) { - throw_type_error(316, concat("incomplete UTF-8 string; last byte: 0x", hex(p[n - 1]))); + throw_type_error(exception_id::invalid_utf8, concat("incomplete UTF-8 string; last byte: 0x", hex_byte(p[n - 1]))); } const unsigned char b = p[i + k]; - if (b < (k == 1 ? lo : 0x80) || b > (k == 1 ? hi : 0xBF)) + if (b < (k == 1 ? shape.second_lo : 0x80) || b > (k == 1 ? shape.second_hi : 0xBF)) { - throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex(b))); + throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex_byte(b))); } } i += len; @@ -3812,7 +3810,7 @@ class editor node* const o = own(object); if (o->kind != static_cast(value_t::object) && o->kind != static_cast(value_t::null)) { - throw_type_error(305, "cannot use operator[] with a string argument with ", object.type_name()); + throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name()); } check_utf8(key.data(), key.size()); const encoded e = encode(std::forward(value)); @@ -3863,7 +3861,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array)) { - throw_type_error(305, "cannot use operator[] with a numeric argument with ", array.type_name()); + throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name()); } check_index(idx, a->len); const encoded e = encode(std::forward(value)); @@ -3880,7 +3878,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array) && a->kind != static_cast(value_t::null)) { - throw_type_error(308, "cannot use push_back() with ", array.type_name()); + throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name()); } const encoded e = encode(std::forward(value)); if (a->kind == static_cast(value_t::null)) @@ -3904,7 +3902,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array)) { - throw_type_error(309, "cannot use insert() with ", array.type_name()); + throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name()); } check_index(idx, a->len + 1); const encoded e = encode(std::forward(value)); @@ -3924,7 +3922,7 @@ class editor node* const o = own(object); if (o->kind != static_cast(value_t::object)) { - throw_type_error(307, "cannot use erase() with ", object.type_name()); + throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name()); } for (const node* k = nav::first(m_doc, o), *end = nav::end(m_doc, o); k != end; k = document_data::after(k + 1)) { @@ -3942,7 +3940,7 @@ class editor node* const a = own(array); if (a->kind != static_cast(value_t::array)) { - throw_type_error(307, "cannot use erase() with ", array.type_name()); + throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name()); } check_index(idx, a->len); node* const h = block_of(m_doc, a, 0); @@ -3965,7 +3963,7 @@ class editor { if (NLOHMANN_VIEW_UNLIKELY(v.m_doc != &m_doc || v.m_node == nullptr)) { - throw_invalid_iterator(202, "view does not belong to this document"); + throw_invalid_iterator(exception_id::iterator_from_other_value, "view does not belong to this document"); } edit_state_of(m_doc); return const_cast(v.m_node); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to this document @@ -3975,7 +3973,7 @@ class editor { if (idx >= limit) { - throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range")); + throw_array_index_out_of_range(idx); } } @@ -4116,7 +4114,7 @@ class editor { if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr)) { - throw_type_error(302, "type must be a value, but is ", "discarded"); + throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded"); } encoded r; if (!is_container(*v.m_node)) @@ -4266,7 +4264,7 @@ class editor { if (NLOHMANN_VIEW_UNLIKELY(len >= 0xFFFFFFFFu)) { - throw_out_of_range(416, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE + throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE } node n = plain_node(value_t::string); n.flags = static_cast(n.flags | node_flags::edited); @@ -4341,12 +4339,12 @@ class editor return string_node(s.data(), s.size()); } case value_t::binary: - throw_type_error(319, "cannot store a binary value in a json_document", ""); + throw_type_error(exception_id::document_binary_value, "cannot store a binary value in a json_document", ""); case value_t::discarded: case value_t::object: case value_t::array: default: - throw_type_error(302, "type must be a value, but is ", "discarded"); + throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded"); } } @@ -4974,7 +4972,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v inline std::vector save_image(const document_data& d) { #if !NLOHMANN_VIEW_LITTLE_ENDIAN - throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE + throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE #endif const std::size_t arena_size = d.arena_size; std::vector compacted; @@ -4991,7 +4989,7 @@ inline std::vector save_image(const document_data& d) if (NLOHMANN_VIEW_UNLIKELY(text_size >= image_limit || total_arena >= image_limit || count >= image_limit)) { // LCOV_EXCL_START (4 GiB) - throw_out_of_range(416, "images of 4 GiB or more are not supported by json_document"); + throw_out_of_range(exception_id::document_too_large, "images of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_STOP } image_header h{}; @@ -5448,7 +5446,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha [[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_image(const char* what) { - throw_parse_error(116, concat("invalid json_document image: ", what)); + throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what)); } /// Read an image into d. The text and the decoded strings stay in the image; @@ -5456,7 +5454,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check) { #if !NLOHMANN_VIEW_LITTLE_ENDIAN - throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE + throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE #endif if (image == nullptr || size < sizeof(image_header)) { @@ -5844,7 +5842,7 @@ class view_iterator { if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0)) { - throw_invalid_iterator(207, "cannot use key() for non-object iterators"); + throw_invalid_iterator(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators"); } return string_view_t(m_doc->str(*m_pos), m_pos->len); } @@ -6152,6 +6150,8 @@ BasicJsonType materialize(const document_data& d, const node* n) case value_t::binary: case value_t::discarded: default: + // false positive: the parser's frame stack points into its own storage (never null) + // @infer-ignore NULLPTR_DEREFERENCE sax.null(); break; } @@ -6236,51 +6236,15 @@ enum class pointer_mode contains, ///< as basic_json::contains(): no exceptions at all }; -/// the outcome of reading an array index from a reference token -enum class index_status -{ - ok, - leading_zero, ///< parse_error.106 - not_number, ///< parse_error.109 - unresolved, ///< out_of_range.404 - too_large, ///< out_of_range.410 -}; +/// the outcome of reading an array index: detail::parse_array_index(), shared with json_pointer +using index_status = detail::array_index_status; /// reads an array index like json_pointer::array_index (RFC 6901, Sect. 4), /// but reports errors instead of throwing them template index_status array_index(const StringType& s, std::size_t& idx) noexcept { - // as basic_json: the empty token and "-" (past the last element) are - // unresolved rather than not a number - if (s.empty() || (s.size() == 1 && s[0] == '-')) - { - return index_status::unresolved; - } - if (s[0] < '0' || s[0] > '9') - { - return index_status::not_number; - } - if (s.size() > 1 && s[0] == '0') - { - return index_status::leading_zero; - } - std::uint64_t v = 0; - for (const auto c : s) - { - const auto d = static_cast(static_cast(c)) - '0'; - if (d > 9 || v > ((std::numeric_limits::max)() - d) / 10) - { - return index_status::unresolved; // not a number, or beyond unsigned long long - } - v = (v * 10) + d; - } - if (v >= (std::numeric_limits::max)()) // (std::size_t converts to std::uint64_t implicitly) - { - return index_status::too_large; - } - idx = static_cast(v); - return index_status::ok; + return detail::parse_array_index(s, idx); } /// throws the exception json_pointer::array_index throws for this status @@ -6290,15 +6254,15 @@ template switch (status) { case index_status::leading_zero: - throw_parse_error(106, concat("array index '", s, "' must not begin with '0'")); - case index_status::not_number: - throw_parse_error(109, concat("array index '", s, "' is not a number")); - case index_status::too_large: - throw_out_of_range(410, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE + throw_parse_error(exception_id::pointer_index_leading_zero, concat("array index '", s, "' must not begin with '0'")); + case index_status::not_a_number: + throw_parse_error(exception_id::pointer_index_not_a_number, concat("array index '", s, "' is not a number")); + case index_status::exceeds_size_type: + throw_out_of_range(exception_id::value_out_of_range, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE case index_status::unresolved: case index_status::ok: default: - throw_out_of_range(404, concat("unresolved reference token '", s, "'")); + throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", s, "'")); } } @@ -6323,7 +6287,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (mode == pointer_mode::checked) { - throw_out_of_range(403, concat("key '", token, "' not found")); + throw_key_not_found(token); } return View(); } @@ -6335,7 +6299,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (throwing) { - throw_out_of_range(402, concat("array index '-' (", std::to_string(cur.size()), ") is out of range")); + throw_array_index_past_end(cur.size()); } return View(); } @@ -6343,7 +6307,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) const index_status status = array_index(token, idx); if (status != index_status::ok) { - const bool parse_error = status == index_status::leading_zero || status == index_status::not_number; + const bool parse_error = status == index_status::leading_zero || status == index_status::not_a_number; if (throwing || (mode == pointer_mode::value && parse_error)) { throw_array_index_error(status, token); @@ -6354,7 +6318,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (mode == pointer_mode::checked) { - throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range")); + throw_array_index_out_of_range(idx); } return View(); } @@ -6364,7 +6328,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode) { if (throwing) { - throw_out_of_range(404, concat("unresolved reference token '", token, "'")); + throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", token, "'")); } return View(); } @@ -7457,21 +7421,17 @@ class view_serializer m_out.put("null"); return; } - static constexpr bool is_ieee_single_or_double - = std::numeric_limits::is_iec559 - && ((std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) - || (std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024)); - write_float(x, std::integral_constant()); + write_float(x, std::integral_constant::value>()); } - void write_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + void write_float(number_float_t x, std::true_type /*has_native_float_format*/) { std::array buf{}; const char* const end = ::nlohmann::detail::to_chars(buf.data(), buf.data() + buf.size(), x); m_out.put(buf.data(), static_cast(end - buf.data())); } - void write_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + void write_float(number_float_t x, std::false_type /*has_native_float_format*/) { // other types (e.g. long double) are rare: the library writes them const string_t s = BasicJsonType(x).dump(); @@ -7532,8 +7492,8 @@ class view_serializer void write_u_escape(std::uint32_t u) { - static constexpr const char* hex = "0123456789abcdef"; - const std::array e = {{'\\', 'u', hex[(u >> 12u) & 0xFu], hex[(u >> 8u) & 0xFu], hex[(u >> 4u) & 0xFu], hex[u & 0xFu]}}; + std::array e = {{'\\', 'u', 0, 0, 0, 0}}; + hex4_lower(static_cast(u), &e[2]); m_out.put(e.data(), e.size()); } @@ -7730,7 +7690,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const nod case value_t::binary: case value_t::discarded: default: - throw_type_error(302, "type must be number, but is ", value_type_name(static_cast(n.kind))); + throw_type_error(exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast(n.kind))); } } @@ -7740,7 +7700,7 @@ std::vector vector_value(const View& v) { if (NLOHMANN_VIEW_UNLIKELY(!v.is_array())) { - throw_type_error(302, "type must be array, but is ", v.type_name()); + throw_type_error(exception_id::type_mismatch, "type must be array, but is ", v.type_name()); } std::vector r; r.reserve(v.size()); @@ -7758,7 +7718,7 @@ Map map_value(const View& v) { if (NLOHMANN_VIEW_UNLIKELY(!v.is_object())) { - throw_type_error(302, "type must be object, but is ", v.type_name()); + throw_type_error(exception_id::type_mismatch, "type must be object, but is ", v.type_name()); } Map r; for (auto it = v.begin(); it != v.end(); ++it) @@ -7997,7 +7957,7 @@ class basic_json_view { return {}; } - detail::view::throw_type_error(305, "cannot use operator[] with a string argument with ", type_name()); + detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", type_name()); } return lookup(key); } @@ -8023,7 +7983,7 @@ class basic_json_view { return {}; } - detail::view::throw_type_error(305, "cannot use operator[] with a numeric argument with ", type_name()); + detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", type_name()); } return idx < m_node->len ? element(idx) : basic_json_view(); } @@ -8056,12 +8016,12 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_object())) { - detail::view::throw_type_error(304, "cannot use at() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name()); } const basic_json_view r = lookup(key); if (NLOHMANN_VIEW_UNLIKELY(r.is_discarded())) { - detail::view::throw_out_of_range(403, detail::concat("key '", std::string(key.data(), key.size()), "' not found")); + detail::view::throw_key_not_found(std::string(key.data(), key.size())); } return r; } @@ -8082,11 +8042,11 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_array())) { - detail::view::throw_type_error(304, "cannot use at() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name()); } if (NLOHMANN_VIEW_UNLIKELY(idx >= m_node->len)) { - detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range")); + detail::view::throw_array_index_out_of_range(idx); } return element(idx); } @@ -8114,7 +8074,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_object())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } const basic_json_view r = lookup(key); return r.is_discarded() ? default_value : r.template get(); @@ -8124,7 +8084,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_object())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } // (the default is made only if there is no such member) const basic_json_view r = lookup(key); @@ -8139,7 +8099,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_structured())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value); return r.is_discarded() ? default_value : r.template get(); @@ -8149,7 +8109,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_structured())) { - detail::view::throw_type_error(306, "cannot use value() with ", type_name()); + detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name()); } const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value); return r.is_discarded() ? string_t(default_value) : r.template get(); @@ -8162,7 +8122,7 @@ class basic_json_view const iterator it = begin(); if (NLOHMANN_VIEW_UNLIKELY(it == end())) { - detail::view::throw_invalid_iterator(214, "cannot get value"); + detail::view::throw_invalid_iterator(detail::exception_id::iterator_value_unavailable, "cannot get value"); } return *it; } @@ -8315,7 +8275,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_string())) { - detail::view::throw_type_error(302, "type must be string, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be string, but is ", type_name()); } return {m_doc->str(*m_node), m_node->len}; } @@ -8327,7 +8287,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_number())) { - detail::view::throw_type_error(302, "type must be number, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name()); } return {m_doc->str(*m_node), detail::view::number_length(*m_node)}; } @@ -8584,7 +8544,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_boolean())) { - detail::view::throw_type_error(302, "type must be boolean, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be boolean, but is ", type_name()); } return (m_node->flags & detail::view::node_flags::is_true) != 0; } @@ -8594,7 +8554,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(m_node == nullptr)) { - detail::view::throw_type_error(302, "type must be number, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name()); } return detail::view::arithmetic_value(*m_doc, *m_node); } @@ -8603,7 +8563,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(!is_null())) { - detail::view::throw_type_error(302, "type must be null, but is ", type_name()); + detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be null, but is ", type_name()); } return nullptr; } @@ -8912,7 +8872,7 @@ class basic_json_document { if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded)) { - detail::view::throw_type_error(320, "cannot save a discarded json_document"); + detail::view::throw_type_error(detail::exception_id::document_image_unsupported, "cannot save a discarded json_document"); } return detail::view::save_image(*m_data); } @@ -9013,7 +8973,7 @@ class basic_json_document const std::size_t idx = pointer_index(token); if (idx >= 0xFFFFFFFFu) { - detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range")); + detail::view::throw_array_index_out_of_range(idx); } for (std::size_t i = 0; i < idx; ++i) { @@ -9069,7 +9029,7 @@ class basic_json_document { if (ptr.empty()) { - detail::view::throw_out_of_range(405, "JSON pointer has no parent"); + detail::view::throw_out_of_range(detail::exception_id::patch_on_root, "JSON pointer has no parent"); } const view_type parent = root().at(ptr.parent_pointer()); const auto& token = ptr.back(); @@ -9089,7 +9049,7 @@ class basic_json_document static_assert(Editable, "only an editable document can be edited: use basic_json_document (json_editable_document)"); if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded)) { - detail::view::throw_invalid_iterator(202, "view does not belong to this document"); + detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document"); } return detail::view::editor(*m_data); } @@ -9106,7 +9066,7 @@ class basic_json_document { if (idx < 0) { - detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range")); + detail::view::throw_array_index_out_of_range(idx); } return static_cast(idx); } diff --git a/tests/benchmarks/src/benchmarks.cpp b/tests/benchmarks/src/benchmarks.cpp index 6e51d4778..0d9e06d21 100644 --- a/tests/benchmarks/src/benchmarks.cpp +++ b/tests/benchmarks/src/benchmarks.cpp @@ -21,7 +21,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; ////////////////////////////////////////////////////////////////////////////// // parse JSON from file diff --git a/tests/benchmarks/src/benchmarks_view.cpp b/tests/benchmarks/src/benchmarks_view.cpp index 475ed54a4..02920ffab 100644 --- a/tests/benchmarks/src/benchmarks_view.cpp +++ b/tests/benchmarks/src/benchmarks_view.cpp @@ -17,8 +17,8 @@ #include #include -using json = nlohmann::json; -using json_document = nlohmann::json_document; +using nlohmann::json; +using nlohmann::json_document; static std::string read_file(const char* filename) { diff --git a/tests/src/fuzzer-json_view_image.cpp b/tests/src/fuzzer-json_view_image.cpp index 8495468a4..d990600af 100644 --- a/tests/src/fuzzer-json_view_image.cpp +++ b/tests/src/fuzzer-json_view_image.cpp @@ -33,8 +33,8 @@ drivers. #error "the fuzzer drivers must be built without NDEBUG" #endif -using json = nlohmann::json; -using json_document = nlohmann::json_document; +using nlohmann::json; +using nlohmann::json_document; using image_check = json_document::image_check; // see http://llvm.org/docs/LibFuzzer.html diff --git a/tests/src/fuzzer-parse_json_view.cpp b/tests/src/fuzzer-parse_json_view.cpp index 9b9f5a02a..5e41d6c79 100644 --- a/tests/src/fuzzer-parse_json_view.cpp +++ b/tests/src/fuzzer-parse_json_view.cpp @@ -42,8 +42,8 @@ drivers. #error "the fuzzer drivers must be built without NDEBUG" #endif -using json = nlohmann::json; -using json_document = nlohmann::json_document; +using nlohmann::json; +using nlohmann::json_document; namespace { diff --git a/tests/src/unit-bjdata.cpp b/tests/src/unit-bjdata.cpp index 2f91462da..67e9e0a05 100644 --- a/tests/src/unit-bjdata.cpp +++ b/tests/src/unit-bjdata.cpp @@ -19,7 +19,7 @@ #include using nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::ordered_json; #include #include diff --git a/tests/src/unit-class_lexer.cpp b/tests/src/unit-class_lexer.cpp index fac88687f..2926ef88b 100644 --- a/tests/src/unit-class_lexer.cpp +++ b/tests/src/unit-class_lexer.cpp @@ -2042,3 +2042,118 @@ TEST_CASE("string scanning kernels") CHECK(nlohmann::detail::read_eight_bytes(bytes.data() + 8) == 0xFF0F0E0D0C0B0A09u); CHECK(nlohmann::detail::read_eight_bytes(reinterpret_cast(bytes.data()) + 3) == 0x0B0A090807060504u); } + +TEST_CASE("find_ascii_copyable_run agrees with is_ascii_copyable for every byte") +{ + // the SWAR mask in find_ascii_copyable_run() encodes the predicate a + // second time; check every byte value at every position of a 24-byte buffer + // (three words) and for several lengths, with the byte at the end or inside + for (unsigned int value = 0; value < 256; ++value) + { + for (std::size_t position = 0; position < 24; ++position) + { + std::array buffer{}; + buffer.fill(static_cast('a')); + buffer[position] = static_cast(value); + const bool copyable = nlohmann::detail::is_ascii_copyable(static_cast(value)); + for (const std::size_t n : + { + position, position + 1, std::size_t{24} + }) + { + if (n > 24) + { + continue; + } + const std::size_t expected = (copyable || n <= position) ? n : position; + CAPTURE(value) + CAPTURE(position) + CAPTURE(n) + CHECK(nlohmann::detail::find_ascii_copyable_run(buffer.data(), n) == expected); + } + } + } +} + +TEST_CASE("UTF-8 validators accept the same sequences") +{ + // The library checks UTF-8 in several places: detail::decode (serializer), + // the per-lead-byte switch of the lexer (json::accept), validate_one_utf8 + // (bulk string scan), utf8_shape, and the byte path of the BON8 reader. + // They must accept exactly the same sequences (RFC 3629, section 4). + using nlohmann::json; + + // bytes for the third and fourth position: the first two pairs are checked + // against all validators, the others only against the cheap ones + const std::array, 6> tails = + { + { + {{0x80, 0x80}}, {{0xBF, 0xBF}}, {{0x7F, 0x80}}, {{0xC0, 0x80}}, {{0x80, 0x7F}}, {{0x80, 0xC0}} + } + }; + + for (unsigned lead = 0x80; lead <= 0xFF; ++lead) + { + const auto shape = nlohmann::detail::utf8_shape(static_cast(lead)); + for (unsigned second = 0; second <= 0xFF; ++second) + { + for (std::size_t t = 0; t < tails.size(); ++t) + { + const std::array bytes = {{static_cast(lead), static_cast(second), tails[t][0], tails[t][1]}}; + + // expectation from the shape: the sequence is bytes[0..length) + const std::size_t length = shape.length; + bool expected = length >= 2 && second >= shape.second_lo && second <= shape.second_hi; + for (std::size_t i = 2; expected && i < length; ++i) + { + expected = bytes[i] >= 0x80 && bytes[i] <= 0xBF; + } + + // the DFA of the serializer + bool dfa = length >= 2; + if (dfa) + { + std::uint8_t state = nlohmann::detail::UTF8_ACCEPT; + std::uint32_t codep = 0; + for (std::size_t i = 0; i < length; ++i) + { + nlohmann::detail::decode(state, codep, bytes[i]); + } + dfa = state == nlohmann::detail::UTF8_ACCEPT; + } + + // the bulk path: exactly the sequence, and one byte short + const std::size_t bulk = nlohmann::detail::validate_one_utf8(bytes.data(), length); + const std::size_t bulk_short = length > 0 ? nlohmann::detail::validate_one_utf8(bytes.data(), length - 1) : 0; + + CAPTURE(lead) + CAPTURE(second) + CAPTURE(t) + CHECK((dfa == expected && bulk == (expected ? length : 0) && bulk_short == 0)); + + if (t < 2 && length >= 2) + { + // the lexer's byte path + const std::string sequence(reinterpret_cast(bytes.data()), length); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + CHECK(json::accept("\"" + sequence + "\"") == expected); + + // the byte path of the BON8 reader (a string ends at 0xFF) + if (lead >= 0xC2 && lead <= 0xF7) + { + std::vector packed(bytes.begin(), bytes.begin() + static_cast(length)); + packed.push_back(0xFF); + const json j = json::from_bon8(packed, true, false); + if (expected) + { + CHECK(j == json(sequence)); + } + else + { + CHECK_FALSE((j.is_string() && j.get() == sequence)); + } + } + } + } + } + } +} diff --git a/tests/src/unit-constructor1.cpp b/tests/src/unit-constructor1.cpp index 600eb0069..96a64e721 100644 --- a/tests/src/unit-constructor1.cpp +++ b/tests/src/unit-constructor1.cpp @@ -361,6 +361,40 @@ TEST_CASE("constructors") CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&); CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&); CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&); +#if JSON_DIAGNOSTICS + // the exception names the element that has the wrong type + CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.type_error.302] (/0) type must be string, but is number", json::type_error&); +#else + CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.type_error.302] type must be string, but is number", json::type_error&); +#endif + + // the elements are converted in order, so a type error comes before a missing element + json const j2{1, "two"}; + std::array a{{0, 0, 0}}; +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS((j2.get>()), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error&); + CHECK_THROWS_WITH_AS(j2.get_to(a), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error&); +#else + CHECK_THROWS_WITH_AS((j2.get>()), "[json.exception.type_error.302] type must be number, but is string", json::type_error&); + CHECK_THROWS_WITH_AS(j2.get_to(a), "[json.exception.type_error.302] type must be number, but is string", json::type_error&); +#endif + CHECK_THROWS_WITH_AS(j.get_to(a), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&); +#if !defined(JSON_NOEXCEPTION) // without exceptions, the get_to calls in CHECK_THROWS_WITH_AS do not run + CHECK(a[0] == 1); +#endif + + json const j3{"one", "two"}; + CHECK_THROWS_WITH_AS((j3.get>()), "[json.exception.out_of_range.401] array index 2 is out of range", json::out_of_range&); + } + + SECTION("std::array from a longer array or from booleans") + { + json const j{1, 2.5, true, 4u}; + CHECK(j.get>() == (std::array {{1, 2, 1}})); + std::array a{{0, 0, 0, 0}}; + j.get_to(a); + CHECK(a == (std::array {{1, 2, 1, 4}})); + CHECK(j.get>().empty()); } SECTION("std::forward_list") diff --git a/tests/src/unit-diagnostic-positions.cpp b/tests/src/unit-diagnostic-positions.cpp index f5c6bc648..9e0625115 100644 --- a/tests/src/unit-diagnostic-positions.cpp +++ b/tests/src/unit-diagnostic-positions.cpp @@ -14,7 +14,7 @@ #define JSON_DIAGNOSTIC_POSITIONS 1 #include -using json = nlohmann::json; +using nlohmann::json; TEST_CASE("Better diagnostics with positions") { diff --git a/tests/src/unit-disabled_exceptions.cpp b/tests/src/unit-disabled_exceptions.cpp index 40a37c339..0e36d622e 100644 --- a/tests/src/unit-disabled_exceptions.cpp +++ b/tests/src/unit-disabled_exceptions.cpp @@ -13,7 +13,7 @@ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") #include -using json = nlohmann::json; +using nlohmann::json; #ifdef JSON_TEST_NO_GLOBAL_UDLS using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif diff --git a/tests/src/unit-element_access2.cpp b/tests/src/unit-element_access2.cpp index 7765c0601..9f4d67fe8 100644 --- a/tests/src/unit-element_access2.cpp +++ b/tests/src/unit-element_access2.cpp @@ -1948,7 +1948,7 @@ TEST_CASE_TEMPLATE("element access 2 (additional value() tests)", Json, nlohmann #ifdef JSON_HAS_CPP_17 TEST_CASE("operator[] with user-defined std::string_view-convertible types") { - using json = nlohmann::json; + using nlohmann::json; class TestClass { @@ -2031,7 +2031,7 @@ TEST_CASE("keys convertible to std::string_view work with all lookup functions ( SECTION("nlohmann::json") { - using json = nlohmann::json; + using nlohmann::json; SECTION("ViewKey") { @@ -2072,7 +2072,7 @@ TEST_CASE("keys convertible to std::string_view work with all lookup functions ( SECTION("nlohmann::ordered_json") { - using ordered_json = nlohmann::ordered_json; + using nlohmann::ordered_json; SECTION("ViewKey") { diff --git a/tests/src/unit-explicit_instantiation.cpp b/tests/src/unit-explicit_instantiation.cpp index 71132a59f..ddb5e40f9 100644 --- a/tests/src/unit-explicit_instantiation.cpp +++ b/tests/src/unit-explicit_instantiation.cpp @@ -15,7 +15,7 @@ #include "doctest_compatibility.h" #include -using json = nlohmann::json; +using nlohmann::json; ///////////////////////////////////////////////////////////////////// // for #4825 - explicitly instantiating basic_json must compile; this diff --git a/tests/src/unit-format-as.cpp b/tests/src/unit-format-as.cpp index b275f505c..61821f9ac 100644 --- a/tests/src/unit-format-as.cpp +++ b/tests/src/unit-format-as.cpp @@ -9,8 +9,8 @@ #include "doctest_compatibility.h" #include -using json = nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::json; +using nlohmann::ordered_json; namespace { diff --git a/tests/src/unit-hash.cpp b/tests/src/unit-hash.cpp index 117bc0151..e544cff9b 100644 --- a/tests/src/unit-hash.cpp +++ b/tests/src/unit-hash.cpp @@ -9,8 +9,8 @@ #include "doctest_compatibility.h" #include -using json = nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::json; +using nlohmann::ordered_json; #include #include diff --git a/tests/src/unit-no_automatic_udls.cpp b/tests/src/unit-no_automatic_udls.cpp index 1a708da50..02f900077 100644 --- a/tests/src/unit-no_automatic_udls.cpp +++ b/tests/src/unit-no_automatic_udls.cpp @@ -19,7 +19,7 @@ #include #include -using json = nlohmann::json; +using nlohmann::json; // An argument type whose associated namespace is the library namespace, so // argument-dependent lookup of a literal operator called by its function name diff --git a/tests/src/unit-no_io_and_user_exceptions.cpp b/tests/src/unit-no_io_and_user_exceptions.cpp index 667d114e7..5a2b7da44 100644 --- a/tests/src/unit-no_io_and_user_exceptions.cpp +++ b/tests/src/unit-no_io_and_user_exceptions.cpp @@ -38,7 +38,7 @@ static int json_throw_user_call_count = 0; // NOLINT(cppcoreguidelines-avoid-non #include "doctest_compatibility.h" #include -using json = nlohmann::json; +using nlohmann::json; TEST_CASE("JSON_NO_IO") { diff --git a/tests/src/unit-regression2.cpp b/tests/src/unit-regression2.cpp index b532e75ae..c7566c799 100644 --- a/tests/src/unit-regression2.cpp +++ b/tests/src/unit-regression2.cpp @@ -33,8 +33,8 @@ #define JSON_TESTS_PRIVATE #include -using json = nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::json; +using nlohmann::ordered_json; #ifdef JSON_TEST_NO_GLOBAL_UDLS using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif diff --git a/tests/src/unit-regression3.cpp b/tests/src/unit-regression3.cpp index b31cfd054..148ce2658 100644 --- a/tests/src/unit-regression3.cpp +++ b/tests/src/unit-regression3.cpp @@ -36,8 +36,8 @@ #define JSON_TESTS_PRIVATE #include -using json = nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::json; +using nlohmann::ordered_json; #ifdef JSON_TEST_NO_GLOBAL_UDLS using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif diff --git a/tests/src/unit-std-format.cpp b/tests/src/unit-std-format.cpp index f2be8d5ec..9f32868e4 100644 --- a/tests/src/unit-std-format.cpp +++ b/tests/src/unit-std-format.cpp @@ -16,8 +16,8 @@ #include "doctest_compatibility.h" #include -using json = nlohmann::json; -using ordered_json = nlohmann::ordered_json; +using nlohmann::json; +using nlohmann::ordered_json; // JSON_HAS_CPP_20 (do not remove; see note at top of file) #if JSON_HAS_STD_FORMAT