Compare commits

..
Author SHA1 Message Date
Niels Lohmann f57dbdc1e8 Document what counts as a breaking change in the API stability rules
Spell out the 3.x compatibility rules in the roadmap: new defaulted
parameters, new default arguments, noexcept/constexpr, template
parameters, parse and dump results, accepted input, key iteration order,
iterator invalidation, implicit conversions, to_json/from_json lookup,
json_sax, value_t enumerators, and documented macros, CMake options and
headers. Also list std::hash values as not part of the public API, and
link the macro overview from the section.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-08 08:45:59 +02:00
Niels Lohmann e7fe98180e Address review comments on #5775 and #5779
Allow new defaulted parameters and new default arguments in the API
stability rules, mention the macro opt-in, and drop the redundant
recompile advice. Describe test-diagnostics-optimized as the regression
test for the fixed #5742.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-07 19:31:59 +02:00
62 changed files with 1573 additions and 1983 deletions

No files matched your search

-45
View File
@@ -31,51 +31,6 @@ jobs:
- name: Build - name: Build
run: cmake --build build --target ci_test_gcc run: cmake --build build --target ci_test_gcc
ci_meson_install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Get latest CMake and ninja
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
- name: Check that Meson and pkg-config offer the CMake options
run: make check_build_options
- name: Install Meson
run: pip install meson
- name: Install with Meson
run: |
meson setup build-meson --prefix=${{ github.workspace }}/install
meson install -C build-meson
- name: Use the installed package with find_package
run: |
cmake -S tests/cmake_import/project -B build-import -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install
cmake --build build-import
- name: Install with Meson and non-default options
run: |
meson setup build-meson-options --prefix=${{ github.workspace }}/install-options -DMultipleHeaders=true -DDiagnostics=true -DGlobalUDLs=false -DDisableTupleReferenceConversion=true -DStrictBinaryUTF8=true -DDeleteDeprecatedFunctions=true
meson install -C build-meson-options
- name: Check that the options reach the installed files
run: |
test -d install-options/include/nlohmann/detail
cflags=$(PKG_CONFIG_PATH=${{ github.workspace }}/install-options/share/pkgconfig pkg-config --cflags nlohmann_json)
echo "$cflags"
echo "$cflags" | grep -q -- '-DJSON_DIAGNOSTICS=1'
echo "$cflags" | grep -q -- '-DJSON_USE_GLOBAL_UDLS=0'
echo "$cflags" | grep -q -- '-DJSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1'
echo "$cflags" | grep -q -- '-DJSON_STRICT_BINARY_UTF8=1'
echo "$cflags" | grep -q -- '-DJSON_DELETE_DEPRECATED_FUNCTIONS=1'
grep -q 'JSON_USE_GLOBAL_UDLS=0;JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1;JSON_DIAGNOSTICS=1' install-options/share/cmake/nlohmann_json/nlohmann_jsonTargets.cmake
grep -q 'JSON_STRICT_BINARY_UTF8=1;JSON_DELETE_DEPRECATED_FUNCTIONS=1' install-options/share/cmake/nlohmann_json/nlohmann_jsonTargets.cmake
cmake -S tests/cmake_import/project -B build-import-options -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-options
cmake --build build-import-options
- name: Install with Meson and the include directory outside the prefix
run: |
meson setup build-meson-split --prefix=${{ github.workspace }}/install-split --includedir=${{ github.workspace }}/install-split-dev/include
meson install -C build-meson-split
cmake -S tests/cmake_import/project -B build-import-split -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-split
cmake --build build-import-split
ci_infer: ci_infer:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
+1 -33
View File
@@ -189,39 +189,7 @@ if (MSVC)
) )
endif() endif()
# Install a pkg-config file, so other tools can find this. It carries the same # Install a pkg-config file, so other tools can find this.
# compile definitions as the target above.
set(NLOHMANN_JSON_PKGCONFIG_CFLAGS "")
if (NOT JSON_GlobalUDLs)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_GLOBAL_UDLS=0")
endif()
if (NOT JSON_ImplicitConversions)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_IMPLICIT_CONVERSIONS=0")
endif()
if (JSON_DisableEnumSerialization)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DISABLE_ENUM_SERIALIZATION=1")
endif()
if (JSON_DisableTupleReferenceConversion)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1")
endif()
if (JSON_Diagnostics)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DIAGNOSTICS=1")
endif()
if (JSON_Diagnostic_Positions)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DIAGNOSTIC_POSITIONS=1")
endif()
if (JSON_LegacyDiscardedValueComparison)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1")
endif()
if (JSON_StrictNulHandling)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_STRICT_NUL_HANDLING=1")
endif()
if (JSON_StrictBinaryUTF8)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_STRICT_BINARY_UTF8=1")
endif()
if (JSON_DeleteDeprecatedFunctions)
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DELETE_DEPRECATED_FUNCTIONS=1")
endif()
configure_file( configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
+2 -24
View File
@@ -249,31 +249,9 @@ make BUILD.bazel
The "Check amalgamation" workflow fails if the file is out of date. The "Check amalgamation" workflow fails if the file is out of date.
### `meson.build` and `meson_options.txt` ### `meson.build`
Meson build definitions suitable for use as a subproject ("wrap" in Meson terminology). The build definition for the [Meson](https://mesonbuild.com) build system.
Projects wishing to use the wrap can execute:
```sh
meson wrap install nlohmann_json
```
Which allows Meson to build from source when a system provided dependency isn't available.
To build directly:
```sh
meson setup builddir
ninja -C builddir
```
`meson_options.txt` defines the options, which mirror the CMake options that change the library's target (for example,
`-DDiagnostics=true`). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options`
([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files
stay in sync with the CMake options.
When installing, `meson.build` installs the headers, a pkg-config file, and the CMake package config files, so that
`find_package(nlohmann_json)` works. As Meson cannot generate `nlohmann_jsonTargets.cmake` itself, it is created from
the template `cmake/nlohmann_jsonTargets.cmake.in`, which is only used by Meson.
### `Package.swift` ### `Package.swift`
+2 -6
View File
@@ -1,4 +1,4 @@
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check check_build_options .PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check
########################################################################## ##########################################################################
# configuration # configuration
@@ -127,10 +127,6 @@ macro_builder_check:
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \ diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1) diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
# check that the Meson build and the pkg-config files offer the options of the CMake target
check_build_options:
python3 tools/check_build_options/check_build_options.py .
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources # check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
check-amalgamation: check-amalgamation:
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ @mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
@@ -188,7 +184,7 @@ json.tar.xz:
# We use `-X` to make the resulting ZIP file reproducible, see # We use `-X` to make the resulting ZIP file reproducible, see
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>. # <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
include.zip: BUILD.bazel include.zip: BUILD.bazel
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build meson_options.txt LICENSE.MIT zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
# Create the files for a release and add signatures and hashes. # Create the files for a release and add signatures and hashes.
release: include.zip json.tar.xz release: include.zip json.tar.xz
-42
View File
@@ -1,42 +0,0 @@
# Imported target for installations made with Meson (see meson.build).
#
# CMake installations generate this file with install(EXPORT ...). Meson cannot
# do that, but as the library is header-only, the target only needs an include
# directory, the C++ standard, and the compile definitions of the options that
# differ from their defaults. Paths are computed relative to this file so that
# the installation can be relocated (e.g., into a sysroot), unless includedir or
# datadir is outside the prefix.
if(TARGET @PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@)
return()
endif()
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_DIR}/@NLOHMANN_JSON_CONFIG_TO_PREFIX@" ABSOLUTE)
# As in CMake's generated file: avoid "//include" for an installation to "/".
if(_IMPORT_PREFIX STREQUAL "/")
set(_IMPORT_PREFIX "")
endif()
add_library(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ INTERFACE IMPORTED)
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "@NLOHMANN_JSON_INCLUDE_DIR@"
)
if(CMAKE_VERSION VERSION_LESS 3.8)
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
INTERFACE_COMPILE_FEATURES cxx_range_for
)
else()
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
INTERFACE_COMPILE_FEATURES cxx_std_11
)
endif()
set(_NLOHMANN_JSON_COMPILE_DEFINITIONS "@NLOHMANN_JSON_COMPILE_DEFINITIONS@")
if(_NLOHMANN_JSON_COMPILE_DEFINITIONS)
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "${_NLOHMANN_JSON_COMPILE_DEFINITIONS}"
)
endif()
unset(_NLOHMANN_JSON_COMPILE_DEFINITIONS)
unset(_IMPORT_PREFIX)
+1 -1
View File
@@ -4,4 +4,4 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: @PROJECT_NAME@ Name: @PROJECT_NAME@
Description: JSON for Modern C++ Description: JSON for Modern C++
Version: @PROJECT_VERSION@ Version: @PROJECT_VERSION@
Cflags: -I${includedir}@NLOHMANN_JSON_PKGCONFIG_CFLAGS@ Cflags: -I${includedir}
@@ -67,7 +67,7 @@ By default, implicit conversions are enabled.
`JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`: `JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`:
```cpp ```cpp
using wjson = nlohmann::json::with_string_t<std::wstring>; using wjson = nlohmann::basic_json<std::map, std::vector, std::wstring>;
void load(const nlohmann::json& j); void load(const nlohmann::json& j);
+23 -9
View File
@@ -36,13 +36,26 @@ work items are tracked in the [GitHub milestones](https://github.com/nlohmann/js
## API stability ## API stability
Releases follow [semantic versioning](https://semver.org): a minor or patch release of version 3.x does not break code Releases follow [semantic versioning](https://semver.org): a minor or patch release of version 3.x does not break code
that uses the public API. In particular, a 3.x release does not: that uses the public API, unless that code opts in to a change with a macro as described [below](#version-40). In
particular, a 3.x release does not:
- change the signature of a function (its parameter types, return type, number of parameters, or the const-ness of a - make breaking changes to the signature of a function: the types or order of its existing parameters, its return type,
member function); its `noexcept` or `constexpr` specifier, or the const-ness of a member function. New parameters may be added if they
- remove or rename a function or class; have a default value;
- remove or rename a function or class, or change the template parameters of a public class template;
- change which exceptions a function throws, or the [exception ids](../home/exceptions.md); - change which exceptions a function throws, or the [exception ids](../home/exceptions.md);
- change access specifiers or default arguments. - change access specifiers, or change or remove existing default arguments. New default arguments may be added;
- change the JSON type that a valid input parses to, or the text that `dump()` produces for a valid value;
- accept input that was rejected before, or reject input that was accepted before;
- change the order in which the keys of an object are iterated. The default type sorts keys, and
[`ordered_json`](../api/ordered_json.md) keeps insertion order;
- change when iterators, pointers, or references are invalidated, or the state of a moved-from `basic_json`;
- add or remove implicit conversions from `basic_json`;
- change how `to_json` and `from_json` functions are found, or the behavior of
[`adl_serializer`](../api/adl_serializer/index.md);
- add pure virtual functions to the [`json_sax`](../api/json_sax/index.md) interface;
- remove, rename, renumber, or add enumerators of `value_t`;
- 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 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.md).
@@ -51,13 +64,14 @@ The following are **not** part of the public API and may change in any release,
- The text of exception messages returned by `what()`. Use the [exception id](../home/exceptions.md) to tell errors - The text of exception messages returned by `what()`. Use the [exception id](../home/exceptions.md) to tell errors
apart. apart.
- The ABI, including `sizeof(basic_json)` and the memory layout of its values. Recompile your code when you upgrade the - The ABI, including `sizeof(basic_json)` and the memory layout of its values. The
library. The [versioned inline namespace](../features/namespace.md) turns mixing versions into a link error. [versioned inline namespace](../features/namespace.md) turns mixing versions into a link error.
- The hash values returned by `std::hash` for `basic_json`. Numbers that compare equal still hash equally.
- Everything in namespace `nlohmann::detail`, and macros and type traits that are not documented in the - Everything in namespace `nlohmann::detail`, and macros and type traits that are not documented in the
[API reference](../api/basic_json/index.md). [API reference](../api/basic_json/index.md).
Changes that would break the public API are only added behind a macro whose default keeps the 3.x behavior, see Breaking changes are only added behind a macro whose default keeps the 3.x behavior. See [Version 4.0](#version-40) and
[Version 4.0](#version-40). the [macro overview](../features/macros.md).
## Version 4.0 ## Version 4.0
@@ -1,10 +1,11 @@
#include <iostream> #include <iostream>
#include <map>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "custom_array_type.hpp" #include "custom_array_type.hpp"
using custom_json = nlohmann::json::with_array_t<custom_array_type>; using custom_json = nlohmann::basic_json<std::map, custom_array_type>;
int main() int main()
{ {
@@ -1,10 +1,16 @@
#include <cstdint>
#include <iostream> #include <iostream>
#include <map>
#include <string>
#include <vector>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "custom_binary_type.hpp" #include "custom_binary_type.hpp"
using custom_json = nlohmann::json::with_binary_t<custom_binary_type>; using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, custom_binary_type>;
int main() int main()
{ {
@@ -1,11 +1,12 @@
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
#include <vector>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "custom_object_type.hpp" #include "custom_object_type.hpp"
using custom_json = nlohmann::json::with_object_t<custom_object_type>; using custom_json = nlohmann::basic_json<custom_object_type, std::vector>;
int main() int main()
{ {
@@ -1,10 +1,12 @@
#include <iostream> #include <iostream>
#include <map>
#include <vector>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "custom_string_type.hpp" #include "custom_string_type.hpp"
using custom_json = nlohmann::json::with_string_t<custom_string_type>; using custom_json = nlohmann::basic_json<std::map, std::vector, custom_string_type>;
int main() int main()
{ {
@@ -351,8 +351,9 @@ The number types can be changed with template parameters.
A `basic_json` type that uses `#!c long double` as floating-point type. A `basic_json` type that uses `#!c long double` as floating-point type.
```cpp hl_lines="1" ```cpp hl_lines="2"
using json_ld = nlohmann::json::with_float_t<long double>; using json_ld = nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, long double>;
``` ```
Note values should then be parsed with `json_ld::parse` rather than `json::parse` as the latter would parse Note values should then be parsed with `json_ld::parse` rather than `json::parse` as the latter would parse
@@ -8,10 +8,6 @@ these requirements so they do not have to be discovered by trial and error. Each
that are known to work for that parameter and the ones that do not, checked against Boost 1.83, Abseil 20250127.0, that are known to work for that parameter and the ones that do not, checked against Boost 1.83, Abseil 20250127.0,
Folly, EASTL 3.21, `ankerl::unordered_dense`, `phmap`, `gtl`, `robin_hood`, `tsl::ordered_map`, and Qt 6. Folly, EASTL 3.21, `ankerl::unordered_dense`, `phmap`, `gtl`, `robin_hood`, `tsl::ordered_map`, and Qt 6.
To change a single template parameter and keep the others, use the member alias templates
[`with_*_t`](../../api/basic_json/with_t.md); for instance, `nlohmann::json::with_float_t<long double>` is `json` with
`#!cpp long double` as [`number_float_t`](../../api/basic_json/number_float_t.md).
## How to read this page ## How to read this page
Requirements are split into two groups: Requirements are split into two groups:
@@ -147,7 +143,7 @@ struct unordered_map_object
using base_t::base_t; using base_t::base_t;
}; };
using unordered_json = nlohmann::json::with_object_t<unordered_map_object>; using unordered_json = nlohmann::basic_json<unordered_map_object>;
``` ```
Whether `#!cpp std::unordered_map` can be instantiated at all depends on the standard library: `object_t` is formed Whether `#!cpp std::unordered_map` can be instantiated at all depends on the standard library: `object_t` is formed
@@ -180,7 +176,7 @@ struct flat_hash_object
using base_t::base_t; using base_t::base_t;
}; };
using flat_hash_json = nlohmann::json::with_object_t<flat_hash_object>; using flat_hash_json = nlohmann::basic_json<flat_hash_object>;
``` ```
`absl::node_hash_map` keeps references to the mapped values valid across insertions; `absl::flat_hash_map` does not, `absl::node_hash_map` keeps references to the mapped values valid across insertions; `absl::flat_hash_map` does not,
@@ -116,7 +116,7 @@ function to use instead.
=== "Deprecated" === "Deprecated"
```cpp ```cpp
using my_json = nlohmann::json::with_string_t<my_string_type>; using my_json = nlohmann::basic_json<std::map, std::vector, my_string_type>;
nlohmann::json_pointer<my_json> ptr("/foo/bar/1"); nlohmann::json_pointer<my_json> ptr("/foo/bar/1");
``` ```
@@ -125,24 +125,10 @@ meson wrap install nlohmann_json
Please see the Meson project for any issues regarding the packaging. Please see the Meson project for any issues regarding the packaging.
The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in
which case a [pkg-config](pkg-config.md) file and the CMake package config files are installed. To use it, have your build system require which case a [pkg-config](pkg-config.md) file is installed. To use it, have your build system require the
the `nlohmann_json` pkg-config dependency, or use [`find_package(nlohmann_json)`](cmake.md#external) in CMake. In Meson, `nlohmann_json` pkg-config dependency. In Meson, it is preferred to use the
it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than
subproject fallback, rather than using the subproject directly. using the subproject directly.
The options that change the library's configuration are available in Meson as well, named like the
[CMake options](cmake.md#cmake-options) without the `JSON_` prefix: `MultipleHeaders`, `GlobalUDLs`,
`ImplicitConversions`, `DisableEnumSerialization`, `DisableTupleReferenceConversion`, `Diagnostics`,
`Diagnostic_Positions`, `LegacyDiscardedValueComparison`, `StrictNulHandling`, `StrictBinaryUTF8`, and
`DeleteDeprecatedFunctions`. They have the same defaults as in CMake, except that
`MultipleHeaders` is `false`. Set them with `-D` when setting up the build, or with the subproject name as prefix when
the library is used as a subproject:
```shell
meson setup build -Dnlohmann_json:Diagnostics=true
```
The resulting compile definitions are part of the Meson dependency, the pkg-config file, and the CMake target.
??? example "Example: Wrap" ??? example "Example: Wrap"
+2 -11
View File
@@ -9,15 +9,12 @@
#pragma once #pragma once
#include <cstdint> // uint64_t #include <cstdint> // uint64_t
#if !defined(__SIZEOF_INT128__) && defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
#include <intrin0.h> // __umulh, _umul128
#endif
#include <nlohmann/detail/abi_macros.hpp> #include <nlohmann/detail/abi_macros.hpp>
// Portable bit-level helpers for the number and string scanners. They use // Portable bit-level helpers for the number and string scanners. They use
// compiler builtins or platform-specific intrinsics where available and plain // compiler builtins where available and plain C++ otherwise, so they need no
// C++ otherwise, so they work regardless of byte order. // platform headers and work regardless of byte order.
NLOHMANN_JSON_NAMESPACE_BEGIN NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail namespace detail
@@ -55,12 +52,6 @@ inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexc
__extension__ using uint128 = unsigned __int128; __extension__ using uint128 = unsigned __int128;
const uint128 r = static_cast<uint128>(a) * b; const uint128 r = static_cast<uint128>(a) * b;
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)}; return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
#elif defined(_MSC_VER) && defined(_M_X64)
std::uint64_t high = 0;
const std::uint64_t low = _umul128(a, b, &high);
return {low, high};
#elif defined(_MSC_VER) && defined(_M_ARM64)
return {a * b, __umulh(a, b)};
#else #else
const std::uint64_t a_lo = a & 0xFFFFFFFFu; const std::uint64_t a_lo = a & 0xFFFFFFFFu;
const std::uint64_t a_hi = a >> 32u; const std::uint64_t a_hi = a >> 32u;
@@ -48,7 +48,7 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_null())) if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{ {
throw_type_must_be("null", j); JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
} }
n = nullptr; n = nullptr;
} }
@@ -102,7 +102,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
throw_type_must_be("number", j); JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
} }
} }
@@ -111,7 +111,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{ {
throw_type_must_be("boolean", j); JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
} }
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
} }
@@ -121,7 +121,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
throw_type_must_be("string", j); JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
} }
@@ -137,7 +137,7 @@ inline void from_json(const BasicJsonType& j, StringType& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
throw_type_must_be("string", j); JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
@@ -183,7 +183,7 @@ inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
l.clear(); l.clear();
std::transform(j.rbegin(), j.rend(), std::transform(j.rbegin(), j.rend(),
@@ -200,7 +200,7 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
l.resize(j.size()); l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l), std::transform(j.begin(), j.end(), std::begin(l),
@@ -305,7 +305,7 @@ void())
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
from_json_array_impl(j, arr, priority_tag<3> {}); from_json_array_impl(j, arr, priority_tag<3> {});
@@ -324,7 +324,7 @@ auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {}); return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {});
@@ -335,7 +335,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{ {
throw_type_must_be("binary", j); JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
} }
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@@ -356,7 +356,7 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
} }
else else
{ {
throw_type_must_be("binary or array", j); JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
} }
} }
@@ -377,7 +377,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
throw_type_must_be("object", j); JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
} }
ConstructibleObjectType ret; ConstructibleObjectType ret;
@@ -432,7 +432,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
throw_type_must_be("number", j); JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
} }
} }
@@ -504,7 +504,7 @@ auto from_json(const BasicJsonType& j, TupleRelated&& t)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {}); return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {});
@@ -517,14 +517,14 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
m.clear(); m.clear();
for (const auto& p : j) for (const auto& p : j)
{ {
if (JSON_HEDLEY_UNLIKELY(!p.is_array())) if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{ {
throw_type_must_be("array", p); JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
} }
m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>()); m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>());
} }
@@ -591,7 +591,7 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
throw_type_must_be("string", j); JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
} }
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
// Checking for C++20 standard or later can be insufficient in case the // Checking for C++20 standard or later can be insufficient in case the
@@ -414,13 +414,13 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
BasicJsonType key = p.first; BasicJsonType key = p.first;
if (JSON_HEDLEY_UNLIKELY(!key.is_string())) if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
{ {
throw_type_must_be("string", key); JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key));
} }
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>(); auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second)) if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
{ {
JSON_THROW(type_error::create(exception_id::enum_key_duplicate, concat("duplicate object key '", key_string, "'"), &key)); JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
} }
} }
external_constructor<value_t::object>::construct(j, std::move(obj)); external_constructor<value_t::object>::construct(j, std::move(obj));
-144
View File
@@ -45,84 +45,6 @@ namespace detail
// exceptions // // exceptions //
//////////////// ////////////////
/*!
@brief the ids of the exceptions thrown by the library
@note The values are part of the public API: they are the `id` member of the
exceptions and appear in their `what()` messages.
@sa https://json.nlohmann.me/home/exceptions/
*/
enum class exception_id : int
{
// parse_error
syntax_error = 101, ///< unexpected token or invalid literal while parsing JSON
patch_not_an_array = 104, ///< a JSON Patch document is not an array of objects
patch_invalid_operation = 105, ///< a JSON Patch operation is malformed
pointer_index_leading_zero = 106, ///< a JSON Pointer array index has a leading zero
pointer_missing_slash = 107, ///< a JSON Pointer does not start with '/'
pointer_invalid_escape = 108, ///< a JSON Pointer contains an escape other than ~0 and ~1
pointer_index_not_a_number = 109, ///< a JSON Pointer array index is not a number
unexpected_end_of_input = 110, ///< a binary input ends early, or has bytes left at its end
unexpected_byte = 112, ///< a binary input contains an unexpected byte or invalid length
invalid_string_or_size = 113, ///< a binary input contains an invalid string or size specification
bson_unsupported_type = 114, ///< a BSON record type is not supported
invalid_high_precision_number = 115, ///< a UBJSON/BJData high-precision number cannot be parsed
// invalid_iterator
iterators_incompatible = 201, ///< the iterators of a range belong to different values
iterator_from_other_value = 202, ///< an iterator does not belong to the value it is used with
iterator_range_from_other_value = 203, ///< an iterator range does not belong to the value it is used with
iterator_range_out_of_range = 204, ///< an iterator range of a primitive value is not [begin, end)
iterator_out_of_range = 205, ///< an iterator of a primitive value is not begin()
iterator_range_of_null = 206, ///< an iterator range belongs to a null value
iterator_key_not_object = 207, ///< key() is called on an iterator of a non-object
iterator_subscript_on_object = 208, ///< operator[] is called on an iterator of an object
iterator_arithmetic_on_object = 209, ///< an offset operator is used on an iterator of an object
insert_range_incompatible = 210, ///< the iterators of an inserted range belong to different values
insert_range_into_itself = 211, ///< an inserted range belongs to the value it is inserted into
iterators_compare_different_values = 212, ///< iterators of different values are compared
iterator_order_on_object = 213, ///< iterators of an object are compared by order
iterator_value_unavailable = 214, ///< an iterator does not refer to a value
// type_error
object_from_non_pairs = 301, ///< an object is created from an initializer list that is not a list of pairs
type_mismatch = 302, ///< a value has the wrong type for a conversion
incompatible_reference_type = 303, ///< get_ref() is called with a reference type that does not match the value
at_wrong_type = 304, ///< at() is called on a value of the wrong type
subscript_wrong_type = 305, ///< operator[] is called on a value of the wrong type
value_wrong_type = 306, ///< value() is called on a value of the wrong type
erase_wrong_type = 307, ///< erase() is called on a value of the wrong type
push_back_wrong_type = 308, ///< push_back() or operator+= is called on a value of the wrong type
insert_wrong_type = 309, ///< insert() is called on a value of the wrong type
swap_wrong_type = 310, ///< swap() is called on a value of the wrong type
emplace_wrong_type = 311, ///< emplace() or emplace_back() is called on a value of the wrong type
update_wrong_type = 312, ///< update() is called on a value of the wrong type
unflatten_invalid_value = 313, ///< unflatten() finds conflicting paths
unflatten_not_object = 314, ///< unflatten() is called on a non-object
unflatten_value_not_primitive = 315, ///< unflatten() is called on an object with non-primitive values
invalid_utf8 = 316, ///< dump() finds a string that is not valid UTF-8
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
discarded_value_used = 321, ///< a discarded value is used
// out_of_range
array_index_out_of_range = 401, ///< an array index is out of range
pointer_past_the_end_index = 402, ///< a JSON Pointer uses the array index '-'
key_not_found = 403, ///< an object key is not found
pointer_unresolved = 404, ///< a JSON Pointer reference token cannot be resolved
patch_on_root = 405, ///< the root of a value has no parent, e.g. for a JSON Patch 'remove' or 'add' at the root
number_overflow = 406, ///< a number cannot be stored without overflowing to NaN or INF
integer_too_large = 407, ///< an integer cannot be represented in the binary format
container_too_large = 408, ///< the size of a container in a binary input exceeds the maximal capacity
bson_key_with_null = 409, ///< a BSON key contains U+0000
value_out_of_range = 410, ///< an enum value is undefined, or a JSON Pointer array index exceeds size_type
patch_add_parent_not_container = 411, ///< the parent of a JSON Patch 'add' target is not a container
length_too_large = 412, ///< a length does not fit into the length field of a binary format
patch_remove_parent_not_container = 413, ///< the parent of a JSON Patch 'remove' target is not a container
patch_move_into_child = 414, ///< a JSON Patch 'move' moves a value into one of its children
subtype_out_of_range = 415, ///< a binary subtype does not fit into one byte
// other_error
internal_error = 500, ///< unreachable code was reached
patch_test_failed = 501, ///< a JSON Patch 'test' operation failed
size_marker_required = 502 ///< UBJSON/BJData output with type markers requires size markers
};
/// @brief general exception of the @ref basic_json class /// @brief general exception of the @ref basic_json class
/// @sa https://json.nlohmann.me/api/basic_json/exception/ /// @sa https://json.nlohmann.me/api/basic_json/exception/
class exception : public std::exception class exception : public std::exception
@@ -273,18 +195,6 @@ class parse_error : public exception
return {id_, byte_, w.c_str()}; return {id_, byte_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), pos, what_arg, context);
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), byte_, what_arg, context);
}
/*! /*!
@brief byte index of the parse error @brief byte index of the parse error
@@ -319,12 +229,6 @@ class invalid_iterator : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static invalid_iterator create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
invalid_iterator(int id_, const char* what_arg) invalid_iterator(int id_, const char* what_arg)
@@ -343,12 +247,6 @@ class type_error : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static type_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -366,12 +264,6 @@ class out_of_range : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static out_of_range create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -389,12 +281,6 @@ class other_error : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static other_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -421,36 +307,6 @@ void templated_json_throw(ExceptionType exception)
(void)exception; (void)exception;
} }
/*!
@brief throws because @a j does not have the type a conversion expects
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
@param[in] j the value with the wrong type
@throw type_error.302 always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const BasicJsonType& j)
{
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(j);
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
}
/*!
@brief throws because an operation is not supported for the type of @a j
@param[in] id_ the id of the type_error exception (at_wrong_type..update_wrong_type)
@param[in] operation the operation, e.g. "erase()"
@param[in] j the value the operation was called on
@throw type_error always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
{
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(operation);
static_cast<void>(j);
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
}
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
+135 -94
View File
@@ -12,6 +12,7 @@
#include <cmath> // ldexp #include <cmath> // ldexp
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t, uintmax_t #include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t, uintmax_t
#include <cstdio> // snprintf
#include <cstring> // memcpy #include <cstring> // memcpy
#include <iterator> // back_inserter #include <iterator> // back_inserter
#include <limits> // numeric_limits #include <limits> // numeric_limits
@@ -195,7 +196,8 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof())) if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))
{ {
return last_byte_error(exception_id::unexpected_end_of_input, concat("expected end of input; last byte: 0x", get_token_string()), "value"); return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
exception_message(concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
} }
} }
@@ -317,7 +319,8 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start)) if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
{ {
return last_byte_error(exception_id::unexpected_byte, concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document"); return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
exception_message(concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document"), nullptr));
} }
return true; return true;
} }
@@ -509,7 +512,9 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(len < 1)) if (JSON_HEDLEY_UNLIKELY(len < 1))
{ {
return last_byte_error(exception_id::unexpected_byte, concat("string length must be at least 1, is ", std::to_string(len)), "string"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result))) if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result)))
@@ -519,7 +524,10 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(get() != 0x00)) if (JSON_HEDLEY_UNLIKELY(get() != 0x00))
{ {
return last_byte_error(exception_id::unexpected_byte, "BSON string is not null-terminated", "string"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message("BSON string is not null-terminated",
"string"), nullptr));
} }
return check_string_utf8(result, "string"); return check_string_utf8(result, "string");
@@ -539,7 +547,9 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(len < 0)) if (JSON_HEDLEY_UNLIKELY(len < 0))
{ {
return last_byte_error(exception_id::unexpected_byte, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
} }
// All BSON binary values have a subtype // All BSON binary values have a subtype
@@ -629,9 +639,11 @@ class binary_reader
default: // anything else is not supported (yet) default: // anything else is not supported (yet)
{ {
const std::string cr_str = hex_byte(static_cast<std::uint8_t>(element_type)); std::array<char, 3> cr{{}};
static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
const std::string cr_str{cr.data()};
return sax->parse_error(element_type_parse_position, cr_str, return sax->parse_error(element_type_parse_position, cr_str,
parse_error::create(exception_id::bson_unsupported_type, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr)); parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
} }
} }
} }
@@ -955,7 +967,9 @@ class binary_reader
{ {
if (tag_handler == cbor_tag_handler_t::error) if (tag_handler == cbor_tag_handler_t::error)
{ {
return invalid_byte("value"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
// ignore and store: the tag value is already in the head, so // ignore and store: the tag value is already in the head, so
@@ -975,7 +989,9 @@ class binary_reader
{ {
case cbor_tag_handler_t::error: case cbor_tag_handler_t::error:
{ {
return invalid_byte("value"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
case cbor_tag_handler_t::ignore: case cbor_tag_handler_t::ignore:
@@ -1049,7 +1065,9 @@ class binary_reader
default: // anything else (0xFF is handled inside the other types) default: // anything else (0xFF is handled inside the other types)
{ {
return invalid_byte("value"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
} }
} }
@@ -1062,7 +1080,10 @@ class binary_reader
*/ */
bool cbor_indefinite_string_error(const char* type_name, const char* context) bool cbor_indefinite_string_error(const char* type_name, const char* context)
{ {
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); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("indefinite-length ", type_name,
" is not allowed inside indefinite-length ", type_name, "; last byte: 0x", last_token), context), nullptr));
} }
/*! /*!
@@ -1139,7 +1160,9 @@ class binary_reader
default: default:
{ {
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", get_token_string()), "string"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", last_token), "string"), nullptr));
} }
} }
} }
@@ -1269,7 +1292,9 @@ class binary_reader
break; break;
} }
return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
} }
/*! /*!
@@ -1350,7 +1375,9 @@ class binary_reader
default: default:
{ {
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", get_token_string()), "binary"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", last_token), "binary"), nullptr));
} }
} }
} }
@@ -1496,7 +1523,7 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size())) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
exception_message(concat("excessive ", context, " size"), "size"), nullptr)); exception_message(concat("excessive ", context, " size"), "size"), nullptr));
} }
result = conditional_static_cast<std::size_t>(len); result = conditional_static_cast<std::size_t>(len);
@@ -1984,7 +2011,9 @@ class binary_reader
default: // anything else default: // anything else
{ {
return invalid_byte("value"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
} }
} }
} }
@@ -2065,7 +2094,9 @@ class binary_reader
default: default:
{ {
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", get_token_string()), "string"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
} }
} }
} }
@@ -2157,7 +2188,9 @@ class binary_reader
break; break;
} }
return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
} }
/*! /*!
@@ -2466,7 +2499,8 @@ class binary_reader
{ {
if (JSON_HEDLEY_UNLIKELY(len < 0)) if (JSON_HEDLEY_UNLIKELY(len < 0))
{ {
return last_byte_error(exception_id::invalid_string_or_size, "string length must not be negative", "string"); return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
exception_message("string length must not be negative", "string"), nullptr));
} }
return true; return true;
} }
@@ -2566,7 +2600,18 @@ class binary_reader
default: default:
break; break;
} }
return length_type_error("", "string"); auto last_token = get_token_string();
std::string message;
if (input_format != input_format_t::bjdata)
{
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
}
else
{
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
}
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(message, "string"), nullptr));
} }
/*! /*!
@@ -2651,11 +2696,12 @@ class binary_reader
} }
if (JSON_HEDLEY_UNLIKELY(number < 0)) if (JSON_HEDLEY_UNLIKELY(number < 0))
{ {
return last_byte_error(exception_id::invalid_string_or_size, "count in an optimized container must be positive", "size"); return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
exception_message("count in an optimized container must be positive", "size"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
exception_message("integer value overflow", "size"), nullptr)); exception_message("integer value overflow", "size"), nullptr));
} }
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
@@ -2753,7 +2799,7 @@ class binary_reader
} }
if (!value_in_range_of<std::size_t>(number)) if (!value_in_range_of<std::size_t>(number))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
exception_message("integer value overflow", "size"), nullptr)); exception_message("integer value overflow", "size"), nullptr));
} }
result = detail::conditional_static_cast<std::size_t>(number); result = detail::conditional_static_cast<std::size_t>(number);
@@ -2768,7 +2814,7 @@ class binary_reader
} }
if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array
{ {
return last_byte_error(exception_id::invalid_string_or_size, "ndarray dimensional vector is not allowed", "size"); return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message("ndarray dimensional vector is not allowed", "size"), nullptr));
} }
std::vector<size_t> dim; std::vector<size_t> dim;
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim))) if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
@@ -2804,7 +2850,9 @@ class binary_reader
const char* type_name = bjd_type_name(ndarray_dtype); const char* type_name = bjd_type_name(ndarray_dtype);
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr)) if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
{ {
return invalid_byte("type"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message("invalid byte: 0x" + last_token, "type"), nullptr));
} }
string_t type_key = "_ArrayType_"; string_t type_key = "_ArrayType_";
@@ -2832,7 +2880,7 @@ class binary_reader
// or SIZE_MAX. // or SIZE_MAX.
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i)) if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr)); return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
} }
result *= i; result *= i;
// the pre-check above already rules out result becoming 0 // the pre-check above already rules out result becoming 0
@@ -2841,7 +2889,7 @@ class binary_reader
// unknown-size container (see get_ubjson_size_type()) // unknown-size container (see get_ubjson_size_type())
if (result == npos) if (result == npos)
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr)); return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i))) if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i)))
{ {
@@ -2858,7 +2906,18 @@ class binary_reader
default: default:
break; break;
} }
return length_type_error(" after '#'", "size"); auto last_token = get_token_string();
std::string message;
if (input_format != input_format_t::bjdata)
{
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
}
else
{
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
}
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(message, "size"), nullptr));
} }
/*! /*!
@@ -2891,7 +2950,9 @@ class binary_reader
if (input_format == input_format_t::bjdata if (input_format == input_format_t::bjdata
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second))) && JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
{ {
return last_byte_error(exception_id::unexpected_byte, concat("marker 0x", get_token_string(), " is not a permitted optimized array type"), "type"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type"))) if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
@@ -2906,7 +2967,9 @@ class binary_reader
{ {
return false; return false;
} }
return last_byte_error(exception_id::unexpected_byte, concat("expected '#' after type information; last byte: 0x", get_token_string()), "size"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
} }
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second); const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
@@ -2925,7 +2988,8 @@ class binary_reader
const bool is_error = get_ubjson_size_value(result.first, is_ndarray); const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray) if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
{ {
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size"); return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
exception_message("ndarray requires both type and size", "size"), nullptr));
} }
return is_error; return is_error;
} }
@@ -3057,7 +3121,9 @@ class binary_reader
} }
if (JSON_HEDLEY_UNLIKELY(current > 127)) if (JSON_HEDLEY_UNLIKELY(current > 127))
{ {
return last_byte_error(exception_id::invalid_string_or_size, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", get_token_string()), "char"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
} }
string_t s(1, static_cast<typename string_t::value_type>(current)); string_t s(1, static_cast<typename string_t::value_type>(current));
return sax->string(s); return sax->string(s);
@@ -3078,7 +3144,8 @@ class binary_reader
default: // anything else default: // anything else
break; break;
} }
return invalid_byte("value"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message("invalid byte: 0x" + last_token, "value"), nullptr));
} }
/*! /*!
@@ -3141,7 +3208,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F') if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F')
&& size_and_type.first > max_valueless_container_size)) && size_and_type.first > max_valueless_container_size))
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
exception_message("excessive array size", "size"), nullptr)); exception_message("excessive array size", "size"), nullptr));
} }
@@ -3177,7 +3244,9 @@ class binary_reader
// do not accept ND-array size in objects in BJData // do not accept ND-array size in objects in BJData
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0) if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
{ {
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object"); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message("BJData object does not support ND-array size in optimized format", "object"), nullptr));
} }
if (size_and_type.first != npos) if (size_and_type.first != npos)
@@ -3214,7 +3283,7 @@ class binary_reader
// the lexer would stop at a NUL and accept the digits before it // the lexer would stop at a NUL and accept the digits before it
if (JSON_HEDLEY_UNLIKELY(current == '\0')) if (JSON_HEDLEY_UNLIKELY(current == '\0'))
{ {
return sax->parse_error(chars_read, "00", parse_error::create(exception_id::invalid_high_precision_number, chars_read, return sax->parse_error(chars_read, "00", parse_error::create(115, chars_read,
exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr)); exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr));
} }
number_vector.push_back(static_cast<char>(current)); number_vector.push_back(static_cast<char>(current));
@@ -3231,7 +3300,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
{ {
return sax->parse_error(chars_read, number_string, parse_error::create(exception_id::invalid_high_precision_number, chars_read, return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
} }
@@ -3249,7 +3318,7 @@ class binary_reader
return sax->parse_error( return sax->parse_error(
chars_read, chars_read,
number_string, number_string,
out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", number_string, '\''), nullptr)); out_of_range::create(406, concat("number overflow parsing '", number_string, '\''), nullptr));
} }
// number_string is a std::string, while the SAX interface takes a // number_string is a std::string, while the SAX interface takes a
// string_t; convert explicitly, as the two are only implicitly // string_t; convert explicitly, as the two are only implicitly
@@ -3271,7 +3340,7 @@ class binary_reader
case token_type::end_of_input: case token_type::end_of_input:
case token_type::literal_or_value: case token_type::literal_or_value:
default: default:
return sax->parse_error(chars_read, number_string, parse_error::create(exception_id::invalid_high_precision_number, chars_read, return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
} }
} }
@@ -3330,6 +3399,20 @@ class binary_reader
return 0x80 <= c && c <= 0xBF; return 0x80 <= c && c <= 0xBF;
} }
/*!
@brief report a parse error at the last read byte
@param[in] detail a detailed error message
@param[in] context further context information
@return false
*/
bool bon8_error(const std::string& detail, const char* context)
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(concat(detail, ": 0x", last_token), context), nullptr));
}
/*! /*!
@brief read a BON8 value and everything nested inside it @brief read a BON8 value and everything nested inside it
@@ -3545,7 +3628,7 @@ class binary_reader
} }
// 0xFE: end of container where a value is expected // 0xFE: end of container where a value is expected
return invalid_byte("value"); return bon8_error("invalid byte", "value");
} }
/*! /*!
@@ -3672,7 +3755,7 @@ class binary_reader
current = byte; current = byte;
} }
return unexpected_byte("expected a string; last byte", "key"); return bon8_error("expected a string; last byte", "key");
} }
/*! /*!
@@ -3795,7 +3878,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(!valid_second)) if (JSON_HEDLEY_UNLIKELY(!valid_second))
{ {
return unexpected_byte("invalid UTF-8 byte", "string"); return bon8_error("invalid UTF-8 byte", "string");
} }
result.push_back(static_cast<typename string_t::value_type>(byte)); result.push_back(static_cast<typename string_t::value_type>(byte));
@@ -3809,7 +3892,7 @@ class binary_reader
} }
if (JSON_HEDLEY_UNLIKELY(!is_bon8_continuation(current))) if (JSON_HEDLEY_UNLIKELY(!is_bon8_continuation(current)))
{ {
return unexpected_byte("invalid UTF-8 byte", "string"); return bon8_error("invalid UTF-8 byte", "string");
} }
result.push_back(static_cast<typename string_t::value_type>(current)); result.push_back(static_cast<typename string_t::value_type>(current));
} }
@@ -3854,7 +3937,7 @@ class binary_reader
{ {
// in case of failure, advance position by 1 to report the failing location // in case of failure, advance position by 1 to report the failing location
++chars_read; ++chars_read;
sax->parse_error(chars_read, "<end of file>", parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr)); sax->parse_error(chars_read, "<end of file>", parse_error::create(110, chars_read, exception_message("unexpected end of input", context), nullptr));
return false; return false;
} }
return true; return true;
@@ -4008,7 +4091,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result))) if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
{ {
return sax->parse_error(chars_read, get_token_string(), return sax->parse_error(chars_read, get_token_string(),
out_of_range::create(exception_id::number_overflow, exception_message("number overflow", "value"), nullptr)); out_of_range::create(406, exception_message("number overflow", "value"), nullptr));
} }
return sax->number_float(result, ""); return sax->number_float(result, "");
} }
@@ -4128,7 +4211,9 @@ class binary_reader
if (error_handler == error_handler_t::strict) if (error_handler == error_handler_t::strict)
{ {
return last_byte_error(exception_id::invalid_string_or_size, "invalid string: ill-formed UTF-8 byte", context); auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message("invalid string: ill-formed UTF-8 byte", context), nullptr));
} }
result = sanitize_utf8(result, error_handler); result = sanitize_utf8(result, error_handler);
@@ -4224,63 +4309,19 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof())) if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))
{ {
return sax->parse_error(chars_read, "<end of file>", return sax->parse_error(chars_read, "<end of file>",
parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr)); parse_error::create(110, chars_read, exception_message("unexpected end of input", context), nullptr));
} }
return true; return true;
} }
/*!
@brief reports a parse error at the last read byte
@param[in] id_ the id of the parse_error exception
@param[in] detail a detailed error message
@param[in] context further context information
@return the result of the SAX parser's parse_error()
*/
bool last_byte_error(const exception_id id_, const std::string& detail, const std::string& context) const
{
return sax->parse_error(chars_read, get_token_string(), parse_error::create(id_, chars_read, exception_message(detail, context), nullptr));
}
/*!
@brief reports the last read byte as unexpected (parse_error.112)
@param[in] detail what is wrong with the byte, e.g. "invalid byte"
@param[in] context further context information
@return the result of the SAX parser's parse_error()
*/
bool unexpected_byte(const char* detail, const std::string& context) const
{
return last_byte_error(exception_id::unexpected_byte, concat(detail, ": 0x", get_token_string()), context);
}
/*!
@brief reports the last read byte as invalid (parse_error.112)
@param[in] context further context information
@return the result of the SAX parser's parse_error()
*/
bool invalid_byte(const char* context) const
{
return unexpected_byte("invalid byte", context);
}
/*!
@brief reports that the last read byte is not a UBJSON/BJData length type (parse_error.113)
@param[in] position where the length type was expected, e.g. " after '#'"
@param[in] context further context information
@return the result of the SAX parser's parse_error()
*/
bool length_type_error(const char* position, const char* context) const
{
const char* types = input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L";
return last_byte_error(exception_id::invalid_string_or_size,
concat("expected length type specification (", types, ")", position, "; last byte: 0x", get_token_string()), context);
}
/*! /*!
@return a string representation of the last read byte @return a string representation of the last read byte
*/ */
std::string get_token_string() const std::string get_token_string() const
{ {
return hex_byte(static_cast<std::uint8_t>(current)); std::array<char, 3> cr{{}};
static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
return std::string{cr.data()};
} }
/*! /*!
@@ -581,7 +581,7 @@ class wide_string_input_adapter
template<class T> template<class T>
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1) JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
{ {
JSON_THROW(parse_error::create(exception_id::unexpected_byte, 1, "wide string type cannot be interpreted as binary data", nullptr)); JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
} }
private: private:
@@ -793,7 +793,7 @@ inline file_input_adapter input_adapter(std::FILE* file)
{ {
if (file == nullptr) if (file == nullptr)
{ {
JSON_THROW(parse_error::create(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(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return file_input_adapter(file); return file_input_adapter(file);
} }
@@ -802,7 +802,7 @@ inline input_stream_adapter input_adapter(std::istream& stream)
{ {
if (stream.rdbuf() == nullptr) if (stream.rdbuf() == nullptr)
{ {
JSON_THROW(parse_error::create(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(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return input_stream_adapter(stream); return input_stream_adapter(stream);
} }
@@ -827,7 +827,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b)
{ {
if (b == nullptr) if (b == nullptr)
{ {
JSON_THROW(parse_error::create(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(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
auto length = std::strlen(reinterpret_cast<const char*>(b)); auto length = std::strlen(reinterpret_cast<const char*>(b));
const auto* ptr = reinterpret_cast<const char*>(b); const auto* ptr = reinterpret_cast<const char*>(b);
+76 -83
View File
@@ -176,27 +176,6 @@ template<typename ArrayType>
inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/) inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/)
{} {}
/*!
@brief reports an object or array whose announced size exceeds max_size()
Shared by json_sax_dom_parser and json_sax_dom_callback_parser.
@param[in] sax the SAX parser to report the error to
@param[in] len the number of elements announced by the input, or unknown_size()
@param[in] kind "object" or "array"
@param[in] ref the object or array that was just created
@return whether parsing should continue (false after reporting out_of_range.408)
*/
template<typename SAX, typename BasicJsonType>
bool check_container_size(SAX& sax, std::size_t len, const char* kind, BasicJsonType* ref)
{
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref->max_size()))
{
return sax.parse_error(0, "", out_of_range::create(exception_id::container_too_large, concat("excessive ", kind, " size: ", std::to_string(len)), ref));
}
return true;
}
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
/*! /*!
@brief set the diagnostic positions of a value the DOM SAX parsers just stored @brief set the diagnostic positions of a value the DOM SAX parsers just stored
@@ -206,35 +185,6 @@ befriends this struct, as the position members are private.
*/ */
struct diagnostic_positions struct diagnostic_positions
{ {
/*!
@param[in,out] v the object or array whose opening brace or bracket was just read
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
*/
template<typename BasicJsonType, typename LexerType>
static void set_container_start(BasicJsonType& v, LexerType* lexer)
{
if (lexer)
{
// Lexer has read the first character of the container, so
// subtract 1 from the position to get the correct start position.
v.start_position = lexer->get_position() - 1;
}
}
/*!
@param[in,out] v the object or array whose closing brace or bracket was just read
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
*/
template<typename BasicJsonType, typename LexerType>
static void set_container_end(BasicJsonType& v, LexerType* lexer)
{
if (lexer)
{
// Lexer's position is past the closing brace or bracket, so set that as the end position.
v.end_position = lexer->get_position();
}
}
/*! /*!
@param[in,out] v the value that was just parsed @param[in,out] v the value that was just parsed
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone @param[in] lexer the lexer that read it, or nullptr to leave @a v alone
@@ -399,11 +349,22 @@ class json_sax_dom_parser
ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the object here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer has read the first character of the object, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
return check_container_size(*this, len, "object", ref_stack.back()); if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
return true;
} }
bool key(string_t& val) bool key(string_t& val)
@@ -422,7 +383,11 @@ class json_sax_dom_parser
JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(ref_stack.back()->is_object());
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing brace, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -435,13 +400,17 @@ class json_sax_dom_parser
ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the array here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back()))) if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{ {
return false; return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
} }
if (len != detail::unknown_size()) if (len != detail::unknown_size())
@@ -458,7 +427,11 @@ class json_sax_dom_parser
JSON_ASSERT(ref_stack.back()->is_array()); JSON_ASSERT(ref_stack.back()->is_array());
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing bracket, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -638,11 +611,21 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the object here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer has read the first character of the object, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
return check_container_size(*this, len, "object", ref_stack.back()); // check object limit
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
} }
return true; return true;
} }
@@ -712,7 +695,11 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing brace, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -723,7 +710,13 @@ class json_sax_dom_callback_parser
} }
} }
const string_t object_key = pop_container(); JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
const string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
{ {
@@ -749,13 +742,20 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the array here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer has read the first character of the array, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back()))) // check array limit
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{ {
return false; return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
} }
if (len != detail::unknown_size()) if (len != detail::unknown_size())
@@ -779,7 +779,11 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing bracket, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -805,7 +809,13 @@ class json_sax_dom_callback_parser
} }
} }
const string_t object_key = pop_container(); JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
const string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
// remove discarded value // remove discarded value
if (!ref_stack.empty() && ref_stack.back()) if (!ref_stack.empty() && ref_stack.back())
@@ -892,23 +902,6 @@ class json_sax_dom_callback_parser
return string_t{}; return string_t{};
} }
/*!
@brief leave the object or array that end_object()/end_array() just closed
@return the key the container is stored under in its parent (see
container_key_stack)
*/
string_t pop_container()
{
JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
return object_key;
}
/*! /*!
@brief remove the discarded value the callback rejected from its parent, @brief remove the discarded value the callback rejected from its parent,
unless it is a duplicate key's slot with a stashed previous value, in unless it is a duplicate key's slot with a stashed previous value, in
+39 -26
View File
@@ -156,7 +156,9 @@ class parser
// strict mode: next byte must be EOF // strict mode: next byte must be EOF
if (get_token() != token_type::end_of_input) if (get_token() != token_type::end_of_input)
{ {
return syntax_error(*sax, exception_message(token_type::end_of_input, "value")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
} }
} }
else else
@@ -195,7 +197,10 @@ class parser
// in strict mode, input must be completely read // in strict mode, input must be completely read
if (get_token() != token_type::end_of_input) if (get_token() != token_type::end_of_input)
{ {
syntax_error(sdp, exception_message(token_type::end_of_input, "value")); sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value"), nullptr));
} }
} }
else else
@@ -245,7 +250,9 @@ class parser
// parse key // parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{ {
return syntax_error(*sax, exception_message(token_type::value_string, "object key")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{ {
@@ -255,7 +262,9 @@ class parser
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
{ {
return syntax_error(*sax, exception_message(token_type::name_separator, "object separator")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
} }
// remember we are now inside an object // remember we are now inside an object
@@ -298,7 +307,7 @@ class parser
{ {
return sax->parse_error(m_lexer.get_position(), return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(), m_lexer.get_token_string(),
out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)); out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
@@ -366,16 +375,23 @@ class parser
case token_type::parse_error: case token_type::parse_error:
{ {
// using "uninitialized" to avoid an "expected" message // using "uninitialized" to avoid an "expected" message
return syntax_error(*sax, exception_message(token_type::uninitialized, "value")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
} }
case token_type::end_of_input: case token_type::end_of_input:
{ {
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1)) if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
{ {
return syntax_error(*sax, "attempting to parse an empty input; check that your input string or stream contains the expected JSON"); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return syntax_error(*sax, exception_message(token_type::literal_or_value, "value")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
} }
case token_type::uninitialized: case token_type::uninitialized:
case token_type::end_array: case token_type::end_array:
@@ -385,7 +401,9 @@ class parser
case token_type::literal_or_value: case token_type::literal_or_value:
default: // the last token was unexpected default: // the last token was unexpected
{ {
return syntax_error(*sax, exception_message(token_type::literal_or_value, "value")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
} }
} }
} }
@@ -436,7 +454,9 @@ class parser
continue; continue;
} }
return syntax_error(*sax, exception_message(token_type::end_array, "array")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
} }
// states.back() is false -> object // states.back() is false -> object
@@ -453,7 +473,9 @@ class parser
// parse key // parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{ {
return syntax_error(*sax, exception_message(token_type::value_string, "object key")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
@@ -464,7 +486,9 @@ class parser
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
{ {
return syntax_error(*sax, exception_message(token_type::name_separator, "object separator")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
} }
// parse values // parse values
@@ -492,7 +516,9 @@ class parser
continue; continue;
} }
return syntax_error(*sax, exception_message(token_type::end_object, "object")); return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
} }
} }
@@ -509,19 +535,6 @@ class parser
return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type; return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type;
} }
/*!
@brief reports a syntax error at the current token (parse_error.101)
@param[in] sax the SAX parser to report the error to
@param[in] message the error message
@return the result of the SAX parser's parse_error()
*/
template<typename SAX>
bool syntax_error(SAX& sax, const std::string& message)
{
return sax.parse_error(m_lexer.get_position(), m_lexer.get_token_string(),
parse_error::create(exception_id::syntax_error, m_lexer.get_position(), message, nullptr));
}
std::string exception_message(const token_type expected, const std::string& context) std::string exception_message(const token_type expected, const std::string& context)
{ {
std::string error_msg = "syntax error "; std::string error_msg = "syntax error ";
+12 -18
View File
@@ -279,12 +279,6 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
} }
} }
/// @throw invalid_iterator.214 always
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
{
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
}
public: public:
/*! /*!
@brief return a reference to the value pointed to by the iterator @brief return a reference to the value pointed to by the iterator
@@ -309,7 +303,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
} }
case value_t::null: case value_t::null:
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
case value_t::string: case value_t::string:
case value_t::boolean: case value_t::boolean:
@@ -325,7 +319,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object; return *m_object;
} }
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
} }
} }
} }
@@ -367,7 +361,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_object; return m_object;
} }
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
} }
} }
} }
@@ -484,7 +478,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object)); JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
} }
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -533,7 +527,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object)); JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
} }
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -546,7 +540,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object)); JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
case value_t::array: case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator); return (m_it.array_iterator < other.m_it.array_iterator);
@@ -602,7 +596,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object)); JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
case value_t::array: case value_t::array:
{ {
@@ -681,7 +675,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object)); JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
case value_t::array: case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator; return m_it.array_iterator - other.m_it.array_iterator;
@@ -710,13 +704,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object)); JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
case value_t::array: case value_t::array:
return *std::next(m_it.array_iterator, n); return *std::next(m_it.array_iterator, n);
case value_t::null: case value_t::null:
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
case value_t::string: case value_t::string:
case value_t::boolean: case value_t::boolean:
@@ -732,7 +726,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object; return *m_object;
} }
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
} }
} }
} }
@@ -750,7 +744,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_it.object_iterator->first; return m_it.object_iterator->first;
} }
JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object)); JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
} }
/*! /*!
+22 -37
View File
@@ -166,7 +166,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
reference_tokens.erase(reference_tokens.begin()); reference_tokens.erase(reference_tokens.begin());
@@ -178,7 +178,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
return reference_tokens.front(); return reference_tokens.front();
@@ -204,7 +204,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
reference_tokens.pop_back(); reference_tokens.pop_back();
@@ -216,7 +216,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
return reference_tokens.back(); return reference_tokens.back();
@@ -244,21 +244,6 @@ class json_pointer
} }
private: private:
/// @throw out_of_range.405 always
JSON_HEDLEY_NO_RETURN static void throw_no_parent()
{
JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr));
}
/// @throw out_of_range.404 always
template<typename BasicJsonContext>
JSON_HEDLEY_NO_RETURN static void throw_unresolved(const string_t& reference_token, BasicJsonContext context)
{
static_cast<void>(reference_token); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(context);
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_unresolved, detail::concat("unresolved reference token '", reference_token, "'"), context));
}
/*! /*!
@brief result of @ref parse_array_index @brief result of @ref parse_array_index
@@ -344,13 +329,13 @@ class json_pointer
// the branches differ in their messages, not after JSON_THROW's expansion // the branches differ in their messages, not after JSON_THROW's expansion
// NOLINTNEXTLINE(bugprone-branch-clone) // NOLINTNEXTLINE(bugprone-branch-clone)
case array_index_status::leading_zero: case array_index_status::leading_zero:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
case array_index_status::not_a_number: case array_index_status::not_a_number:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
case array_index_status::unresolved: case array_index_status::unresolved:
throw_unresolved(s, nullptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
case array_index_status::exceeds_size_type: case array_index_status::exceeds_size_type:
JSON_THROW(detail::out_of_range::create(detail::exception_id::value_out_of_range, detail::concat("array index ", s, " exceeds size_type"), nullptr)); JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));
case array_index_status::ok: case array_index_status::ok:
default: default:
break; break;
@@ -364,7 +349,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
json_pointer result = *this; json_pointer result = *this;
@@ -452,7 +437,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_invalid_value, "invalid value to unflatten", &j)); JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
} }
prefix.push_back(reference_token); prefix.push_back(reference_token);
@@ -535,7 +520,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
throw_unresolved(reference_token, ptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
} }
} }
@@ -567,7 +552,7 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{ {
// "-" always fails the range check // "-" always fails the range check
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat( JSON_THROW(detail::out_of_range::create(402, detail::concat(
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
") is out of range"), ptr)); ") is out of range"), ptr));
} }
@@ -576,7 +561,7 @@ class json_pointer
// Bounds check before access to avoid exception with JSON_NOEXCEPTION // Bounds check before access to avoid exception with JSON_NOEXCEPTION
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
{ {
JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat( JSON_THROW(detail::out_of_range::create(401, detail::concat(
"array index ", std::to_string(idx), " is out of range"), ptr)); "array index ", std::to_string(idx), " is out of range"), ptr));
} }
ptr = &ptr->operator[](idx); ptr = &ptr->operator[](idx);
@@ -592,7 +577,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
throw_unresolved(reference_token, ptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
} }
} }
@@ -636,7 +621,7 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{ {
// "-" cannot be used for const access // "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(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)); JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access; the const operator[] // use unchecked array access; the const operator[]
@@ -654,7 +639,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
throw_unresolved(reference_token, ptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
} }
} }
@@ -709,9 +694,9 @@ class json_pointer
// the branches differ in their messages, not after JSON_THROW's expansion // the branches differ in their messages, not after JSON_THROW's expansion
// NOLINTNEXTLINE(bugprone-branch-clone) // NOLINTNEXTLINE(bugprone-branch-clone)
case array_index_status::leading_zero: case array_index_status::leading_zero:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr)); JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
case array_index_status::not_a_number: case array_index_status::not_a_number:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr)); JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
case array_index_status::unresolved: case array_index_status::unresolved:
case array_index_status::exceeds_size_type: case array_index_status::exceeds_size_type:
return nullptr; return nullptr;
@@ -838,7 +823,7 @@ class json_pointer
// check if a nonempty reference string begins with slash // check if a nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{ {
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
} }
// extract the reference tokens: // extract the reference tokens:
@@ -874,7 +859,7 @@ class json_pointer
(reference_token[pos + 1] != '0' && (reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1'))) reference_token[pos + 1] != '1')))
{ {
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
} }
} }
@@ -971,7 +956,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(!value.is_object())) if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{ {
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_not_object, "only objects can be unflattened", &value)); JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
} }
BasicJsonType result; BasicJsonType result;
@@ -999,7 +984,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{ {
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_value_not_primitive, "values in object must be primitive", &element.second)); JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
} }
// Assign the value to the reference pointed to by JSON pointer. Note // Assign the value to the reference pointed to by JSON pointer. Note
+2 -2
View File
@@ -312,7 +312,7 @@
return ej_pair.first == e; \ return ej_pair.first == e; \
}); \ }); \
if (it != std::end(m)) j = it->second; \ if (it != std::end(m)) j = it->second; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range,"enum value out of range for " #ENUM_TYPE, nullptr)); \ else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \
} \ } \
template<typename BasicJsonType> \ template<typename BasicJsonType> \
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
@@ -327,7 +327,7 @@
return ej_pair.second == j; \ return ej_pair.second == j; \
}); \ }); \
if (it != std::end(m)) e = it->first; \ if (it != std::end(m)) e = it->first; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \ else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
} }
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They // Ugly macros to avoid uglier copy-paste when specializing basic_json. They
@@ -151,7 +151,7 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
default: default:
{ {
JSON_THROW(type_error::create(exception_id::type_not_serializable, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j)); JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
} }
} }
} }
@@ -354,7 +354,7 @@ class binary_writer
{ {
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length)))
{ {
JSON_THROW(out_of_range::create(exception_id::length_too_large, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j)); JSON_THROW(out_of_range::create(412, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j));
} }
static_cast<void>(j); static_cast<void>(j);
@@ -613,7 +613,7 @@ class binary_writer
{ {
if (JSON_HEDLEY_UNLIKELY(j.m_data.m_value.binary->subtype() > (std::numeric_limits<std::uint8_t>::max)())) if (JSON_HEDLEY_UNLIKELY(j.m_data.m_value.binary->subtype() > (std::numeric_limits<std::uint8_t>::max)()))
{ {
JSON_THROW(out_of_range::create(exception_id::subtype_out_of_range, concat("subtype ", std::to_string(j.m_data.m_value.binary->subtype()), " is too large for the MessagePack ext type (max 255)"), &j)); JSON_THROW(out_of_range::create(415, concat("subtype ", std::to_string(j.m_data.m_value.binary->subtype()), " is too large for the MessagePack ext type (max 255)"), &j));
} }
write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype())); write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype()));
@@ -767,7 +767,7 @@ class binary_writer
{ {
if (!use_count) if (!use_count)
{ {
JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j)); JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
} }
oa.write_character(to_char_type('$')); oa.write_character(to_char_type('$'));
oa.write_character(bjdata_draft3 ? 'B' : 'U'); oa.write_character(bjdata_draft3 ? 'B' : 'U');
@@ -866,7 +866,7 @@ class binary_writer
{ {
static_cast<void>(j); // unused when JSON_NOEXCEPTION is defined static_cast<void>(j); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(format_name); static_cast<void>(format_name);
JSON_THROW(type_error::create(exception_id::discarded_value_used, concat("cannot serialize discarded value to ", format_name), &j)); JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j));
} }
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j) void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
@@ -1132,7 +1132,7 @@ class binary_writer
{ {
if (!use_count) if (!use_count)
{ {
JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j)); JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
} }
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
const bool same_prefix = std::all_of(j.begin() + 1, j.end(), const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
@@ -1182,7 +1182,7 @@ class binary_writer
{ {
if (!use_count) if (!use_count)
{ {
JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j)); JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
} }
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
const bool same_prefix = std::all_of(j.begin(), j.end(), const bool same_prefix = std::all_of(j.begin(), j.end(),
@@ -1397,7 +1397,7 @@ class binary_writer
const auto it = name.find(static_cast<typename string_t::value_type>(0)); const auto it = name.find(static_cast<typename string_t::value_type>(0));
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
{ {
JSON_THROW(out_of_range::create(exception_id::bson_key_with_null, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j)); JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
} }
string_t storage; string_t storage;
@@ -1415,7 +1415,7 @@ class binary_writer
{ {
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
{ {
JSON_THROW(out_of_range::create(exception_id::length_too_large, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr)); JSON_THROW(out_of_range::create(412, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr));
} }
return static_cast<std::int32_t>(size); return static_cast<std::int32_t>(size);
@@ -1601,7 +1601,7 @@ class binary_writer
if (value.has_subtype() && JSON_HEDLEY_UNLIKELY(value.subtype() > (std::numeric_limits<std::uint8_t>::max)())) if (value.has_subtype() && JSON_HEDLEY_UNLIKELY(value.subtype() > (std::numeric_limits<std::uint8_t>::max)()))
{ {
JSON_THROW(out_of_range::create(exception_id::subtype_out_of_range, concat("subtype ", std::to_string(value.subtype()), " is too large for the BSON binary subtype (max 255)"), &j)); JSON_THROW(out_of_range::create(415, concat("subtype ", std::to_string(value.subtype()), " is too large for the BSON binary subtype (max 255)"), &j));
} }
return sizeof(std::int32_t) + value.size() + 1ul; return sizeof(std::int32_t) + value.size() + 1ul;
@@ -2551,7 +2551,7 @@ class binary_writer
{ {
if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)())) if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)()))
{ {
JSON_THROW(out_of_range::create(exception_id::integer_too_large, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j)); JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j));
} }
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned)); write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
string_open = false; string_open = false;
@@ -2704,7 +2704,7 @@ class binary_writer
const std::size_t valid = valid_utf8_prefix(data, s.size()); const std::size_t valid = valid_utf8_prefix(data, s.size());
if (JSON_HEDLEY_UNLIKELY(valid != s.size())) if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
{ {
JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context)); JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context));
} }
} }
@@ -888,7 +888,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr)); JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
} }
case error_handler_t::ignore: case error_handler_t::ignore:
@@ -1021,7 +1021,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr)); JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr));
} }
case error_handler_t::ignore: case error_handler_t::ignore:
+71 -80
View File
@@ -639,7 +639,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
object = nullptr; // silence warning, see #821 object = nullptr; // silence warning, see #821
if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
{ {
JSON_THROW(other_error::create(detail::exception_id::internal_error, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
} }
break; break;
} }
@@ -2287,7 +2287,7 @@ public:
// if an object is wanted but impossible, throw an exception // if an object is wanted but impossible, throw an exception
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
{ {
JSON_THROW(type_error::create(detail::exception_id::object_from_non_pairs, "cannot create object from initializer list", nullptr)); JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
} }
} }
@@ -2407,7 +2407,7 @@ public:
// make sure the iterator fits the current value // make sure the iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterators_incompatible, "iterators are not compatible", nullptr)); JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
} }
// copy type from the first iterator // copy type from the first iterator
@@ -2426,7 +2426,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end())) || !last.m_it.primitive_iterator.is_end()))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object)); JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
} }
break; break;
} }
@@ -2494,7 +2494,7 @@ public:
case value_t::null: case value_t::null:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object)); JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
} }
set_parents(); set_parents();
@@ -2882,7 +2882,7 @@ public:
return *ptr; return *ptr;
} }
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj)); JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
} }
public: public:
@@ -3266,7 +3266,7 @@ public:
{ {
if (!is_binary()) if (!is_binary())
{ {
detail::throw_type_must_be("binary", *this); JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
} }
return *get_ptr<binary_t*>(); return *get_ptr<binary_t*>();
@@ -3278,7 +3278,7 @@ public:
{ {
if (!is_binary()) if (!is_binary())
{ {
detail::throw_type_must_be("binary", *this); JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
} }
return *get_ptr<const binary_t*>(); return *get_ptr<const binary_t*>();
@@ -3320,7 +3320,7 @@ public:
// at only works for objects // at only works for objects
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::at_wrong_type, "at()", j); JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", j.type_name()), &j));
} }
auto it = object_lookup(j, std::forward<KeyType>(key)); auto it = object_lookup(j, std::forward<KeyType>(key));
@@ -3330,7 +3330,7 @@ public:
// std::map or ordered_map) never moves from its argument, so key is still // std::map or ordered_map) never moves from its argument, so key is still
// valid here regardless of whether KeyType was deduced as an rvalue reference // valid here regardless of whether KeyType was deduced as an rvalue reference
// NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved) // NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved)
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", string_t(key), "' not found"), &j)); JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), &j));
} }
return it->second; return it->second;
} }
@@ -3345,12 +3345,12 @@ public:
// at only works for arrays // at only works for arrays
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
detail::throw_cannot_use_with(detail::exception_id::at_wrong_type, "at()", j); JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", j.type_name()), &j));
} }
if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size())) if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size()))
{ {
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j)); JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
} }
return (*j.m_data.m_value.array)[idx]; return (*j.m_data.m_value.array)[idx];
@@ -3383,15 +3383,6 @@ public:
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
} }
/// @brief throws because operator[] is not supported for the type of this value
/// @param[in] argument the kind of the operator's argument, "numeric" or "string"
/// @throw type_error.305 always
JSON_HEDLEY_NO_RETURN void throw_subscript_wrong_type(const char* argument) const
{
detail::throw_cannot_use_with(detail::exception_id::subscript_wrong_type,
detail::concat("operator[] with a ", argument, " argument").c_str(), *this);
}
public: public:
//////////////////// ////////////////////
// element access // // element access //
@@ -3496,7 +3487,7 @@ public:
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
throw_subscript_wrong_type("numeric"); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
} }
/// @brief access specified array element /// @brief access specified array element
@@ -3510,7 +3501,7 @@ public:
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
throw_subscript_wrong_type("numeric"); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
} }
/// @brief access specified object element /// @brief access specified object element
@@ -3530,7 +3521,7 @@ public:
return set_parent(result.first->second); return set_parent(result.first->second);
} }
throw_subscript_wrong_type("string"); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
} }
/// @brief access specified object element /// @brief access specified object element
@@ -3545,7 +3536,7 @@ public:
return it->second; return it->second;
} }
throw_subscript_wrong_type("string"); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
} }
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
@@ -3581,7 +3572,7 @@ public:
return set_parent(result.first->second); return set_parent(result.first->second);
} }
throw_subscript_wrong_type("string"); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
} }
/// @brief access specified object element /// @brief access specified object element
@@ -3598,7 +3589,7 @@ public:
return it->second; return it->second;
} }
throw_subscript_wrong_type("string"); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
} }
private: private:
@@ -3622,7 +3613,7 @@ public:
// value only works for objects // value only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::value_wrong_type, "value()", *this); JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
} }
const auto it = find(std::forward<KeyType>(key)); const auto it = find(std::forward<KeyType>(key));
@@ -3637,7 +3628,7 @@ public:
// value only works for arrays and objects // value only works for arrays and objects
if (JSON_HEDLEY_UNLIKELY(!is_structured())) if (JSON_HEDLEY_UNLIKELY(!is_structured()))
{ {
detail::throw_cannot_use_with(detail::exception_id::value_wrong_type, "value()", *this); JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
} }
return ptr.get_checked_or_null(this); return ptr.get_checked_or_null(this);
@@ -3804,7 +3795,7 @@ public:
// make sure the iterator fits the current value // make sure the iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
} }
IteratorType result = end(); IteratorType result = end();
@@ -3820,7 +3811,7 @@ public:
{ {
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_out_of_range, "iterator out of range", this)); JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
} }
m_data.m_value.destroy(m_data.m_type); m_data.m_value.destroy(m_data.m_type);
@@ -3846,7 +3837,7 @@ public:
case value_t::null: case value_t::null:
case value_t::discarded: case value_t::discarded:
default: default:
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this); JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
} }
return result; return result;
@@ -3862,7 +3853,7 @@ public:
// make sure the iterator fits the current value // make sure the iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_from_other_value, "iterators do not fit current value", this)); JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
} }
IteratorType result = end(); IteratorType result = end();
@@ -3879,7 +3870,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end())) || !last.m_it.primitive_iterator.is_end()))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this)); JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
} }
m_data.m_value.destroy(m_data.m_type); m_data.m_value.destroy(m_data.m_type);
@@ -3907,7 +3898,7 @@ public:
case value_t::null: case value_t::null:
case value_t::discarded: case value_t::discarded:
default: default:
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this); JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
} }
return result; return result;
@@ -3921,7 +3912,7 @@ public:
// this erase only works for objects // this erase only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this); JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
} }
const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key)); const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key));
@@ -3936,7 +3927,7 @@ public:
// this erase only works for objects // this erase only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this); JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
} }
const auto it = object_lookup(*this, std::forward<KeyType>(key)); const auto it = object_lookup(*this, std::forward<KeyType>(key));
@@ -3978,14 +3969,14 @@ public:
{ {
if (JSON_HEDLEY_UNLIKELY(idx >= size())) if (JSON_HEDLEY_UNLIKELY(idx >= size()))
{ {
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
} }
m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx)); m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
} }
else else
{ {
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this); JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
} }
} }
@@ -4474,7 +4465,7 @@ public:
// push_back only works for null objects or arrays // push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{ {
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
} }
// transform a null object into an array // transform a null object into an array
@@ -4505,7 +4496,7 @@ public:
// push_back only works for null objects or arrays // push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{ {
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
} }
// transform a null object into an array // transform a null object into an array
@@ -4535,7 +4526,7 @@ public:
// push_back only works for null objects or objects // push_back only works for null objects or objects
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{ {
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this); JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
} }
// transform a null object into an object // transform a null object into an object
@@ -4589,7 +4580,7 @@ public:
// emplace_back only works for null objects or arrays // emplace_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{ {
detail::throw_cannot_use_with(detail::exception_id::emplace_wrong_type, "emplace_back()", *this); JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
} }
// transform a null object into an array // transform a null object into an array
@@ -4612,7 +4603,7 @@ public:
// emplace only works for null objects or arrays // emplace only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{ {
detail::throw_cannot_use_with(detail::exception_id::emplace_wrong_type, "emplace()", *this); JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
} }
// transform a null object into an object // transform a null object into an object
@@ -4664,14 +4655,14 @@ public:
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
} }
// insert to array and return iterator // insert to array and return iterator
return insert_iterator(pos, val); return insert_iterator(pos, val);
} }
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
} }
/// @brief inserts element into array /// @brief inserts element into array
@@ -4684,7 +4675,7 @@ public:
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
} }
// moving into a local first keeps this safe even if val aliases // moving into a local first keeps this safe even if val aliases
@@ -4693,7 +4684,7 @@ public:
return insert_iterator(pos, std::move(tmp)); return insert_iterator(pos, std::move(tmp));
} }
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
} }
/// @brief inserts copies of element into array /// @brief inserts copies of element into array
@@ -4706,14 +4697,14 @@ public:
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
} }
// insert to array and return iterator // insert to array and return iterator
return insert_iterator(pos, cnt, val); return insert_iterator(pos, cnt, val);
} }
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
} }
/// @brief inserts range of elements into array /// @brief inserts range of elements into array
@@ -4723,30 +4714,30 @@ public:
// insert only works for arrays // insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array())) if (JSON_HEDLEY_UNLIKELY(!is_array()))
{ {
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
} }
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
} }
// check if range iterators belong to the same JSON object // check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this)); JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
} }
if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_into_itself, "passed iterators may not belong to container", this)); JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
} }
// passed iterators must belong to arrays // passed iterators must belong to arrays
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array())) if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array()))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to arrays", this)); JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to arrays", this));
} }
// insert to array and return iterator // insert to array and return iterator
@@ -4760,13 +4751,13 @@ public:
// insert only works for arrays // insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array())) if (JSON_HEDLEY_UNLIKELY(!is_array()))
{ {
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
} }
// check if iterator pos fits to this JSON value // check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this)); JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
} }
// copy the values first: ilist may refer to elements of this array // copy the values first: ilist may refer to elements of this array
@@ -4788,19 +4779,19 @@ public:
// insert only works for objects // insert only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this); JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
} }
// check if range iterators belong to the same JSON object // check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this)); JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
} }
// passed iterators must belong to objects // passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to objects", this)); JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
} }
m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
@@ -4817,7 +4808,7 @@ public:
// j, not the copy made below) // j, not the copy made below)
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", j); JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", j.type_name()), &j));
} }
// copy first: j may be *this or one of its descendants, and is // copy first: j may be *this or one of its descendants, and is
@@ -4835,13 +4826,13 @@ public:
// check if range iterators belong to the same JSON object // check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{ {
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this)); JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
} }
// passed iterators must belong to objects // passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", *first.m_object); JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
} }
// copy first: the range may belong to *this or one of its // copy first: the range may belong to *this or one of its
@@ -4877,7 +4868,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(!is_object())) if (JSON_HEDLEY_UNLIKELY(!is_object()))
{ {
detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", *this); JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
} }
} }
@@ -5040,7 +5031,7 @@ public:
} }
else else
{ {
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(array_t&)", *this); JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this));
} }
} }
@@ -5057,7 +5048,7 @@ public:
} }
else else
{ {
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(object_t&)", *this); JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this));
} }
} }
@@ -5073,7 +5064,7 @@ public:
} }
else else
{ {
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(string_t&)", *this); JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this));
} }
} }
@@ -5089,7 +5080,7 @@ public:
} }
else else
{ {
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t&)", *this); JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this));
} }
} }
@@ -5105,7 +5096,7 @@ public:
} }
else else
{ {
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t::container_type&)", *this); JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this));
} }
} }
@@ -6567,7 +6558,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
{ {
// avoid undefined behavior // avoid undefined behavior
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent)); JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
} }
// default case: insert add offset // default case: insert add offset
@@ -6586,7 +6577,7 @@ public:
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
JSON_THROW(out_of_range::create(detail::exception_id::patch_add_parent_not_container, detail::concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent)); JSON_THROW(out_of_range::create(411, detail::concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
} }
}; };
@@ -6609,7 +6600,7 @@ public:
} }
else else
{ {
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", last_path, "' not found"), this)); JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
} }
} }
else if (parent.is_array()) else if (parent.is_array())
@@ -6621,7 +6612,7 @@ public:
{ {
// the parent of a "remove" target must be an object or array // the parent of a "remove" target must be an object or array
// (see #5396) // (see #5396)
JSON_THROW(out_of_range::create(detail::exception_id::patch_remove_parent_not_container, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent)); JSON_THROW(out_of_range::create(413, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
} }
}; };
@@ -6652,7 +6643,7 @@ public:
// type check: top level value must be an array // type check: top level value must be an array
if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
{ {
JSON_THROW(parse_error::create(detail::exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &json_patch)); JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
} }
// iterate and apply the operations // iterate and apply the operations
@@ -6673,14 +6664,14 @@ public:
if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end())) if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
{ {
// NOLINTNEXTLINE(performance-inefficient-string-concatenation) // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
} }
// check if the result is of type string // check if the result is of type string
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
{ {
// NOLINTNEXTLINE(performance-inefficient-string-concatenation) // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val)); JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
} }
// no error: return value // no error: return value
@@ -6690,7 +6681,7 @@ public:
// type check: every element of the array must be an object // type check: every element of the array must be an object
if (JSON_HEDLEY_UNLIKELY(!val.is_object())) if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
{ {
JSON_THROW(parse_error::create(detail::exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &val)); JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
} }
// collect mandatory members // collect mandatory members
@@ -6726,7 +6717,7 @@ public:
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr))) if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
{ {
JSON_THROW(out_of_range::create(detail::exception_id::patch_move_into_child, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result)); JSON_THROW(out_of_range::create(414, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result));
} }
// the "from" location must exist - use at() // the "from" location must exist - use at()
@@ -6773,7 +6764,7 @@ public:
// throw an exception if the test fails // throw an exception if the test fails
if (JSON_HEDLEY_UNLIKELY(!success)) if (JSON_HEDLEY_UNLIKELY(!success))
{ {
JSON_THROW(other_error::create(detail::exception_id::patch_test_failed, detail::concat("unsuccessful: ", val.dump()), &val)); JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
} }
break; break;
@@ -6784,7 +6775,7 @@ public:
{ {
// op must be "add", "remove", "replace", "move", "copy", or // op must be "add", "remove", "replace", "move", "copy", or
// "test" // "test"
JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat("operation value '", op, "' is invalid"), &val)); JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
} }
} }
} }
+24 -17
View File
@@ -90,19 +90,6 @@ private:
return self.end(); return self.end();
} }
/// @brief shared implementation of the const and non-const at() overloads
/// @throw std::out_of_range if @a key is not found
template<typename Self, typename KeyType>
static auto at_impl(Self& self, const KeyType& key) -> decltype((self.begin()->second))
{
const auto it = find_impl(self, key);
if (it == self.end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
}
/// @brief remove the entry @a it points to, preserving order /// @brief remove the entry @a it points to, preserving order
/// @note keys are not movable, so the tail is destroyed and re-constructed in place /// @note keys are not movable, so the tail is destroyed and re-constructed in place
void erase_at(iterator it) void erase_at(iterator it)
@@ -169,26 +156,46 @@ public:
T& at(const key_type& key) T& at(const key_type& key)
{ {
return at_impl(*this, key); const auto it = find_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
{ {
return at_impl(*this, key); const auto it = find_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
const T& at(const key_type& key) const const T& at(const key_type& key) const
{ {
return at_impl(*this, key); const auto it = find_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
{ {
return at_impl(*this, key); const auto it = find_impl(*this, key);
if (it == this->end())
{
JSON_THROW(std::out_of_range("key not found"));
}
return it->second;
} }
size_type erase(const key_type& key) size_type erase(const key_type& key)
+8 -104
View File
@@ -2,120 +2,24 @@ project('nlohmann_json',
'cpp', 'cpp',
version : '3.12.0', version : '3.12.0',
license : 'MIT', license : 'MIT',
meson_version : '>= 0.64',
default_options: ['cpp_std=c++11'],
) )
if get_option('MultipleHeaders')
incdir = 'include'
else
incdir = 'single_include'
endif
# The same compile definitions as the CMake target (see target_compile_definitions
# in CMakeLists.txt): only an option that differs from its default adds one.
json_defines = []
if not get_option('GlobalUDLs')
json_defines += 'JSON_USE_GLOBAL_UDLS=0'
endif
if not get_option('ImplicitConversions')
json_defines += 'JSON_USE_IMPLICIT_CONVERSIONS=0'
endif
if get_option('DisableEnumSerialization')
json_defines += 'JSON_DISABLE_ENUM_SERIALIZATION=1'
endif
if get_option('DisableTupleReferenceConversion')
json_defines += 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1'
endif
if get_option('Diagnostics')
json_defines += 'JSON_DIAGNOSTICS=1'
endif
if get_option('Diagnostic_Positions')
json_defines += 'JSON_DIAGNOSTIC_POSITIONS=1'
endif
if get_option('LegacyDiscardedValueComparison')
json_defines += 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1'
endif
if get_option('StrictNulHandling')
json_defines += 'JSON_STRICT_NUL_HANDLING=1'
endif
if get_option('StrictBinaryUTF8')
json_defines += 'JSON_STRICT_BINARY_UTF8=1'
endif
if get_option('DeleteDeprecatedFunctions')
json_defines += 'JSON_DELETE_DEPRECATED_FUNCTIONS=1'
endif
cpp_args = []
foreach define : json_defines
cpp_args += '-D' + define
endforeach
nlohmann_json_dep = declare_dependency( nlohmann_json_dep = declare_dependency(
compile_args: cpp_args, include_directories: include_directories('single_include')
include_directories: include_directories(incdir)
) )
meson.override_dependency('nlohmann_json', nlohmann_json_dep)
# The multi-header version under the name earlier versions of this file used
nlohmann_json_multiple_headers = declare_dependency( nlohmann_json_multiple_headers = declare_dependency(
compile_args: cpp_args,
include_directories: include_directories('include') include_directories: include_directories('include')
) )
if not meson.is_subproject() if not meson.is_subproject()
install_subdir( install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
incdir / 'nlohmann', install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann')
install_dir: get_option('includedir'), install_headers('single_include/nlohmann/json_literals.hpp', subdir: 'nlohmann')
install_tag: 'devel',
)
pkgc = import('pkgconfig') pkgc = import('pkgconfig')
pkgc.generate(name: 'nlohmann_json', pkgc.generate(name: 'nlohmann_json',
version: meson.project_version(), version: meson.project_version(),
description: 'JSON for Modern C++', description: 'JSON for Modern C++'
extra_cflags: cpp_args, )
install_dir: get_option('datadir') / 'pkgconfig',
)
# CMake package config files, so that find_package(nlohmann_json) works. The
# include directory is given relative to the config files, so that the
# installation can be relocated. This is not possible if includedir or datadir
# is an absolute path outside the prefix (e.g., with the separate outputs of
# Nix); then the absolute include directory is used, as CMake does.
fs = import('fs')
cmake_install_dir = get_option('datadir') / 'cmake' / meson.project_name()
cmake_to_prefix = []
if fs.is_absolute(get_option('includedir')) or fs.is_absolute(cmake_install_dir)
cmake_include_dir = (get_option('prefix') / get_option('includedir')).replace('\\', '/')
else
foreach component : cmake_install_dir.split('/')
cmake_to_prefix += '..'
endforeach
cmake_include_dir = '${_IMPORT_PREFIX}/' + get_option('includedir')
endif
cmake_conf = configuration_data()
cmake_conf.set('PROJECT_NAME', meson.project_name())
cmake_conf.set('PROJECT_VERSION', meson.project_version())
cmake_conf.set('PROJECT_VERSION_MAJOR', meson.project_version().split('.')[0])
cmake_conf.set('NLOHMANN_JSON_TARGET_NAME', meson.project_name())
cmake_conf.set('NLOHMANN_JSON_TARGETS_EXPORT_NAME', meson.project_name() + 'Targets')
cmake_conf.set('NLOHMANN_JSON_INCLUDE_DIR', cmake_include_dir)
cmake_conf.set('NLOHMANN_JSON_CONFIG_TO_PREFIX', '/'.join(cmake_to_prefix))
cmake_conf.set('NLOHMANN_JSON_COMPILE_DEFINITIONS', ';'.join(json_defines))
foreach cmake_file : [
['cmake/config.cmake.in', 'nlohmann_jsonConfig.cmake'],
['cmake/nlohmann_jsonConfigVersion.cmake.in', 'nlohmann_jsonConfigVersion.cmake'],
['cmake/nlohmann_jsonTargets.cmake.in', 'nlohmann_jsonTargets.cmake'],
]
configure_file(
input: cmake_file[0],
output: cmake_file[1],
configuration: cmake_conf,
format: 'cmake@',
install_dir: cmake_install_dir,
)
endforeach
endif endif
-66
View File
@@ -1,66 +0,0 @@
option(
'MultipleHeaders',
type: 'boolean',
value: false,
description: 'Use non-amalgamated version of the library',
)
option(
'GlobalUDLs',
type: 'boolean',
value: true,
description: 'Place user-defined string literals in the global namespace',
)
option(
'ImplicitConversions',
type: 'boolean',
value: true,
description: 'Enable implicit conversions',
)
option(
'DisableEnumSerialization',
type: 'boolean',
value: false,
description: 'Disable default integer enum serialization',
)
option(
'DisableTupleReferenceConversion',
type: 'boolean',
value: false,
description: 'Disable conversion from a one-element tuple of a JSON reference',
)
option(
'Diagnostics',
type: 'boolean',
value: false,
description: 'Use extended diagnostic messages',
)
option(
'Diagnostic_Positions',
type: 'boolean',
value: false,
description: 'Enable diagnostic positions',
)
option(
'LegacyDiscardedValueComparison',
type: 'boolean',
value: false,
description: 'Enable legacy discarded value comparison',
)
option(
'StrictNulHandling',
type: 'boolean',
value: false,
description: 'Enable strict NUL-byte handling',
)
option(
'StrictBinaryUTF8',
type: 'boolean',
value: false,
description: 'Enable UTF-8 checks in the CBOR, UBJSON, BJData, and BSON writers',
)
option(
'DeleteDeprecatedFunctions',
type: 'boolean',
value: false,
description: 'Delete the deprecated functions instead of only deprecating them',
)
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -138,7 +138,8 @@ json_test_set_test_options(test-disabled_exceptions
# only the #972 regression test needs thirdparty/fifo_map on its include path # only the #972 regression test needs thirdparty/fifo_map on its include path
json_test_set_test_options(test-regression1 LINK_LIBRARIES fifo_map_include) json_test_set_test_options(test-regression1 LINK_LIBRARIES fifo_map_include)
# GCC's false -Warray-bounds error with JSON_DIAGNOSTICS only shows up when optimizing (#5742). # Regression test for GCC's false -Warray-bounds error with JSON_DIAGNOSTICS (#5742, fixed in #5585). It only
# showed up when optimizing, so build this test with -O3 and the warning as an error.
# -O3 makes the optimizer-driven warnings of the ci_test_gcc flag set (-Winline, # -O3 makes the optimizer-driven warnings of the ci_test_gcc flag set (-Winline,
# -Wsuggest-attribute=...) fire on the library's inline functions; they are not # -Wsuggest-attribute=...) fire on the library's inline functions; they are not
# what this test checks, so turn them off for it. # what this test checks, so turn them off for it.
+32 -3
View File
@@ -51,7 +51,22 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers. drivers.
*/ */
#include "fuzzer_common.hpp" #include <cassert>
#include <nlohmann/json.hpp>
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
// value-stable comparison for the round-trip checks below; see the note // value-stable comparison for the round-trip checks below; see the note
// above on why this compares dump()s rather than the json values directly // above on why this compares dump()s rather than the json values directly
@@ -65,9 +80,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{ {
std::vector<uint8_t> const vec1(data, data + size); std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions // step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
bool noexcept_threw = false; bool noexcept_threw = false;
json const j_noexcept = parse_without_exceptions([&] { return json::from_bjdata(vec1, true, false); }, noexcept_threw); try
{
j_noexcept = json::from_bjdata(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
// whether step 1 succeeded; if not, the catch blocks below check that // whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too // step 0 failed, too
bool parsed = false; bool parsed = false;
+32 -3
View File
@@ -25,8 +25,23 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers. drivers.
*/ */
#include <cassert>
#include <sstream> #include <sstream>
#include "fuzzer_common.hpp" #include <nlohmann/json.hpp>
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
namespace namespace
{ {
@@ -57,9 +72,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
std::vector<uint8_t> const vec1(data, data + size); std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions // step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
bool noexcept_threw = false; bool noexcept_threw = false;
json const j_noexcept = parse_without_exceptions([&] { return json::from_bon8(vec1, true, false); }, noexcept_threw); try
{
j_noexcept = json::from_bon8(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
// whether step 1 succeeded; if not, the catch blocks below check that // whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too // step 0 failed, too
bool parsed = false; bool parsed = false;
+32 -3
View File
@@ -21,16 +21,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers. drivers.
*/ */
#include "fuzzer_common.hpp" #include <cassert>
#include <nlohmann/json.hpp>
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
// see http://llvm.org/docs/LibFuzzer.html // see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{ {
std::vector<uint8_t> const vec1(data, data + size); std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions // step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
bool noexcept_threw = false; bool noexcept_threw = false;
json const j_noexcept = parse_without_exceptions([&] { return json::from_bson(vec1, true, false); }, noexcept_threw); try
{
j_noexcept = json::from_bson(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
// whether step 1 succeeded; if not, the catch blocks below check that // whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too // step 0 failed, too
bool parsed = false; bool parsed = false;
+32 -3
View File
@@ -21,16 +21,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers. drivers.
*/ */
#include "fuzzer_common.hpp" #include <cassert>
#include <nlohmann/json.hpp>
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
// see http://llvm.org/docs/LibFuzzer.html // see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{ {
std::vector<uint8_t> const vec1(data, data + size); std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions // step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
bool noexcept_threw = false; bool noexcept_threw = false;
json const j_noexcept = parse_without_exceptions([&] { return json::from_cbor(vec1, true, false); }, noexcept_threw); try
{
j_noexcept = json::from_cbor(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
// whether step 1 succeeded; if not, the catch blocks below check that // whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too // step 0 failed, too
bool parsed = false; bool parsed = false;
+32 -3
View File
@@ -22,14 +22,43 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers. drivers.
*/ */
#include "fuzzer_common.hpp" #include <cassert>
#include <nlohmann/json.hpp>
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
// see http://llvm.org/docs/LibFuzzer.html // see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{ {
// step 0: parse input without exceptions // step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
bool noexcept_threw = false; bool noexcept_threw = false;
json const j_noexcept = parse_without_exceptions([&] { return json::parse(data, data + size, nullptr, false); }, noexcept_threw); try
{
j_noexcept = json::parse(data, data + size, nullptr, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
// whether step 1 succeeded; if not, the catch blocks below check that // whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too // step 0 failed, too
bool parsed = false; bool parsed = false;
+32 -3
View File
@@ -21,16 +21,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers. drivers.
*/ */
#include "fuzzer_common.hpp" #include <cassert>
#include <nlohmann/json.hpp>
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
// see http://llvm.org/docs/LibFuzzer.html // see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{ {
std::vector<uint8_t> const vec1(data, data + size); std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions // step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
bool noexcept_threw = false; bool noexcept_threw = false;
json const j_noexcept = parse_without_exceptions([&] { return json::from_msgpack(vec1, true, false); }, noexcept_threw); try
{
j_noexcept = json::from_msgpack(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
// whether step 1 succeeded; if not, the catch blocks below check that // whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too // step 0 failed, too
bool parsed = false; bool parsed = false;
+32 -3
View File
@@ -30,16 +30,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers. drivers.
*/ */
#include "fuzzer_common.hpp" #include <cassert>
#include <nlohmann/json.hpp>
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
// see http://llvm.org/docs/LibFuzzer.html // see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{ {
std::vector<uint8_t> const vec1(data, data + size); std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions // step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
bool noexcept_threw = false; bool noexcept_threw = false;
json const j_noexcept = parse_without_exceptions([&] { return json::from_ubjson(vec1, true, false); }, noexcept_threw); try
{
j_noexcept = json::from_ubjson(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
// whether step 1 succeeded; if not, the catch blocks below check that // whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too // step 0 failed, too
bool parsed = false; bool parsed = false;
-51
View File
@@ -1,51 +0,0 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// code shared by the fuzzer drivers tests/src/fuzzer-parse_*.cpp
#pragma once
#include <cassert> // assert
#include <nlohmann/json.hpp>
using nlohmann::json; // NOLINT(google-global-names-in-headers): shared by all fuzzer drivers
// the round-trip checks of the drivers are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
inline bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
// step 0 of each driver: parse the input without exceptions; a parse error
// must then be reported as a discarded value, never thrown. Type and
// out-of-range errors are not parse errors and still throw; then @a threw is
// set and null is returned.
template<typename Parse>
json parse_without_exceptions(Parse parse, bool& threw)
{
threw = false;
try
{
return parse();
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
threw = true;
}
return {};
}
-185
View File
@@ -1,185 +0,0 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t (via json::binary_t)
#include <limits> // numeric_limits
#include <string> // string, to_string
#include <vector> // vector
#include <nlohmann/json.hpp>
// the loggers are defined in this header only, so their vtables are emitted in
// every test that includes it
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-vtables"
#endif
namespace utils
{
/// a SAX event consumer that records every event it receives as a
/// human-readable string, used by the deserialization tests to check the
/// exact sequence of SAX events a parse run produces
struct SaxEventLogger : public nlohmann::json_sax<nlohmann::json>
{
using json = nlohmann::json;
bool null() override
{
events.emplace_back("null()");
return true;
}
bool boolean(bool val) override
{
events.emplace_back(val ? "boolean(true)" : "boolean(false)");
return true;
}
bool number_integer(json::number_integer_t val) override
{
events.push_back("number_integer(" + std::to_string(val) + ")");
return true;
}
bool number_unsigned(json::number_unsigned_t val) override
{
events.push_back("number_unsigned(" + std::to_string(val) + ")");
return true;
}
bool number_float(json::number_float_t /*val*/, const std::string& s) override
{
events.push_back("number_float(" + s + ")");
return true;
}
bool string(std::string& val) override
{
events.push_back("string(" + val + ")");
return true;
}
bool binary(json::binary_t& val) override
{
std::string binary_contents = "binary(";
std::string comma_space;
for (const auto b : val)
{
binary_contents.append(comma_space);
binary_contents.append(std::to_string(static_cast<int>(b)));
comma_space = ", ";
}
binary_contents.append(")");
events.push_back(binary_contents);
return true;
}
bool start_object(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_object()");
}
else
{
events.push_back("start_object(" + std::to_string(elements) + ")");
}
return true;
}
bool key(std::string& val) override
{
events.push_back("key(" + val + ")");
return true;
}
bool end_object() override
{
events.emplace_back("end_object()");
return true;
}
bool start_array(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_array()");
}
else
{
events.push_back("start_array(" + std::to_string(elements) + ")");
}
return true;
}
bool end_array() override
{
events.emplace_back("end_array()");
return true;
}
bool parse_error(std::size_t position, const std::string& /*last_token*/, const json::exception& /*ex*/) override
{
errored = true;
events.push_back("parse_error(" + std::to_string(position) + ")");
return false;
}
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
bool errored = false;
};
struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger
{
bool start_object(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_object()");
}
else
{
events.push_back("start_object(" + std::to_string(elements) + ")");
}
return false;
}
};
struct SaxEventLoggerExitAfterKey : public SaxEventLogger
{
bool key(std::string& val) override
{
events.push_back("key(" + val + ")");
return false;
}
};
struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger
{
bool start_array(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_array()");
}
else
{
events.push_back("start_array(" + std::to_string(elements) + ")");
}
return false;
}
};
} // namespace utils
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
+48 -6
View File
@@ -370,7 +370,14 @@ TEST_CASE("copy of a deeply nested value survives a failing allocation (#5640)")
#if !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0) #if !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
SECTION("std::map-backed object_t") SECTION("std::map-backed object_t")
{ {
using bad_alloc_json = nlohmann::json::with_allocator_t<nth_alloc_fails_allocator>; using bad_alloc_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
nth_alloc_fails_allocator>;
check_deep_copy_survives_failing_allocation<bad_alloc_json>(false); check_deep_copy_survives_failing_allocation<bad_alloc_json>(false);
check_deep_copy_survives_failing_allocation<bad_alloc_json>(true); check_deep_copy_survives_failing_allocation<bad_alloc_json>(true);
@@ -378,7 +385,14 @@ TEST_CASE("copy of a deeply nested value survives a failing allocation (#5640)")
SECTION("ordered_map-backed object_t") SECTION("ordered_map-backed object_t")
{ {
using bad_alloc_ordered_json = nlohmann::ordered_json::with_allocator_t<nth_alloc_fails_allocator>; using bad_alloc_ordered_json = nlohmann::basic_json<nlohmann::ordered_map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
nth_alloc_fails_allocator>;
check_deep_copy_survives_failing_allocation<bad_alloc_ordered_json>(false); check_deep_copy_survives_failing_allocation<bad_alloc_ordered_json>(false);
check_deep_copy_survives_failing_allocation<bad_alloc_ordered_json>(true); check_deep_copy_survives_failing_allocation<bad_alloc_ordered_json>(true);
@@ -436,7 +450,14 @@ struct scratch_counting_allocator : std::allocator<T>
TEST_CASE("deep copy uses the provided allocator") TEST_CASE("deep copy uses the provided allocator")
{ {
using counting_json = nlohmann::json::with_allocator_t<scratch_counting_allocator>; using counting_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
scratch_counting_allocator>;
// deeper than the 128 levels the copy constructor descends into, so the // deeper than the 128 levels the copy constructor descends into, so the
// innermost objects are copied by the iterative deep copy // innermost objects are copied by the iterative deep copy
@@ -495,7 +516,14 @@ TEST_CASE("converting a deeply nested value from another specialization fails cl
// the allocator in noexcept constructors, so a failing construction crashes // the allocator in noexcept constructors, so a failing construction crashes
// the program there instead of throwing std::bad_alloc. Nothing to check. // the program there instead of throwing std::bad_alloc. Nothing to check.
#if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0) #if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
using countdown_json = nlohmann::json::with_allocator_t<countdown_allocator>; using countdown_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
countdown_allocator>;
// deeper than the 128 levels the converting constructor descends into, so // deeper than the 128 levels the converting constructor descends into, so
// that failures land on both sides of the bound - or, built with // that failures land on both sides of the bound - or, built with
@@ -603,7 +631,14 @@ TEST_CASE("destructor performs no allocation, only deallocation")
// Since that stack could itself throw bad_alloc from inside the // Since that stack could itself throw bad_alloc from inside the
// noexcept destructor (#5135), destroy() no longer allocates anything: // noexcept destructor (#5135), destroy() no longer allocates anything:
// it only ever frees what is already there. // it only ever frees what is already there.
using counting_json = nlohmann::json::with_allocator_t<counting_allocator>; using counting_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
counting_allocator>;
SECTION("array") SECTION("array")
{ {
@@ -648,7 +683,14 @@ TEST_CASE("destructor performs no allocation, only deallocation")
TEST_CASE("a failed allocation leaves the value unchanged") TEST_CASE("a failed allocation leaves the value unchanged")
{ {
// create JSON type using the throwing allocator // create JSON type using the throwing allocator
using my_json = nlohmann::json::with_allocator_t<my_allocator>; using my_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
my_allocator>;
// Each of these creates a string, array, object, or binary value. The // Each of these creates a string, array, object, or binary value. The
// value must be created before the type is changed: otherwise, a failed // value must be created before the type is changed: otherwise, a failed
+1 -1
View File
@@ -237,7 +237,7 @@ namespace
// the binary formats as function pointers for "Binary formats with narrow number types"; // the binary formats as function pointers for "Binary formats with narrow number types";
// named functions rather than lambdas, because clang 3.5 cannot convert a lambda // named functions rather than lambdas, because clang 3.5 cannot convert a lambda
// to a function pointer in the braced initializer of the format table // to a function pointer in the braced initializer of the format table
using narrow_json = nlohmann::json::with_integers_t<std::int32_t, std::uint32_t>::with_float_t<float>; using narrow_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint32_t, float>;
using bytes = std::vector<std::uint8_t>; using bytes = std::vector<std::uint8_t>;
bytes encode_cbor(const json& j) bytes encode_cbor(const json& j)
+3 -34
View File
@@ -17,7 +17,6 @@ using nlohmann::json;
#include <cstdint> // uint32_t, uint64_t #include <cstdint> // uint32_t, uint64_t
#include <cstdlib> // strtod #include <cstdlib> // strtod
#include <cstring> // memcpy #include <cstring> // memcpy
#include <limits> // numeric_limits
#include <sstream> // stringstream #include <sstream> // stringstream
#include <string> // string #include <string> // string
#include <utility> // pair #include <utility> // pair
@@ -892,39 +891,8 @@ TEST_CASE("Eisel-Lemire float conversion")
SECTION("128-bit products and leading zeros") SECTION("128-bit products and leading zeros")
{ {
const auto check_product = [](std::uint64_t a, std::uint64_t b)
{
const auto product = nlohmann::detail::full_multiplication(a, b);
CHECK(big_from(product.high, product.low) == big_mul(big_from(0, a), big_from(0, b)));
};
const std::uint64_t max = (std::numeric_limits<std::uint64_t>::max)();
const std::array<std::pair<std::uint64_t, std::uint64_t>, 13> edge_cases =
{
{
{0, 0},
{0, 1},
{1, 1},
{1, max},
{0xFFFFFFFFu, 0x100000000u},
{0x100000000u, 0x100000000u},
{0x100000001u, 0x100000001u},
{max, max},
{max, 2},
{0xFFFFFFFF00000000u, 0x100000001u},
{0x100000001u, 0xFFFFFFFF00000000u},
{max, 1},
{2, max},
}
};
for (const auto& test : edge_cases)
{
check_product(test.first, test.second);
}
// whichever implementation the compiler gets (with or without a // whichever implementation the compiler gets (with or without a
// 128-bit integer type or a builtin / intrinsic) // 128-bit integer type or a builtin)
std::uint64_t state = 42; std::uint64_t state = 42;
for (int i = 0; i < 10000; ++i) for (int i = 0; i < 10000; ++i)
{ {
@@ -933,7 +901,8 @@ TEST_CASE("Eisel-Lemire float conversion")
state ^= state << 17u; state ^= state << 17u;
const std::uint64_t a = state; const std::uint64_t a = state;
const std::uint64_t b = (state * 0x9E3779B97F4A7C15u) >> (i % 64); const std::uint64_t b = (state * 0x9E3779B97F4A7C15u) >> (i % 64);
check_product(a, b); const auto product = nlohmann::detail::full_multiplication(a, b);
CHECK(big_from(product.high, product.low) == big_mul(big_from(0, a), big_from(0, b)));
const int k = i % 64; const int k = i % 64;
const std::uint64_t x = (std::uint64_t{1} << k) | (a & ((std::uint64_t{1} << k) - 1)); const std::uint64_t x = (std::uint64_t{1} << k) | (a & ((std::uint64_t{1} << k) - 1));
+184 -5
View File
@@ -37,15 +37,194 @@ using nlohmann::json;
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "sax_countdown.hpp"
#include "sax_event_loggers.hpp"
#include "test_utils.hpp" #include "test_utils.hpp"
using utils::SaxCountdown;
using utils::SaxEventLogger;
namespace namespace
{ {
class SaxEventLogger
{
public:
bool null()
{
events.emplace_back("null()");
return true;
}
bool boolean(bool val)
{
events.emplace_back(val ? "boolean(true)" : "boolean(false)");
return true;
}
bool number_integer(json::number_integer_t val)
{
events.push_back("number_integer(" + std::to_string(val) + ")");
return true;
}
bool number_unsigned(json::number_unsigned_t val)
{
events.push_back("number_unsigned(" + std::to_string(val) + ")");
return true;
}
bool number_float(json::number_float_t /*unused*/, const std::string& s)
{
events.push_back("number_float(" + s + ")");
return true;
}
bool string(std::string& val)
{
events.push_back("string(" + val + ")");
return true;
}
bool binary(json::binary_t& val)
{
std::string binary_contents = "binary(";
std::string comma_space;
for (auto b : val)
{
binary_contents.append(comma_space);
binary_contents.append(std::to_string(static_cast<int>(b)));
comma_space = ", ";
}
binary_contents.append(")");
events.push_back(binary_contents);
return true;
}
bool start_object(std::size_t elements)
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_object()");
}
else
{
events.push_back("start_object(" + std::to_string(elements) + ")");
}
return true;
}
bool key(std::string& val)
{
events.push_back("key(" + val + ")");
return true;
}
bool end_object()
{
events.emplace_back("end_object()");
return true;
}
bool start_array(std::size_t elements)
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_array()");
}
else
{
events.push_back("start_array(" + std::to_string(elements) + ")");
}
return true;
}
bool end_array()
{
events.emplace_back("end_array()");
return true;
}
bool parse_error(std::size_t position, const std::string& /*unused*/, const json::exception& /*unused*/)
{
errored = true;
events.push_back("parse_error(" + std::to_string(position) + ")");
return false;
}
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
bool errored = false;
};
class SaxCountdown : public nlohmann::json::json_sax_t
{
public:
explicit SaxCountdown(const int count) : events_left(count)
{}
bool null() override
{
return events_left-- > 0;
}
bool boolean(bool /*val*/) override
{
return events_left-- > 0;
}
bool number_integer(json::number_integer_t /*val*/) override
{
return events_left-- > 0;
}
bool number_unsigned(json::number_unsigned_t /*val*/) override
{
return events_left-- > 0;
}
bool number_float(json::number_float_t /*val*/, const std::string& /*s*/) override
{
return events_left-- > 0;
}
bool string(std::string& /*val*/) override
{
return events_left-- > 0;
}
bool binary(json::binary_t& /*val*/) override
{
return events_left-- > 0;
}
bool start_object(std::size_t /*elements*/) override
{
return events_left-- > 0;
}
bool key(std::string& /*val*/) override
{
return events_left-- > 0;
}
bool end_object() override
{
return events_left-- > 0;
}
bool start_array(std::size_t /*elements*/) override
{
return events_left-- > 0;
}
bool end_array() override
{
return events_left-- > 0;
}
bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& /*ex*/) override
{
return false;
}
private:
int events_left = 0;
};
json parser_helper(const std::string& s); json parser_helper(const std::string& s);
bool accept_helper(const std::string& s); bool accept_helper(const std::string& s);
void comments_helper(const std::string& s); void comments_helper(const std::string& s);
+3 -3
View File
@@ -824,7 +824,7 @@ struct unordered_object_t : std::map<Key, Value, directed_less<Key>, Allocator>
return !(lhs == rhs); return !(lhs == rhs);
} }
}; };
using unordered_json = nlohmann::json::with_object_t<unordered_object_t>; using unordered_json = nlohmann::basic_json<unordered_object_t>;
// the entries "0" to "9", enumerated in ascending or in descending order // the entries "0" to "9", enumerated in ascending or in descending order
unordered_json make_unordered_object(const bool descending) unordered_json make_unordered_object(const bool descending)
@@ -875,7 +875,7 @@ struct key_case_less
template<class Key, class Value, class /*Compare*/, class Allocator> template<class Key, class Value, class /*Compare*/, class Allocator>
using key_case_map = std::map<Key, Value, key_case_less, Allocator>; using key_case_map = std::map<Key, Value, key_case_less, Allocator>;
using key_case_json = nlohmann::json::with_object_t<key_case_map>; using key_case_json = nlohmann::basic_json<key_case_map>;
// the innermost value of a chain of single-element arrays // the innermost value of a chain of single-element arrays
template<typename Json> template<typename Json>
@@ -905,7 +905,7 @@ struct case_insensitive_less
template<class Key, class Value, class /*Compare*/, class Allocator> template<class Key, class Value, class /*Compare*/, class Allocator>
using case_insensitive_map = std::map<Key, Value, case_insensitive_less, Allocator>; using case_insensitive_map = std::map<Key, Value, case_insensitive_less, Allocator>;
using ci_json = nlohmann::json::with_object_t<case_insensitive_map>; using ci_json = nlohmann::basic_json<case_insensitive_map>;
} // namespace } // namespace
TEST_CASE("equality of objects whose entries have no fixed order") TEST_CASE("equality of objects whose entries have no fixed order")
+2 -2
View File
@@ -22,7 +22,7 @@ namespace
// std::deque has no capacity() member function, which the library only needs // std::deque has no capacity() member function, which the library only needs
// to detect a reallocation for JSON_DIAGNOSTICS // to detect a reallocation for JSON_DIAGNOSTICS
using deque_json = nlohmann::json::with_array_t<std::deque>; using deque_json = nlohmann::basic_json<std::map, std::deque>;
// a std::vector whose at() is hidden: the library performs its own bounds // a std::vector whose at() is hidden: the library performs its own bounds
// check and must not fall back to the container's checked accessor // check and must not fall back to the container's checked accessor
@@ -39,7 +39,7 @@ class vector_without_at : public std::vector<T, Allocator>
void at() = delete; void at() = delete;
}; };
using no_at_json = nlohmann::json::with_array_t<vector_without_at>; using no_at_json = nlohmann::basic_json<std::map, vector_without_at>;
} // namespace } // namespace
+3 -3
View File
@@ -179,7 +179,7 @@ class no_key_compare_map
} }
}; };
using no_key_compare_json = nlohmann::json::with_object_t<no_key_compare_map>; using no_key_compare_json = nlohmann::basic_json<no_key_compare_map>;
// An ObjectType whose erase(iterator) returns void rather than the following // An ObjectType whose erase(iterator) returns void rather than the following
// iterator, as for instance Abseil's hash maps do // iterator, as for instance Abseil's hash maps do
@@ -196,7 +196,7 @@ struct void_erase_map : std::map<Key, T, Compare, Allocator>
} }
}; };
using void_erase_json = nlohmann::json::with_object_t<void_erase_map>; using void_erase_json = nlohmann::basic_json<void_erase_map>;
// wraps an iterator, but only offers the LegacyForwardIterator operations, // wraps an iterator, but only offers the LegacyForwardIterator operations,
// like the iterators of std::unordered_map and other hash maps // like the iterators of std::unordered_map and other hash maps
@@ -388,7 +388,7 @@ class forward_only_map
} }
}; };
using forward_only_json = nlohmann::json::with_object_t<forward_only_map>; using forward_only_json = nlohmann::basic_json<forward_only_map>;
} // namespace } // namespace
+147 -7
View File
@@ -36,15 +36,155 @@ using nlohmann::json;
#include <string> #include <string>
#include <valarray> #include <valarray>
#include "sax_event_loggers.hpp"
using utils::SaxEventLogger;
using utils::SaxEventLoggerExitAfterKey;
using utils::SaxEventLoggerExitAfterStartArray;
using utils::SaxEventLoggerExitAfterStartObject;
namespace namespace
{ {
struct SaxEventLogger : public nlohmann::json_sax<json>
{
bool null() override
{
events.emplace_back("null()");
return true;
}
bool boolean(bool val) override
{
events.emplace_back(val ? "boolean(true)" : "boolean(false)");
return true;
}
bool number_integer(json::number_integer_t val) override
{
events.push_back("number_integer(" + std::to_string(val) + ")");
return true;
}
bool number_unsigned(json::number_unsigned_t val) override
{
events.push_back("number_unsigned(" + std::to_string(val) + ")");
return true;
}
bool number_float(json::number_float_t /*val*/, const std::string& s) override
{
events.push_back("number_float(" + s + ")");
return true;
}
bool string(std::string& val) override
{
events.push_back("string(" + val + ")");
return true;
}
bool binary(json::binary_t& val) override
{
std::string binary_contents = "binary(";
std::string comma_space;
for (auto b : val)
{
binary_contents.append(comma_space);
binary_contents.append(std::to_string(static_cast<int>(b)));
comma_space = ", ";
}
binary_contents.append(")");
events.push_back(binary_contents);
return true;
}
bool start_object(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_object()");
}
else
{
events.push_back("start_object(" + std::to_string(elements) + ")");
}
return true;
}
bool key(std::string& val) override
{
events.push_back("key(" + val + ")");
return true;
}
bool end_object() override
{
events.emplace_back("end_object()");
return true;
}
bool start_array(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_array()");
}
else
{
events.push_back("start_array(" + std::to_string(elements) + ")");
}
return true;
}
bool end_array() override
{
events.emplace_back("end_array()");
return true;
}
bool parse_error(std::size_t position, const std::string& /*last_token*/, const json::exception& /*ex*/) override
{
events.push_back("parse_error(" + std::to_string(position) + ")");
return false;
}
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
};
struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger
{
bool start_object(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_object()");
}
else
{
events.push_back("start_object(" + std::to_string(elements) + ")");
}
return false;
}
};
struct SaxEventLoggerExitAfterKey : public SaxEventLogger
{
bool key(std::string& val) override
{
events.push_back("key(" + val + ")");
return false;
}
};
struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger
{
bool start_array(std::size_t elements) override
{
if (elements == (std::numeric_limits<std::size_t>::max)())
{
events.emplace_back("start_array()");
}
else
{
events.push_back("start_array(" + std::to_string(elements) + ")");
}
return false;
}
};
template <typename T> template <typename T>
class proxy_iterator class proxy_iterator
{ {
+3 -3
View File
@@ -157,13 +157,13 @@ TEST_CASE("hash<nlohmann::json>")
// the ends of the integer ranges, which equal floats exactly // the ends of the integer ranges, which equal floats exactly
const auto int_min = (std::numeric_limits<json::number_integer_t>::min)(); const auto int_min = (std::numeric_limits<json::number_integer_t>::min)();
const auto int_max = (std::numeric_limits<json::number_integer_t>::max)(); const auto int_max = (std::numeric_limits<json::number_integer_t>::max)();
const auto two_63 = static_cast<json::number_unsigned_t>(1) << 63U; const auto two_63 = json::number_unsigned_t(1) << 63U;
CHECK(json(int_min) == json(-9223372036854775808.0)); CHECK(json(int_min) == json(-9223372036854775808.0));
CHECK(std::hash<json> {}(json(int_min)) == std::hash<json> {}(json(-9223372036854775808.0))); CHECK(std::hash<json> {}(json(int_min)) == std::hash<json> {}(json(-9223372036854775808.0)));
CHECK(json(two_63) == json(9223372036854775808.0)); CHECK(json(two_63) == json(9223372036854775808.0));
CHECK(std::hash<json> {}(json(two_63)) == std::hash<json> {}(json(9223372036854775808.0))); CHECK(std::hash<json> {}(json(two_63)) == std::hash<json> {}(json(9223372036854775808.0)));
CHECK(json(static_cast<json::number_unsigned_t>(int_max)) == json(int_max)); CHECK(json(json::number_unsigned_t(int_max)) == json(int_max));
CHECK(std::hash<json> {}(json(static_cast<json::number_unsigned_t>(int_max))) == std::hash<json> {}(json(int_max))); CHECK(std::hash<json> {}(json(json::number_unsigned_t(int_max))) == std::hash<json> {}(json(int_max)));
} }
TEST_CASE("hash<nlohmann::ordered_json>") TEST_CASE("hash<nlohmann::ordered_json>")
+6 -18
View File
@@ -426,7 +426,7 @@ TEST_CASE("issue #5392 - binary writers on deeply nested values")
{ {
for (std::size_t depth = 120; depth <= 140; ++depth) for (std::size_t depth = 120; depth <= 140; ++depth)
{ {
CAPTURE(depth) CAPTURE(depth);
const json array = nested_array(depth, json(7)); const json array = nested_array(depth, json(7));
CHECK(json::from_cbor(json::to_cbor(array)) == array); CHECK(json::from_cbor(json::to_cbor(array)) == array);
@@ -464,7 +464,7 @@ TEST_CASE("issue #5392 - binary writers on deeply nested values")
nlohmann::detail::recursion_depth_limit() + 1, nlohmann::detail::recursion_depth_limit() + 2 nlohmann::detail::recursion_depth_limit() + 1, nlohmann::detail::recursion_depth_limit() + 2
}) })
{ {
CAPTURE(depth) CAPTURE(depth);
const json array = nested_array(depth, json(0)); const json array = nested_array(depth, json(0));
std::vector<std::uint8_t> expected_cbor(depth, 0x81); std::vector<std::uint8_t> expected_cbor(depth, 0x81);
@@ -505,22 +505,10 @@ TEST_CASE("issue #5392 - binary writers on deeply nested values")
const json discarded_leaf(json::value_t::discarded); const json discarded_leaf(json::value_t::discarded);
const json deep_discarded = nested_array(depth, discarded_leaf); const json deep_discarded = nested_array(depth, discarded_leaf);
// with diagnostics, the message names the path to the discarded leaf CHECK_THROWS_WITH_AS(json::to_cbor(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to CBOR", json::type_error);
#if JSON_DIAGNOSTICS CHECK_THROWS_WITH_AS(json::to_msgpack(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to MessagePack", json::type_error);
std::string path; CHECK_THROWS_WITH_AS(json::to_ubjson(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error);
for (std::size_t i = 0; i < depth; ++i) CHECK_THROWS_WITH_AS(json::to_bjdata(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to BJData", json::type_error);
{
path += "/0";
}
const std::string prefix = "[json.exception.type_error.321] (" + path + ") ";
#else
const std::string prefix = "[json.exception.type_error.321] ";
#endif
CHECK_THROWS_WITH_AS(json::to_cbor(deep_discarded), (prefix + "cannot serialize discarded value to CBOR").c_str(), json::type_error);
CHECK_THROWS_WITH_AS(json::to_msgpack(deep_discarded), (prefix + "cannot serialize discarded value to MessagePack").c_str(), json::type_error);
CHECK_THROWS_WITH_AS(json::to_ubjson(deep_discarded), (prefix + "cannot serialize discarded value to UBJSON").c_str(), json::type_error);
CHECK_THROWS_WITH_AS(json::to_bjdata(deep_discarded), (prefix + "cannot serialize discarded value to BJData").c_str(), json::type_error);
} }
SECTION("does not overflow the C++ stack") SECTION("does not overflow the C++ stack")
+3 -3
View File
@@ -172,7 +172,7 @@ TEST_CASE("locale-dependent test (LC_NUMERIC=de_DE)")
// a floating-point type that is not a float or a double is written // a floating-point type that is not a float or a double is written
// with snprintf, whose locale-specific decimal point and thousands // with snprintf, whose locale-specific decimal point and thousands
// separator are undone afterwards // separator are undone afterwards
using long_double_json = nlohmann::json::with_float_t<long double>; using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
CHECK(long_double_json(12345.5L).dump() == "12345.5"); CHECK(long_double_json(12345.5L).dump() == "12345.5");
CHECK(long_double_json(1.0L).dump() == "1.0"); CHECK(long_double_json(1.0L).dump() == "1.0");
CHECK(long_double_json(-0.25L).dump() == "-0.25"); CHECK(long_double_json(-0.25L).dump() == "-0.25");
@@ -272,7 +272,7 @@ TEST_CASE("locale changes between lexer construction and number conversion (#519
} }
text += "]"; text += "]";
using long_double_json = nlohmann::json::with_float_t<long double>; using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
// reference values, parsed without a locale switch // reference values, parsed without a locale switch
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr); REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
@@ -432,7 +432,7 @@ TEST_CASE("locale changes during a single dump() (#5709 item 3)")
// long double on 64-bit Arm, where it is IEEE-754 double) takes the // long double on 64-bit Arm, where it is IEEE-754 double) takes the
// locale-independent to_chars() path instead, and this test is a no-op // locale-independent to_chars() path instead, and this test is a no-op
// there. // there.
using long_double_json = nlohmann::json::with_float_t<long double>; using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
using ld_limits = std::numeric_limits<long_double_json::number_float_t>; using ld_limits = std::numeric_limits<long_double_json::number_float_t>;
const bool is_ieee_single_or_double = const bool is_ieee_single_or_double =
(ld_limits::is_iec559 && ld_limits::digits == 24 && ld_limits::max_exponent == 128) || (ld_limits::is_iec559 && ld_limits::digits == 24 && ld_limits::max_exponent == 128) ||
+2 -2
View File
@@ -2432,8 +2432,8 @@ TEST_CASE("MessagePack numbers use the active union member (see #5644)")
// used to read the union member that was not the active one, writing // used to read the union member that was not the active one, writing
// wrong bytes for some values; std::int64_t/std::uint64_t (the default // wrong bytes for some values; std::int64_t/std::uint64_t (the default
// types, where both members have the same width) were not affected // types, where both members have the same width) were not affected
using int32_json = nlohmann::json::with_integers_t<std::int32_t, std::uint64_t>; using int32_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint64_t, double>;
using int16_json = nlohmann::json::with_integers_t<std::int16_t, std::uint64_t>; using int16_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int16_t, std::uint64_t, double>;
SECTION("number_integer_t = std::int32_t") SECTION("number_integer_t = std::int32_t")
{ {
+8 -6
View File
@@ -39,7 +39,7 @@ using nlohmann::json;
template<class K, class V, class dummy_compare, class A> template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>; using my_workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
using my_json = nlohmann::json::with_object_t<my_workaround_fifo_map>; using my_json = nlohmann::basic_json<my_workaround_fifo_map>;
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// for #977 // for #977
@@ -86,7 +86,8 @@ struct foo_serializer < T, typename std::enable_if < !std::is_same<foo, T>::valu
}; };
} // namespace ns } // namespace ns
using foo_json = nlohmann::json::with_json_serializer_t<ns::foo_serializer>; using foo_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t,
std::uint64_t, double, std::allocator, ns::foo_serializer, std::vector<std::uint8_t>>;
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// for #805 // for #805
@@ -253,7 +254,7 @@ TEST_CASE("regression tests 1")
{ {
// create JSON class with nonstandard integer number type // create JSON class with nonstandard integer number type
using custom_json = using custom_json =
nlohmann::json::with_integers_t<std::int32_t, std::uint32_t>::with_float_t<float>; nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float>;
custom_json j; custom_json j;
j["int_1"] = 1; j["int_1"] = 1;
CHECK(j["int_1"] == 1); CHECK(j["int_1"] == 1);
@@ -469,17 +470,18 @@ TEST_CASE("regression tests 1")
// create JSON class with nonstandard float number type // create JSON class with nonstandard float number type
// float // float
nlohmann::json::with_integers_t<std::int32_t, std::uint32_t>::with_float_t<float> const j_float = nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float> const j_float =
1.23e25f; 1.23e25f;
CHECK(j_float.get<float>() == 1.23e25f); CHECK(j_float.get<float>() == 1.23e25f);
// double // double
nlohmann::json const j_double = nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, double> const j_double =
1.23e35; 1.23e35;
CHECK(j_double.get<double>() == 1.23e35); CHECK(j_double.get<double>() == 1.23e35);
// long double // long double
nlohmann::json::with_float_t<long double> const j_long_double = 1.23e45L; nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, long double>
const j_long_double = 1.23e45L;
CHECK(j_long_double.get<long double>() == 1.23e45L); CHECK(j_long_double.get<long double>() == 1.23e45L);
} }
+17 -4
View File
@@ -64,7 +64,18 @@ using ordered_json = nlohmann::ordered_json;
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// for #4804 // for #4804
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
using json_4804 = nlohmann::json::with_binary_t<std::vector<std::byte>>; using json_4804 = nlohmann::basic_json<std::map, // ObjectType
std::vector, // ArrayType
std::string, // StringType
bool, // BooleanType
std::int64_t, // NumberIntegerType
std::uint64_t, // NumberUnsignedType
double, // NumberFloatType
std::allocator, // AllocatorType
nlohmann::adl_serializer, // JSONSerializer
std::vector<std::byte>, // BinaryType
void // CustomBaseClass
>;
#endif #endif
#ifdef JSON_HAS_CPP_20 #ifdef JSON_HAS_CPP_20
@@ -96,7 +107,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
// for #1021 // for #1021
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
using float_json = nlohmann::json::with_float_t<float>; using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
namespace namespace
@@ -144,8 +155,10 @@ struct failing_allocator : std::allocator<T>
}; };
}; };
using failing_json = nlohmann::json::with_allocator_t<failing_allocator>; using failing_json = nlohmann::basic_json<std::map, std::vector, std::string, bool,
using failing_ordered_json = nlohmann::ordered_json::with_allocator_t<failing_allocator>; std::int64_t, std::uint64_t, double, failing_allocator>;
using failing_ordered_json = nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, failing_allocator>;
// builds `depth` levels of nesting around a scalar, iteratively (never // builds `depth` levels of nesting around a scalar, iteratively (never
// recursing: each wrap only moves the previous, already-built value, which // recursing: each wrap only moves the previous, already-built value, which
+13 -2
View File
@@ -62,7 +62,18 @@ using ordered_json = nlohmann::ordered_json;
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// for #4804 // for #4804
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
using json_4804 = nlohmann::json::with_binary_t<std::vector<std::byte>>; using json_4804 = nlohmann::basic_json<std::map, // ObjectType
std::vector, // ArrayType
std::string, // StringType
bool, // BooleanType
std::int64_t, // NumberIntegerType
std::uint64_t, // NumberUnsignedType
double, // NumberFloatType
std::allocator, // AllocatorType
nlohmann::adl_serializer, // JSONSerializer
std::vector<std::byte>, // BinaryType
void // CustomBaseClass
>;
#endif #endif
#ifdef JSON_HAS_CPP_20 #ifdef JSON_HAS_CPP_20
@@ -919,7 +930,7 @@ TEST_CASE("regression test #5476 - array type without reserve()")
{ {
// the capacity reserved for definite-length arrays must not require the // the capacity reserved for definite-length arrays must not require the
// array type to have a reserve() member function // array type to have a reserve() member function
using deque_json = nlohmann::json::with_array_t<std::deque>; using deque_json = nlohmann::basic_json<std::map, std::deque>;
SECTION("std::deque") SECTION("std::deque")
{ {
+2 -1
View File
@@ -367,7 +367,8 @@ TEST_CASE("dump for basic_json with long double number_float_t")
// serializer::dump_float(x, std::false_type). That branch must use the // serializer::dump_float(x, std::false_type). That branch must use the
// "%.*Lg" format specifier; using "%.*g" with a long double argument is // "%.*Lg" format specifier; using "%.*g" with a long double argument is
// undefined behavior and corrupts the output. // undefined behavior and corrupts the output.
using long_double_json = nlohmann::json::with_float_t<long double>; using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string,
bool, std::int64_t, std::uint64_t, long double>;
SECTION("round-trip dump/parse") SECTION("round-trip dump/parse")
{ {
-21
View File
@@ -1,21 +0,0 @@
# check_build_options
Checks that the Meson build and the pkg-config files offer the same options as the CMake target, so that a new CMake
option is not forgotten in one of them.
The compile definitions of the CMake target (`target_compile_definitions` in [`CMakeLists.txt`](../../CMakeLists.txt))
are the reference. When you add an option there, also add it to
- the pkg-config block in `CMakeLists.txt` (`NLOHMANN_JSON_PKGCONFIG_CFLAGS`),
- [`meson_options.txt`](../../meson_options.txt), named without the `JSON_` prefix and with the same default,
- [`meson.build`](../../meson.build) (`json_defines`), and
- the list of Meson options in
[`docs/mkdocs/docs/integration/package_managers.md`](../../docs/mkdocs/docs/integration/package_managers.md).
Run the check with
```shell
make check_build_options
```
It needs only Python 3 and runs in the `ci_meson_install` CI job.
@@ -1,144 +0,0 @@
#!/usr/bin/env python3
"""Check that the Meson build and the pkg-config files offer the CMake options.
The compile definitions of the CMake target (target_compile_definitions in
CMakeLists.txt) are the reference. For every option used there, the script
checks that
- the CMake pkg-config file adds the same definition under the same condition,
- meson_options.txt has a boolean option of the same name without the "JSON_"
prefix and with the same default,
- meson.build adds the same definition under the same condition, and
- the Meson section of the package manager documentation lists the option.
Meson's MultipleHeaders option selects the include directory and adds no
definition; it is the only Meson option without a definition.
"""
import argparse
import os
import re
import sys
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
DOCS = os.path.join('docs', 'mkdocs', 'docs', 'integration', 'package_managers.md')
# Meson options that add no compile definition, with their default
MESON_ONLY = {'MultipleHeaders': 'false'}
def read(root, path):
with open(os.path.join(root, path), encoding='utf-8') as f:
return f.read()
def cmake_target_definitions(cmake):
"""Return {option: (definition, add_if_on)} from target_compile_definitions."""
block = re.search(r'target_compile_definitions\(\s*\$\{NLOHMANN_JSON_TARGET_NAME\}\s*INTERFACE(.*?)\n\)', cmake, re.S)
if not block:
sys.exit('CMakeLists.txt: target_compile_definitions of the target not found')
result = {}
for line in block.group(1).split('\n'):
line = line.strip()
if not line:
continue
m = re.fullmatch(r'\$<\$<NOT:\$<BOOL:\$\{JSON_(\w+)\}>>:(\w+=\w+)>', line)
if m:
result[m.group(1)] = (m.group(2), False)
continue
m = re.fullmatch(r'\$<\$<BOOL:\$\{JSON_(\w+)\}>:(\w+=\w+)>', line)
if m:
result[m.group(1)] = (m.group(2), True)
continue
sys.exit(f'CMakeLists.txt: unexpected line in target_compile_definitions: {line}')
return result
def cmake_defaults(cmake):
"""Return {option: 'true'/'false'} for the option() calls of the form JSON_<name>."""
return {m.group(1): 'true' if m.group(2) == 'ON' else 'false'
for m in re.finditer(r'^option\(JSON_(\w+)\s+"[^"]*"\s+(ON|OFF)\)', cmake, re.M)}
def cmake_pkgconfig_definitions(cmake):
"""Return {option: (definition, add_if_on)} from the pkg-config block."""
return {m.group(2): (m.group(3), m.group(1) is None)
for m in re.finditer(r'if \((NOT )?JSON_(\w+)\)\s*\n\s*string\(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -D(\w+=\w+)"\)', cmake)}
def meson_options(options):
"""Return {option: default} for the boolean options in meson_options.txt."""
result = {}
for block in re.findall(r'option\((.*?)\)', options, re.S):
name = re.search(r"'(\w+)'", block).group(1)
kind = re.search(r"type\s*:\s*'(\w+)'", block)
value = re.search(r'value\s*:\s*(\w+)', block)
result[name] = value.group(1) if kind and kind.group(1) == 'boolean' and value else None
return result
def meson_definitions(meson):
"""Return {option: (definition, add_if_on)} from meson.build."""
return {m.group(2): (m.group(3), m.group(1) is None)
for m in re.finditer(r"if (not )?get_option\('(\w+)'\)\s*\n\s*json_defines \+= '(\w+=\w+)'", meson)}
def describe(definition):
name, add_if_on = definition
return f'{name} if {"enabled" if add_if_on else "disabled"}'
def compare(errors, where, expected, actual):
for option, definition in expected.items():
if option not in actual:
errors.append(f'{where}: no definition for option {option} (expected {describe(definition)})')
elif actual[option] != definition:
errors.append(f'{where}: option {option} adds {describe(actual[option])}, expected {describe(definition)}')
for option in actual.keys() - expected.keys():
errors.append(f'{where}: definition for option {option}, which the CMake target does not have')
def main():
parser = argparse.ArgumentParser(description=__doc__.split('\n')[0])
parser.add_argument('root', nargs='?', default=REPO_ROOT, help='repository root (default: %(default)s)')
root = parser.parse_args().root
cmake = read(root, 'CMakeLists.txt')
reference = cmake_target_definitions(cmake)
defaults = cmake_defaults(cmake)
errors = []
compare(errors, 'CMakeLists.txt (pkg-config)', reference, cmake_pkgconfig_definitions(cmake))
compare(errors, 'meson.build', reference, meson_definitions(read(root, 'meson.build')))
options = meson_options(read(root, 'meson_options.txt'))
for option in reference:
if option not in defaults:
errors.append(f'CMakeLists.txt: no option(JSON_{option} ... ON|OFF)')
elif option not in options:
errors.append(f'meson_options.txt: option {option} missing')
elif options[option] != defaults[option]:
errors.append(f'meson_options.txt: option {option} must be boolean with value {defaults[option]} as in CMake')
for option, default in MESON_ONLY.items():
if options.get(option) != default:
errors.append(f'meson_options.txt: option {option} must be boolean with value {default}')
for option in options.keys() - reference.keys() - MESON_ONLY.keys():
errors.append(f'meson_options.txt: option {option} has no counterpart in the CMake target')
docs = read(root, DOCS)
for option in sorted(set(options) & (reference.keys() | MESON_ONLY.keys())):
if f'`{option}`' not in docs:
errors.append(f'{DOCS}: Meson option {option} not listed')
for error in errors:
print(error, file=sys.stderr)
if errors:
print('The Meson build and the pkg-config files must offer the options of the CMake target; see '
'tools/check_build_options/README.md.', file=sys.stderr)
return 1
print(f'OK: {len(reference)} options with compile definitions agree between CMake, pkg-config, and Meson.')
return 0
if __name__ == '__main__':
sys.exit(main())