mirror of
https://github.com/nlohmann/json.git
synced 2026-10-05 22:20:30 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c302648eb | ||
|
|
dc194c94d9 | ||
|
|
9ffd033af7 | ||
|
|
d304501fa7 | ||
|
|
21c8935269 | ||
|
|
96cedfec81 | ||
|
|
d27698ee55 | ||
|
|
295ff0f778 | ||
|
|
e802c98da8 | ||
|
|
c6bb0844f3 |
@@ -16,9 +16,6 @@ only_commits:
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
# The Visual Studio 2017 jobs compile everything with /std:c++17, so they
|
||||
# only build the C++17 variant of each test, split into two jobs each to
|
||||
# stay below AppVeyor's 60-minute limit per job.
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
configuration: Debug
|
||||
platform: x86
|
||||
@@ -37,13 +34,7 @@ environment:
|
||||
configuration: Release
|
||||
platform: x86
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=0/2"
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
configuration: Release
|
||||
platform: x86
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=1/2"
|
||||
CMAKE_OPTIONS: ""
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
@@ -64,13 +55,7 @@ environment:
|
||||
configuration: Release
|
||||
platform: x64
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /Zc:__cplusplus /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=0/2"
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
configuration: Release
|
||||
platform: x64
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /Zc:__cplusplus /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=1/2"
|
||||
CMAKE_OPTIONS: ""
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
|
||||
init:
|
||||
@@ -81,15 +66,10 @@ install:
|
||||
- if "%platform%"=="x86" set GENERATOR_PLATFORM=Win32
|
||||
|
||||
before_build:
|
||||
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On %CMAKE_OPTIONS%
|
||||
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%"
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config "%configuration%" --parallel 2
|
||||
|
||||
test_script:
|
||||
- if "%configuration%"=="Release" ctest -C "%configuration%" --parallel 2 --output-on-failure
|
||||
# On Debug builds, skip test-unicode_all
|
||||
# as it is extremely slow to run and cause
|
||||
# occasional timeouts on AppVeyor.
|
||||
# More info: https://github.com/nlohmann/json/pull/1570
|
||||
- if "%configuration%"=="Debug" ctest --exclude-regex "test-unicode" -C "%configuration%" --parallel 2 --output-on-failure
|
||||
- ctest -C "%configuration%" --parallel 2 --output-on-failure
|
||||
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
container: ubuntu:24.04
|
||||
strategy:
|
||||
matrix:
|
||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local]
|
||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_no_thread_local]
|
||||
steps:
|
||||
- name: Install build-essential
|
||||
run: apt-get update ; apt-get install -y build-essential unzip wget git
|
||||
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
- name: Build
|
||||
run: cmake --build build --parallel 10
|
||||
- name: Test
|
||||
run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure
|
||||
run: cd build ; ctest -j 10 -C Debug --output-on-failure
|
||||
|
||||
clang-cl-12:
|
||||
runs-on: windows-2022
|
||||
@@ -195,7 +195,7 @@ jobs:
|
||||
- name: Build
|
||||
run: cmake --build build --config Debug --parallel 10
|
||||
- name: Test
|
||||
run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure
|
||||
run: cd build ; ctest -j 10 -C Debug --output-on-failure
|
||||
|
||||
ci_module_cpp20:
|
||||
runs-on: windows-2022
|
||||
|
||||
@@ -62,7 +62,6 @@ option(JSON_MultipleHeaders "Use non-amalgamated version of the l
|
||||
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
||||
option(JSON_StrictNulHandling "Build with strict NUL-byte handling enabled." OFF)
|
||||
option(JSON_StrictBinaryUTF8 "Build with UTF-8 checks in the CBOR, UBJSON, BJData, and BSON writers enabled." OFF)
|
||||
option(JSON_DeleteDeprecatedFunctions "Delete the deprecated functions instead of only deprecating them." OFF)
|
||||
|
||||
if (JSON_CI)
|
||||
include(ci)
|
||||
@@ -124,10 +123,6 @@ if (JSON_StrictBinaryUTF8)
|
||||
message(STATUS "Strict UTF-8 checks in binary writers enabled (JSON_STRICT_BINARY_UTF8=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_DeleteDeprecatedFunctions)
|
||||
message(STATUS "Deprecated functions are deleted (JSON_DELETE_DEPRECATED_FUNCTIONS=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_Diagnostic_Positions)
|
||||
message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)")
|
||||
endif()
|
||||
@@ -164,7 +159,6 @@ target_compile_definitions(
|
||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||
$<$<BOOL:${JSON_StrictNulHandling}>:JSON_STRICT_NUL_HANDLING=1>
|
||||
$<$<BOOL:${JSON_StrictBinaryUTF8}>:JSON_STRICT_BINARY_UTF8=1>
|
||||
$<$<BOOL:${JSON_DeleteDeprecatedFunctions}>:JSON_DELETE_DEPRECATED_FUNCTIONS=1>
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
|
||||
+9
-22
@@ -249,20 +249,6 @@ add_custom_target(ci_test_strict_nul_handling
|
||||
COMMENT "Compile and test with strict NUL-byte handling enabled"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Delete the deprecated functions.
|
||||
###############################################################################
|
||||
|
||||
add_custom_target(ci_test_delete_deprecated_functions
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_DeleteDeprecatedFunctions=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_delete_deprecated_functions
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_delete_deprecated_functions
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_delete_deprecated_functions && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with the deprecated functions deleted"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Disable global UDLs.
|
||||
###############################################################################
|
||||
@@ -376,7 +362,7 @@ add_custom_target(ci_test_coverage
|
||||
# Sanitizers.
|
||||
###############################################################################
|
||||
|
||||
set(CLANG_CXX_FLAGS_SANITIZER "-g -O1 -fsanitize=address -fsanitize=undefined -fsanitize=integer -fsanitize=nullability -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=unsigned-integer-overflow -fno-sanitize=unsigned-shift-base -fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/cmake/clang_sanitizer_ignorelist.txt")
|
||||
set(CLANG_CXX_FLAGS_SANITIZER "-g -O1 -fsanitize=address -fsanitize=undefined -fsanitize=integer -fsanitize=nullability -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=unsigned-integer-overflow -fno-sanitize=unsigned-shift-base")
|
||||
|
||||
add_custom_target(ci_test_clang_sanitizer
|
||||
COMMAND CXX=${CLANG_TOOL} CXXFLAGS=${CLANG_CXX_FLAGS_SANITIZER} ${CMAKE_COMMAND}
|
||||
@@ -456,13 +442,14 @@ add_custom_target(ci_test_single_header
|
||||
# Valgrind.
|
||||
###############################################################################
|
||||
|
||||
# The Unicode test (~17M assertions) is too slow under Valgrind.
|
||||
add_custom_target(ci_test_valgrind
|
||||
COMMAND CXX=${GCC_TOOL} ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON -DJSON_Valgrind=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_valgrind
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_valgrind
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_valgrind && ${CMAKE_CTEST_COMMAND} -L valgrind --parallel ${N} --output-on-failure
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_valgrind && ${CMAKE_CTEST_COMMAND} -L valgrind --exclude-regex "test-unicode" --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with Valgrind"
|
||||
)
|
||||
|
||||
@@ -719,7 +706,7 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
|
||||
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
||||
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
||||
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
||||
JSON_StrictNulHandling JSON_StrictBinaryUTF8 JSON_DeleteDeprecatedFunctions)
|
||||
JSON_StrictNulHandling JSON_StrictBinaryUTF8)
|
||||
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||
|
||||
@@ -787,7 +774,7 @@ foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 g++-11 cla
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_${COMPILER}
|
||||
${ADDITIONAL_FLAGS}
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER}
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --exclude-regex "test-unicode" --output-on-failure
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with ${COMPILER}"
|
||||
)
|
||||
endif()
|
||||
@@ -801,7 +788,7 @@ add_custom_target(ci_test_compiler_default
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_default
|
||||
${ADDITIONAL_FLAGS}
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_default --parallel ${N}
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_default && ${CMAKE_CTEST_COMMAND} --parallel ${N} --exclude-regex "test-unicode" -LE git_required --output-on-failure
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_default && ${CMAKE_CTEST_COMMAND} --parallel ${N} -LE git_required --output-on-failure
|
||||
COMMENT "Compile and test with default C++ compiler"
|
||||
)
|
||||
|
||||
@@ -839,7 +826,7 @@ add_custom_target(ci_icpc
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_icpc
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_icpc
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_icpc && ${CMAKE_CTEST_COMMAND} --parallel ${N} --exclude-regex "test-unicode" --output-on-failure
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_icpc && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with ICPC"
|
||||
)
|
||||
|
||||
@@ -850,7 +837,7 @@ add_custom_target(ci_icpx
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_icpx
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_icpx
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_icpx && ${CMAKE_CTEST_COMMAND} --parallel ${N} --exclude-regex "test-unicode" --output-on-failure
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_icpx && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with ICPX (Intel oneAPI DPC++/C++)"
|
||||
)
|
||||
|
||||
@@ -886,7 +873,7 @@ add_custom_target(ci_nvhpc
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_nvhpc
|
||||
# the pipes are escaped so the surrounding shell passes them to ctest verbatim
|
||||
# instead of treating them as shell pipe operators
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_nvhpc && ${CMAKE_CTEST_COMMAND} --parallel ${N} --exclude-regex "test-unicode\\|test-comparison_cpp20\\|test-comparison_legacy_cpp20\\|test-constructor1_cpp11\\|test-deserialization_cpp20" --output-on-failure
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_nvhpc && ${CMAKE_CTEST_COMMAND} --parallel ${N} --exclude-regex "test-comparison_cpp20\\|test-comparison_legacy_cpp20\\|test-constructor1_cpp11\\|test-deserialization_cpp20" --output-on-failure
|
||||
COMMENT "Compile and test with NVIDIA HPC SDK (nvc++)"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Sanitizer ignore list for ci_test_clang_sanitizer (-fsanitize-ignorelist).
|
||||
#
|
||||
# libstdc++ 14's <format> declares `_Scanner(basic_string_view<_CharT>, size_t __nargs = -1)`, so every std::format
|
||||
# call converts -1 to size_t, which -fsanitize=integer reports as implicit-integer-sign-change. This is
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119429, not a bug in this library. Only that check and only <format> are
|
||||
# excluded, so implicit sign changes in the library and the tests are still reported.
|
||||
[implicit-integer-sign-change]
|
||||
src:*/include/c++/*/format
|
||||
@@ -219,7 +219,6 @@ INSERT INTO searchIndex(name, type, path) VALUES ('Supported Macros', 'Guide', '
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'api/macros/json_assert/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_BRACE_INIT_COPY_SEMANTICS', 'Macro', 'api/macros/json_brace_init_copy_semantics/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DELETE_DEPRECATED_FUNCTIONS', 'Macro', 'api/macros/json_delete_deprecated_functions/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
|
||||
@@ -250,7 +249,6 @@ INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_GLOBAL_UDLS', 'Macro', 'api/macros/json_use_global_udls/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_IMPLICIT_CONVERSIONS', 'Macro', 'api/macros/json_use_implicit_conversions/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON', 'Macro', 'api/macros/json_use_legacy_discarded_value_comparison/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS', 'Macro', 'api/macros/json_use_objects_for_enum_keyed_maps/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_SIMDUTF', 'Macro', 'api/macros/json_use_simdutf/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('Macros', 'Macro', 'api/macros/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
||||
|
||||
@@ -8,7 +8,7 @@ static basic_json diff(const basic_json& source,
|
||||
Creates a [JSON Patch](http://jsonpatch.com) so that value `source` can be changed into the value `target` by calling
|
||||
[`patch`](patch.md) function.
|
||||
|
||||
For two JSON values `source` and `target`, the following code always yields `#!cpp true`:
|
||||
For two JSON values `source` and `target`, the following code yields always `#!cpp true`:
|
||||
```cpp
|
||||
source.patch(diff(source, target)) == target;
|
||||
```
|
||||
@@ -27,7 +27,7 @@ a JSON patch to convert the `source` to `target`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: `source` and `target` are never modified.
|
||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||
|
||||
## Complexity
|
||||
|
||||
|
||||
@@ -120,12 +120,3 @@ Linear in the size of the input.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
- Added `error_handler` parameter in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
- Overload (2) replaces calls to `from_bjdata` with a pointer and a length as first two parameters, which has been
|
||||
deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like
|
||||
`#!cpp from_bjdata(ptr, len, ...);` with `#!cpp from_bjdata(ptr, ptr+len, ...);`.
|
||||
|
||||
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
|
||||
function.
|
||||
|
||||
@@ -106,12 +106,3 @@ Linear in the size of the input.
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
- Overload (2) replaces calls to `from_bon8` with a pointer and a length as first two parameters, which has been
|
||||
deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like
|
||||
`#!cpp from_bon8(ptr, len, ...);` with `#!cpp from_bon8(ptr, ptr+len, ...);`.
|
||||
|
||||
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
|
||||
function.
|
||||
|
||||
@@ -55,10 +55,6 @@ This implementation does exactly follow this approach, as it uses double precisi
|
||||
smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally
|
||||
and be serialized to `null`.
|
||||
|
||||
During deserialization (from JSON text or any of the binary formats), a finite number that does not fit into
|
||||
`number_float_t` is rejected with [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406), for
|
||||
example a double-precision number in a binary format when `number_float_t` is `#!cpp float`.
|
||||
|
||||
### Storage
|
||||
|
||||
Floating-point number values are stored directly inside a `basic_json` type.
|
||||
|
||||
@@ -47,9 +47,8 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va
|
||||
|
||||
When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and
|
||||
the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of
|
||||
range will yield over/underflow when used in a constructor. During deserialization (from JSON text or any of the binary
|
||||
formats), too large or small integer numbers will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md)
|
||||
or [`number_float_t`](number_float_t.md).
|
||||
range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers
|
||||
will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md).
|
||||
|
||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||
|
||||
@@ -48,9 +48,8 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default
|
||||
|
||||
When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and
|
||||
the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow
|
||||
when used in a constructor. During deserialization (from JSON text or any of the binary formats), too large or small
|
||||
integer numbers will automatically be stored as [`number_integer_t`](number_integer_t.md) or
|
||||
[`number_float_t`](number_float_t.md).
|
||||
when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored
|
||||
as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md).
|
||||
|
||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||
|
||||
@@ -58,13 +58,6 @@ header. See also the [macro overview page](../../features/macros.md).
|
||||
- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
|
||||
- [**JSON_DISABLE_TUPLE_REFERENCE_CONVERSION**](json_disable_tuple_reference_conversion.md) - switch off conversion from a one-element tuple of a JSON reference
|
||||
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
|
||||
- [**JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS**](json_use_objects_for_enum_keyed_maps.md) - opt in to storing maps with enum
|
||||
keys as objects
|
||||
|
||||
## Deprecated functions
|
||||
|
||||
- [**JSON_DELETE_DEPRECATED_FUNCTIONS**](json_delete_deprecated_functions.md) - opt in to deleting the deprecated
|
||||
functions ahead of their removal in version 4.0.0
|
||||
|
||||
## Comparison behavior
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
# JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
|
||||
```cpp
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS /* value */
|
||||
```
|
||||
|
||||
When defined to `1`, all [deprecated functions](../../community/roadmap.md#removal-of-deprecated-functions) of the
|
||||
library are declared as deleted (`= delete`) instead of only being marked as deprecated. Code that still calls one of
|
||||
them no longer compiles. This way, you can find all calls that need to be replaced before version 4.0.0 removes these
|
||||
functions; the [migration guide](../../integration/migration_guide.md#replace-deprecated-functions) describes how.
|
||||
|
||||
A deleted function, unlike a removed one, still takes part in overload resolution. A call that would select it
|
||||
therefore fails to compile instead of silently selecting another overload. This matters for the deprecated
|
||||
`from_*(ptr, len)` overloads of [`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md),
|
||||
[`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md),
|
||||
[`from_bon8`](../basic_json/from_bon8.md), and [`from_bson`](../basic_json/from_bson.md): without them, a call like
|
||||
`from_cbor(ptr, len)` would compile, read `ptr` as a NUL-terminated string, and convert `len` to the `strict` parameter.
|
||||
|
||||
The macro does not affect the deprecated legacy comparison of discarded values, which is controlled by
|
||||
[`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](json_use_legacy_discarded_value_comparison.md).
|
||||
|
||||
## Default definition
|
||||
|
||||
The default value is `0` (disabled, the deprecated functions can still be called, and the compiler warns about it).
|
||||
|
||||
```cpp
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 0
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "CMake option"
|
||||
|
||||
The macro can also be set with the CMake option
|
||||
[`JSON_DeleteDeprecatedFunctions`](../../integration/cmake.md#json_deletedeprecatedfunctions) (`OFF` by default).
|
||||
|
||||
!!! warning "Opt-in only"
|
||||
|
||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
||||
effect. Define it for the whole project to avoid different declarations of the same class in different
|
||||
translation units.
|
||||
|
||||
!!! note "ABI compatibility"
|
||||
|
||||
The macro only turns calls that compile into calls that do not; it does not change the layout or the behavior of
|
||||
any type. Its value is therefore not encoded in the [namespace](../../features/namespace.md).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: default behavior (macro not defined)"
|
||||
|
||||
Without the macro, the deprecated overload is called, and the compiler warns about it:
|
||||
|
||||
```cpp
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::vector<std::uint8_t> v = {0x82, 0x01, 0x02};
|
||||
auto j = json::from_cbor(v.data(), v.size());
|
||||
// warning: 'from_cbor' is deprecated: Since 3.8.0; use from_cbor(ptr, ptr + len)
|
||||
}
|
||||
```
|
||||
|
||||
??? example "Example: deleted deprecated functions (macro defined to 1)"
|
||||
|
||||
With the macro, the call does not compile:
|
||||
|
||||
```cpp
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::vector<std::uint8_t> v = {0x82, 0x01, 0x02};
|
||||
auto j = json::from_cbor(v.data(), v.size());
|
||||
// error: call to deleted function 'from_cbor'
|
||||
}
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Roadmap: removal of deprecated functions](../../community/roadmap.md#removal-of-deprecated-functions) - the
|
||||
deprecated functions and the version they were deprecated in
|
||||
- [Migration guide: replace deprecated functions](../../integration/migration_guide.md#replace-deprecated-functions) -
|
||||
how to replace each deprecated function
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
- Planned to be removed in version 4.0.0, which removes the deprecated functions. The deprecated `from_*(ptr, len)`
|
||||
overloads stay deleted in version 4.0.0.
|
||||
@@ -112,4 +112,3 @@ The default value is `0` (disabled — existing behavior is preserved).
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
- Planned to become the default (with the macro removed) in version 4.0.0.
|
||||
|
||||
@@ -44,7 +44,7 @@ By default, implicit conversions are enabled.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: implicit and explicit conversions"
|
||||
??? example "Example: implicit conversion"
|
||||
|
||||
This is an example for an implicit conversion:
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
# JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
|
||||
```cpp
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS /* value */
|
||||
```
|
||||
|
||||
When defined to `1`, maps whose keys are enums (such as `std::map<E, T>` or `std::unordered_map<E, T>`) are stored as
|
||||
JSON objects, using the enum's own conversion for the keys. By default, they are stored as arrays of `[key, value]`
|
||||
pairs.
|
||||
|
||||
## Default definition
|
||||
|
||||
The default value is `0` (disabled — existing behavior is preserved).
|
||||
|
||||
```cpp
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
!!! note "Background"
|
||||
|
||||
JSON object keys are strings, so a map is only stored as an object if its keys can be converted to a string type.
|
||||
Enums are not, even if [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) maps them to strings, so a
|
||||
map with enum keys becomes an array of `[key, value]` pairs:
|
||||
|
||||
```json
|
||||
[["stopped", "aa"], ["completed", "bb"]]
|
||||
```
|
||||
|
||||
With this macro, the same map becomes an object
|
||||
(see [#4378](https://github.com/nlohmann/json/issues/4378)):
|
||||
|
||||
```json
|
||||
{"completed": "bb", "stopped": "aa"}
|
||||
```
|
||||
|
||||
!!! note "Maps with non-unique keys"
|
||||
|
||||
Maps that allow duplicate keys, such as `std::multimap<E, T>` or `std::unordered_multimap<E, T>`, are not affected
|
||||
by the macro and are still stored as arrays of `[key, value]` pairs, as an object cannot hold duplicate keys.
|
||||
|
||||
!!! note "Reading"
|
||||
|
||||
Reading is not affected by the macro: a map with enum keys can always be read from both an array of pairs and an
|
||||
object. For the latter, each key is converted to the enum with its `from_json` function, e.g., the one defined by
|
||||
[`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md). Data written without the macro can therefore
|
||||
still be read after enabling it.
|
||||
|
||||
!!! warning "Keys must serialize to distinct strings"
|
||||
|
||||
Each key is converted with the enum's `to_json` function. If a key is not converted to a string (for instance, an
|
||||
enum without [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md), which is stored as an integer, or an
|
||||
enumerator mapped to `nullptr`), [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) is thrown.
|
||||
If two keys are converted to the same string (for instance, because
|
||||
[`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) maps an unlisted enumerator to the first entry),
|
||||
[`type_error.318`](../../home/exceptions.md#jsonexceptiontype_error318) is thrown. In both cases, the target value
|
||||
is not changed.
|
||||
|
||||
!!! warning "Opt-in only"
|
||||
|
||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no effect.
|
||||
|
||||
!!! note "ABI compatibility"
|
||||
|
||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_ekmo`), resulting in
|
||||
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
||||
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: default behavior (macro not defined)"
|
||||
|
||||
Without the macro, a map with enum keys is stored as an array of pairs:
|
||||
|
||||
```cpp
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED };
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {
|
||||
{TS_STOPPED, "stopped"},
|
||||
{TS_RUNNING, "running"},
|
||||
{TS_COMPLETED, "completed"},
|
||||
})
|
||||
|
||||
int main()
|
||||
{
|
||||
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
|
||||
json j = m;
|
||||
// j is [["stopped","aa"],["completed","bb"]]
|
||||
}
|
||||
```
|
||||
|
||||
??? example "Example: objects for enum-keyed maps (macro defined to 1)"
|
||||
|
||||
With the macro, the same map is stored as an object:
|
||||
|
||||
```cpp
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 1
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED };
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {
|
||||
{TS_STOPPED, "stopped"},
|
||||
{TS_RUNNING, "running"},
|
||||
{TS_COMPLETED, "completed"},
|
||||
})
|
||||
|
||||
int main()
|
||||
{
|
||||
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
|
||||
json j = m;
|
||||
// j is {"completed":"bb","stopped":"aa"}
|
||||
|
||||
auto m2 = j.get<std::map<TaskState, std::string>>();
|
||||
// m2 == m
|
||||
}
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Specializing enum conversion](../../features/enum_conversion.md)
|
||||
- [**NLOHMANN_JSON_SERIALIZE_ENUM**](nlohmann_json_serialize_enum.md) - serialize/deserialize an enum
|
||||
- [**NLOHMANN_JSON_SERIALIZE_ENUM_STRICT**](nlohmann_json_serialize_enum_strict.md) - serialize/deserialize an enum with
|
||||
exceptions
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -41,9 +41,6 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
conversion. Select this default pair carefully. See example 1 below.
|
||||
- If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the
|
||||
list will be returned when converting to or from JSON. See example 2 below.
|
||||
- Maps with enum keys (e.g., `std::map<ENUM_TYPE, T>`) are stored as arrays of `[key, value]` pairs by default.
|
||||
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
|
||||
with the converted keys. Such maps can be read from both forms.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -83,7 +80,6 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
- [Specializing enum conversion](../../features/enum_conversion.md)
|
||||
- [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](./nlohmann_json_serialize_enum_strict.md)
|
||||
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
|
||||
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md)
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -44,9 +44,6 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
`"enum value out of range for <type>"`.
|
||||
- If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the
|
||||
list will be returned when converting to or from JSON. See example 2 below.
|
||||
- Maps with enum keys (e.g., `std::map<ENUM_TYPE, T>`) are stored as arrays of `[key, value]` pairs by default.
|
||||
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
|
||||
with the converted keys. Such maps can be read from both forms.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -102,7 +99,6 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
- [Specializing enum conversion](../../features/enum_conversion.md)
|
||||
- [`NLOHMANN_JSON_SERIALIZE_ENUM`](./nlohmann_json_serialize_enum.md)
|
||||
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
|
||||
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md)
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -64,8 +64,6 @@ The following macros guard changes that are planned to become the default in ver
|
||||
| [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) | `0` | `1`: reading from a stream does not consume the character after a number | – | 3.13.0 |
|
||||
| [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) | `0` | `1`: a NUL byte in the input is a parse error instead of the end of input | [`JSON_StrictNulHandling`](../integration/cmake.md#json_strictnulhandling) | 3.13.0 |
|
||||
| [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) | `0` | `1`: `to_cbor`, `to_ubjson`, `to_bjdata`, and `to_bson` throw for strings that are not valid UTF-8 by default | [`JSON_StrictBinaryUTF8`](../integration/cmake.md#json_strictbinaryutf8) | 3.13.0 |
|
||||
| [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) | `0` | `1`: a `basic_json` value can no longer be created from a one-element tuple of a reference to it, such as `std::forward_as_tuple(j)`| [`JSON_DisableTupleReferenceConversion`](../integration/cmake.md#json_disabletuplereferenceconversion) | 3.13.0 |
|
||||
| [`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md) | `0` | removed: the deprecated functions are removed (see below); the `from_*(ptr, len)` overloads stay deleted | [`JSON_DeleteDeprecatedFunctions`](../integration/cmake.md#json_deletedeprecatedfunctions) | 3.13.0 |
|
||||
|
||||
For example, the following makes a 3.x release behave like version 4.0 with respect to these changes:
|
||||
|
||||
@@ -77,8 +75,6 @@ For example, the following makes a 3.x release behave like version 4.0 with resp
|
||||
#define JSON_PRECISE_STREAM_POSITION 1
|
||||
#define JSON_STRICT_NUL_HANDLING 1
|
||||
#define JSON_STRICT_BINARY_UTF8 1
|
||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 1
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 1
|
||||
#include <nlohmann/json.hpp>
|
||||
```
|
||||
|
||||
@@ -88,13 +84,8 @@ way to achieve this.
|
||||
### Removal of deprecated functions
|
||||
|
||||
Version 4.0 will remove all deprecated functions. Compiling with deprecation warnings enabled shows which of them your
|
||||
code still uses. Defining [`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md) to
|
||||
`1` turns these warnings into errors, as the deprecated functions are then deleted. The
|
||||
[migration guide](../integration/migration_guide.md#replace-deprecated-functions) shows how to replace each of them.
|
||||
|
||||
The `from_*` overloads taking a pointer and a length are not removed in version 4.0, but stay deleted. Without them, a
|
||||
call like `from_cbor(ptr, len)` would still compile: it would read `ptr` as a NUL-terminated string and convert `len`
|
||||
to the `strict` parameter.
|
||||
code still uses. The [migration guide](../integration/migration_guide.md#replace-deprecated-functions) shows how to
|
||||
replace each of them.
|
||||
|
||||
| Deprecated | Since | Migration |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------------------------------------------------------------------------------|
|
||||
@@ -106,7 +97,6 @@ to the `strict` parameter.
|
||||
| [`json_pointer::operator string_t`](../api/json_pointer/operator_string_t.md) | 3.11.0 | [JSON Pointers](../integration/migration_guide.md#json-pointers) |
|
||||
| [`json_pointer`](../api/json_pointer/index.md) with a `basic_json` type as template argument, and the overloads of `value`, `contains`, `operator[]`, and `at` accepting such a pointer | 3.11.0 | [JSON Pointers](../integration/migration_guide.md#json-pointers) |
|
||||
| Comparing a [`json_pointer`](../api/json_pointer/index.md) with a string via [`operator==`](../api/json_pointer/operator_eq.md) or [`operator!=`](../api/json_pointer/operator_ne.md) | 3.11.2 | [JSON Pointers](../integration/migration_guide.md#json-pointers) |
|
||||
| [`from_bjdata`](../api/basic_json/from_bjdata.md) and [`from_bon8`](../api/basic_json/from_bon8.md) with `(ptr, len)` | 3.13.0 | [Parsing](../integration/migration_guide.md#parsing) |
|
||||
|
||||
The deprecated legacy comparison of discarded values is controlled by a macro and therefore listed in the table above.
|
||||
|
||||
|
||||
@@ -168,9 +168,9 @@ The library maps CBOR types to JSON value types as follows:
|
||||
!!! warning "Negative integer overflow"
|
||||
|
||||
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
|
||||
result to fit into `number_integer_t` (`std::int64_t` by default), the result is stored as `number_float_t`, like
|
||||
a too small integer in JSON text. For example, `-18446744073709551616` (`0x3B` followed by eight `0xFF` bytes) is
|
||||
stored as `-1.8446744073709552e+19`.
|
||||
result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
|
||||
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
|
||||
silently.
|
||||
|
||||
!!! warning "Object keys"
|
||||
|
||||
|
||||
@@ -58,23 +58,6 @@ assert(jPi.get<TaskState>() == TS_INVALID );
|
||||
--8<-- "examples/nlohmann_json_serialize_enum.output"
|
||||
```
|
||||
|
||||
## Maps with enum keys
|
||||
|
||||
By default, maps with enum keys, such as `std::map<TaskState, std::string>`, are stored as arrays of `[key, value]`
|
||||
pairs, because JSON object keys must be strings. Define
|
||||
[`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) before including the
|
||||
library to store them as objects, with the keys converted by the enum's `to_json()` function:
|
||||
|
||||
```cpp
|
||||
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
|
||||
json j = m;
|
||||
// default: [["stopped","aa"],["completed","bb"]]
|
||||
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS: {"completed":"bb","stopped":"aa"}
|
||||
```
|
||||
|
||||
Either form can be read back, with or without the macro.
|
||||
|
||||
## Notes
|
||||
|
||||
Just as in [Arbitrary Type Conversions](arbitrary_types.md) above,
|
||||
|
||||
@@ -23,17 +23,6 @@ This macro overrides [`#!cpp catch`](https://en.cppreference.com/w/cpp/language/
|
||||
|
||||
See [full documentation of `JSON_CATCH_USER(exception)`](../api/macros/json_throw_user.md).
|
||||
|
||||
## `JSON_DELETE_DEPRECATED_FUNCTIONS`
|
||||
|
||||
When defined to `1`, all deprecated functions are declared as deleted instead of only being marked as deprecated, so
|
||||
code that still calls them no longer compiles. This way, you can find all calls that need to be replaced before version
|
||||
4.0.0 removes these functions.
|
||||
|
||||
The macro can also be set with the CMake option
|
||||
[`JSON_DeleteDeprecatedFunctions`](../integration/cmake.md#json_deletedeprecatedfunctions) (`OFF` by default).
|
||||
|
||||
See [full documentation of `JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md).
|
||||
|
||||
## `JSON_DIAGNOSTICS`
|
||||
|
||||
This macro enables extended diagnostics for exception messages. Possible values are `1` to enable or `0` to disable
|
||||
@@ -97,8 +86,7 @@ See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json
|
||||
## `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`
|
||||
|
||||
When defined to `1`, a JSON value can no longer be created from a one-element `std::tuple` holding a reference to a JSON
|
||||
value, such as the result of `std::forward_as_tuple(j)`. This lets `std::tuple` convert such tuples element-wise. This
|
||||
is planned to become the default in version 4.0.0.
|
||||
value, such as the result of `std::forward_as_tuple(j)`. This lets `std::tuple` convert such tuples element-wise.
|
||||
|
||||
See [full documentation of `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md).
|
||||
|
||||
@@ -210,13 +198,6 @@ default.
|
||||
|
||||
See [full documentation of `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../api/macros/json_use_legacy_discarded_value_comparison.md).
|
||||
|
||||
## `JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`
|
||||
|
||||
When defined to `1`, maps with enum keys (e.g., `std::map<E, T>`) are stored as objects, using the enum's conversion for
|
||||
the keys, instead of arrays of `[key, value]` pairs. It is switched off (`0`) by default.
|
||||
|
||||
See [full documentation of `JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md).
|
||||
|
||||
## `JSON_USE_SIMDUTF`
|
||||
|
||||
When defined, UTF-8 validation of JSON strings read from contiguous byte input is delegated to the
|
||||
|
||||
@@ -21,8 +21,6 @@ The complete default namespace name is derived as follows:
|
||||
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
||||
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
|
||||
- [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) defined non-zero appends `_sbu8`.
|
||||
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) defined non-zero
|
||||
appends `_ekmo`.
|
||||
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
||||
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
||||
below.
|
||||
|
||||
@@ -331,6 +331,9 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
||||
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
||||
```
|
||||
|
||||
@@ -596,9 +599,6 @@ During implicit or explicit value conversion, the JSON type must be compatible w
|
||||
[json.exception.type_error.302] type must be string, but is object
|
||||
```
|
||||
|
||||
This exception is also thrown with [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)
|
||||
if a key of a map with enum keys is not converted to a string, for instance, because the enum is stored as an integer.
|
||||
|
||||
### json.exception.type_error.303
|
||||
|
||||
To retrieve a reference to a value stored in a `basic_json` object with `get_ref`, the type of the reference must match the value type. For instance, for a JSON array, the `ReferenceType` must be `array_t &`.
|
||||
@@ -791,19 +791,6 @@ The dynamic type of the object cannot be represented in the requested serializat
|
||||
|
||||
Encapsulate the JSON value in an object. That is, instead of serializing `#!json true`, serialize `#!json {"value": true}`
|
||||
|
||||
### json.exception.type_error.318
|
||||
|
||||
With [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md), a map with enum
|
||||
keys is stored as an object. This exception is thrown if two of its keys are converted to the same string, so one of the
|
||||
entries would be lost. This happens, for instance, if [`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md)
|
||||
does not list an enumerator and it is therefore converted like the first listed one.
|
||||
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
[json.exception.type_error.318] duplicate object key 'red'
|
||||
```
|
||||
|
||||
## Out of range
|
||||
|
||||
This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance, in the case of array indices or nonexisting object keys.
|
||||
@@ -876,18 +863,13 @@ The JSON Patch operations 'remove' and 'add' cannot be applied to the root eleme
|
||||
|
||||
### json.exception.out_of_range.406
|
||||
|
||||
A parsed number could not be stored without changing it to NaN or INF. For the binary formats, this happens when a
|
||||
finite floating-point number does not fit into [`number_float_t`](../api/basic_json/number_float_t.md), for example a
|
||||
double-precision number when `number_float_t` is `#!cpp float`.
|
||||
A parsed number could not be stored as without changing it to NaN or INF.
|
||||
|
||||
!!! failure "Example messages"
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
number overflow parsing '10E1000'
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.406] syntax error while parsing CBOR value: number overflow
|
||||
```
|
||||
|
||||
### json.exception.out_of_range.407
|
||||
|
||||
|
||||
@@ -132,12 +132,6 @@ Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/
|
||||
|
||||
Enable CI build targets. The exact targets are used during the several CI steps and are subject to change without notice. This option is `OFF` by default.
|
||||
|
||||
### `JSON_DeleteDeprecatedFunctions`
|
||||
|
||||
Delete the deprecated functions instead of only deprecating them by defining the macro
|
||||
[`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md). This option is `OFF` by
|
||||
default.
|
||||
|
||||
### `JSON_Diagnostics`
|
||||
|
||||
Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default.
|
||||
|
||||
@@ -14,13 +14,6 @@ deprecations are annotated with
|
||||
[`HEDLEY_DEPRECATED_FOR`](https://nemequ.github.io/hedley/api-reference.html#HEDLEY_DEPRECATED_FOR) to report which
|
||||
function to use instead.
|
||||
|
||||
!!! tip "Find all calls of deprecated functions"
|
||||
|
||||
Define [`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md) to `1` (or set the
|
||||
CMake option [`JSON_DeleteDeprecatedFunctions`](cmake.md#json_deletedeprecatedfunctions)) to delete all deprecated
|
||||
functions. Every remaining call then fails to compile, even if deprecation warnings are disabled, so your code is
|
||||
ready for version 4.0.0 once it compiles with the macro.
|
||||
|
||||
### Parsing
|
||||
|
||||
- Function `friend std::istream& operator<<(basic_json&, std::istream&)` is deprecated since 3.0.0. Please use
|
||||
@@ -48,10 +41,8 @@ function to use instead.
|
||||
[`from_ubjson`](../api/basic_json/from_ubjson.md), and [`from_bson`](../api/basic_json/from_bson.md)) via initializer
|
||||
lists is deprecated since 3.8.0. Instead, pass two iterators; for instance, call `from_cbor(ptr, ptr+len)` instead of
|
||||
`from_cbor({ptr, len})`. Likewise, passing a pointer and a length as two separate arguments to `from_cbor`,
|
||||
`from_msgpack`, `from_ubjson`, and `from_bson` is deprecated since 3.8.0, and to
|
||||
[`from_bjdata`](../api/basic_json/from_bjdata.md) and [`from_bon8`](../api/basic_json/from_bon8.md) since 3.13.0; call
|
||||
`from_cbor(ptr, ptr+len)` instead of `from_cbor(ptr, len)`. These overloads will not be removed in version 4.0.0, but
|
||||
deleted, so a call like `from_cbor(ptr, len)` cannot compile and convert `len` to the `strict` parameter.
|
||||
`from_msgpack`, `from_ubjson`, and `from_bson` is deprecated since 3.8.0; call `from_cbor(ptr, ptr+len)` instead of
|
||||
`from_cbor(ptr, len)`.
|
||||
|
||||
=== "Deprecated"
|
||||
|
||||
|
||||
@@ -292,7 +292,6 @@ nav:
|
||||
- 'JSON_BRACE_INIT_COPY_SEMANTICS': api/macros/json_brace_init_copy_semantics.md
|
||||
- 'JSON_CATCH_USER, JSON_THROW_USER, JSON_TRY_USER': api/macros/json_throw_user.md
|
||||
- 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
|
||||
- 'JSON_DELETE_DEPRECATED_FUNCTIONS': api/macros/json_delete_deprecated_functions.md
|
||||
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
||||
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
||||
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
|
||||
@@ -314,7 +313,6 @@ nav:
|
||||
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
|
||||
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
||||
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
|
||||
- 'JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS': api/macros/json_use_objects_for_enum_keyed_maps.md
|
||||
- 'JSON_USE_SIMDUTF': api/macros/json_use_simdutf.md
|
||||
- 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md
|
||||
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md
|
||||
|
||||
@@ -50,10 +50,6 @@
|
||||
#define JSON_STRICT_BINARY_UTF8 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
@@ -96,20 +92,14 @@
|
||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
||||
#endif
|
||||
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS _ekmo
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h) json_abi ## a ## b ## c ## d ## e ## f ## g ## h
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h)
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
@@ -119,8 +109,7 @@
|
||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
||||
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
|
||||
@@ -172,7 +172,7 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
|
||||
typename BasicJsonType::number_unsigned_t, underlying_type>::type;
|
||||
value_type val;
|
||||
get_arithmetic_value(j, val);
|
||||
e = static_cast<EnumType>(bool_aware_static_cast<underlying_type>(val));
|
||||
e = static_cast<EnumType>(static_cast<underlying_type>(val));
|
||||
}
|
||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||
|
||||
@@ -530,40 +530,11 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
|
||||
}
|
||||
}
|
||||
|
||||
// read a map with enum keys from an object, using the enum's own from_json for
|
||||
// the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written
|
||||
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
template<typename BasicJsonType, typename Map>
|
||||
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
|
||||
{
|
||||
if (!j.is_object())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m.clear();
|
||||
for (const auto& p : *j.template get_ptr<const typename BasicJsonType::object_t*>())
|
||||
{
|
||||
m.emplace(BasicJsonType(p.first).template get<typename Map::key_type>(), p.second.template get<typename Map::mapped_type>());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename Map>
|
||||
inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
||||
typename = enable_if_t < !std::is_constructible <
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
|
||||
{
|
||||
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
|
||||
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
|
||||
{
|
||||
return;
|
||||
}
|
||||
from_json_pair_array_to_map(j, m);
|
||||
}
|
||||
|
||||
@@ -572,11 +543,6 @@ template < typename BasicJsonType, typename Key, typename Value, typename Hash,
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
|
||||
{
|
||||
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
|
||||
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
|
||||
{
|
||||
return;
|
||||
}
|
||||
from_json_pair_array_to_map(j, m);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <valarray> // valarray
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/std_fs.hpp>
|
||||
@@ -287,20 +286,9 @@ struct external_constructor<value_t::object>
|
||||
/////////////
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
// whether storing the value of a std::optional<T> cannot throw; MSVC 2017
|
||||
// evaluates std::is_nothrow_assignable as true even if T's to_json throws, so
|
||||
// the exception would call std::terminate (#5642)
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1920
|
||||
template<typename BasicJsonType, typename T>
|
||||
using is_nothrow_optional_to_json = std::false_type;
|
||||
#else
|
||||
template<typename BasicJsonType, typename T>
|
||||
using is_nothrow_optional_to_json = std::is_nothrow_assignable<BasicJsonType&, const T&>;
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(is_nothrow_optional_to_json<BasicJsonType, T>::value)
|
||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_nothrow_assignable<BasicJsonType&, const T&>::value)
|
||||
{
|
||||
if (opt.has_value())
|
||||
{
|
||||
@@ -376,7 +364,7 @@ inline void to_json(BasicJsonType& j, EnumType e) noexcept
|
||||
{
|
||||
using underlying_type = typename std::underlying_type<EnumType>::type;
|
||||
static constexpr value_t integral_value_t = std::is_unsigned<underlying_type>::value ? value_t::number_unsigned : value_t::number_integer;
|
||||
external_constructor<integral_value_t>::construct(j, bool_aware_static_cast<underlying_type>(e));
|
||||
external_constructor<integral_value_t>::construct(j, static_cast<underlying_type>(e));
|
||||
}
|
||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||
|
||||
@@ -396,9 +384,6 @@ template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
!is_basic_json<CompatibleArrayType>::value
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
&& !is_enum_keyed_map<CompatibleArrayType>::value
|
||||
#endif
|
||||
,
|
||||
int > = 0 >
|
||||
@@ -453,33 +438,6 @@ inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
|
||||
external_constructor<value_t::object>::construct(j, obj);
|
||||
}
|
||||
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
// store a map with enum keys as an object, using the enum's own to_json for the
|
||||
// keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); without the macro, such maps
|
||||
// are stored as arrays of [key, value] pairs
|
||||
template < typename BasicJsonType, typename EnumKeyedMap,
|
||||
enable_if_t < is_enum_keyed_map<EnumKeyedMap>::value&& !is_basic_json<EnumKeyedMap>::value, int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
||||
{
|
||||
typename BasicJsonType::object_t obj;
|
||||
for (const auto& p : map)
|
||||
{
|
||||
BasicJsonType key = p.first;
|
||||
if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
|
||||
{
|
||||
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*>();
|
||||
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
|
||||
{
|
||||
JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
|
||||
}
|
||||
}
|
||||
external_constructor<value_t::object>::construct(j, std::move(obj));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -81,13 +81,9 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_string_t/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
|
||||
operator string_t() const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return to_string();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
/// @brief write string representation of the JSON pointer to stream
|
||||
@@ -1026,13 +1022,9 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
|
||||
bool operator==(const string_t& rhs) const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return *this == json_pointer(rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief 3-way compares two JSON pointers
|
||||
template<typename RefStringTypeRhs>
|
||||
@@ -1110,26 +1102,18 @@ template<typename RefStringTypeLhs,
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
|
||||
inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return lhs == json_pointer<RefStringTypeLhs>(rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeRhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
|
||||
inline bool operator==(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return json_pointer<RefStringTypeRhs>(lhs) == rhs;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
@@ -1143,26 +1127,18 @@ template<typename RefStringTypeLhs,
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
|
||||
inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeRhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
|
||||
inline bool operator!=(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
|
||||
@@ -888,10 +888,6 @@
|
||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
||||
#endif
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
#undef JSON_PRECISE_STREAM_POSITION
|
||||
#undef JSON_STRICT_NUL_HANDLING
|
||||
#undef JSON_STRICT_BINARY_UTF8
|
||||
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#endif
|
||||
|
||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||
|
||||
@@ -438,30 +438,6 @@ template<typename BasicJsonType, typename CompatibleObjectType>
|
||||
struct is_compatible_object_type
|
||||
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
|
||||
|
||||
template<typename T>
|
||||
using insert_result_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()));
|
||||
|
||||
template<typename T>
|
||||
using insert_result_second_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()).second);
|
||||
|
||||
// a map-like type (std::map, std::unordered_map, ...) whose keys are enums; see
|
||||
// JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
template<typename T, typename = void>
|
||||
struct is_enum_keyed_map : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_enum_keyed_map <
|
||||
T, enable_if_t < is_detected<mapped_type_t, T>::value&&
|
||||
is_detected<key_type_t, T>::value >>
|
||||
{
|
||||
// maps with non-unique keys (std::multimap, std::unordered_multimap, ...)
|
||||
// are excluded, because an object cannot hold duplicate keys; they are
|
||||
// detected by insert() returning an iterator instead of a pair<iterator, bool>
|
||||
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
|
||||
static constexpr bool value = std::is_enum<typename T::key_type>::value &&
|
||||
!(is_detected<insert_result_t, T>::value && !is_detected<insert_result_second_t, T>::value);
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||
typename = void>
|
||||
struct is_constructible_object_type_impl : std::false_type {};
|
||||
@@ -906,21 +882,6 @@ T conditional_static_cast(U value)
|
||||
return value;
|
||||
}
|
||||
|
||||
// like conditional_static_cast, but converts to bool by comparing with zero,
|
||||
// because MSVC 2015 warns about any conversion to bool (C4800), even with an
|
||||
// explicit cast; used for enums whose underlying type is bool
|
||||
template < typename T, typename U, enable_if_t < !std::is_same<T, bool>::value, int > = 0 >
|
||||
T bool_aware_static_cast(U value)
|
||||
{
|
||||
return conditional_static_cast<T>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename U, enable_if_t<std::is_same<T, bool>::value, int> = 0>
|
||||
bool bool_aware_static_cast(U value)
|
||||
{
|
||||
return value != U();
|
||||
}
|
||||
|
||||
template<typename... Types>
|
||||
using all_integral = conjunction<std::is_integral<Types>...>;
|
||||
|
||||
|
||||
+14
-147
@@ -3492,13 +3492,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return value(ptr.convert(), default_value);
|
||||
}
|
||||
#endif
|
||||
|
||||
template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
|
||||
detail::enable_if_t <
|
||||
@@ -3507,13 +3503,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return value(ptr.convert(), std::forward<ValueType>(default_value));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief access the first element
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/front/
|
||||
@@ -3871,13 +3863,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return ptr.contains(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -3988,13 +3976,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// that is, replace `json::iterator_wrapper(j)` with `j.items()`.
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
|
||||
static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return ref.items();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief wrapper to access iterator member functions in range-based for
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/items/
|
||||
@@ -4003,13 +3987,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// that is, replace `json::iterator_wrapper(j)` with `j.items()`.
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
|
||||
static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return ref.items();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief helper to access iterator member functions in range-based for
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/items/
|
||||
@@ -5098,7 +5078,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||
template<typename ScalarType>
|
||||
requires std::is_scalar_v<ScalarType>
|
||||
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) <= rhs;
|
||||
}
|
||||
@@ -5107,7 +5087,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||
template<typename ScalarType>
|
||||
requires std::is_scalar_v<ScalarType>
|
||||
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) >= rhs;
|
||||
}
|
||||
@@ -5128,16 +5108,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
#endif
|
||||
}
|
||||
|
||||
// The friend comparisons with a scalar name the JSON type via decltype of
|
||||
// their parameter in noexcept, because older MSVC versions do not see the
|
||||
// class scope there: MSVC 2015 and 2017 take basic_json as the template,
|
||||
// and MSVC 2019 16.0 rejects member types and template parameters.
|
||||
|
||||
/// @brief comparison: equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator==(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(lhs)>, ScalarType>::value)
|
||||
friend bool operator==(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return lhs == basic_json(rhs);
|
||||
}
|
||||
@@ -5146,7 +5121,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator==(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator==(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) == rhs;
|
||||
}
|
||||
@@ -5162,7 +5137,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(lhs)>, ScalarType>::value)
|
||||
friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return lhs != basic_json(rhs);
|
||||
}
|
||||
@@ -5171,7 +5146,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) != rhs;
|
||||
}
|
||||
@@ -5191,7 +5166,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator<(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(lhs)>, ScalarType>::value)
|
||||
friend bool operator<(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return lhs < basic_json(rhs);
|
||||
}
|
||||
@@ -5200,7 +5175,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator<(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator<(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) < rhs;
|
||||
}
|
||||
@@ -5220,7 +5195,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(lhs)>, ScalarType>::value)
|
||||
friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return lhs <= basic_json(rhs);
|
||||
}
|
||||
@@ -5229,7 +5204,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) <= rhs;
|
||||
}
|
||||
@@ -5250,7 +5225,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator>(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(lhs)>, ScalarType>::value)
|
||||
friend bool operator>(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return lhs > basic_json(rhs);
|
||||
}
|
||||
@@ -5259,7 +5234,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator>(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator>(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) > rhs;
|
||||
}
|
||||
@@ -5279,7 +5254,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(lhs)>, ScalarType>::value)
|
||||
friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return lhs >= basic_json(rhs);
|
||||
}
|
||||
@@ -5288,7 +5263,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||
template<typename ScalarType, typename std::enable_if<
|
||||
std::is_scalar<ScalarType>::value, int>::type = 0>
|
||||
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<detail::uncvref_t<decltype(rhs)>, ScalarType>::value)
|
||||
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||
{
|
||||
return basic_json(lhs) >= rhs;
|
||||
}
|
||||
@@ -5324,9 +5299,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return o;
|
||||
}
|
||||
|
||||
#if !JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
// the deleted version is a function template after the class, because
|
||||
// GCC < 5 and Clang < 10 reject deleted friend functions in class templates
|
||||
/// @brief serialize to stream
|
||||
/// @sa https://json.nlohmann.me/api/operator_ltlt/
|
||||
/// @deprecated This function is deprecated since 3.0.0 and will be removed in
|
||||
@@ -5338,7 +5310,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
{
|
||||
return o << j;
|
||||
}
|
||||
#endif
|
||||
#endif // JSON_NO_IO
|
||||
/// @}
|
||||
|
||||
@@ -5390,16 +5361,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
const bool allow_exceptions = true,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
basic_json result;
|
||||
auto p = parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
p.parse(true, result);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief check if the input is valid JSON
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||
@@ -5429,13 +5396,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static bool accept(detail::span_input_adapter&& i,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief generate SAX events
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
||||
@@ -5496,9 +5459,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
const bool strict = true,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
auto ia = i.get();
|
||||
return format == input_format_t::json
|
||||
@@ -5507,14 +5467,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
||||
}
|
||||
#endif
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#ifndef JSON_NO_IO
|
||||
#if !JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
// the deleted version is a function template after the class, because
|
||||
// GCC < 5 and Clang < 10 reject deleted friend functions in class templates
|
||||
/// @brief deserialize from stream
|
||||
/// @sa https://json.nlohmann.me/api/operator_gtgt/
|
||||
/// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in
|
||||
@@ -5526,7 +5482,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
{
|
||||
return operator>>(i, j);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief deserialize from stream
|
||||
/// @sa https://json.nlohmann.me/api/operator_gtgt/
|
||||
@@ -5874,13 +5829,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
|
||||
@@ -5888,13 +5839,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_binary_impl(i.get(), input_format_t::cbor, strict, allow_exceptions, error_handler_t::keep, tag_handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||
@@ -5927,26 +5874,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json from_msgpack(const T* ptr, std::size_t len,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
|
||||
static basic_json from_msgpack(detail::span_input_adapter&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_binary_impl(i.get(), input_format_t::msgpack, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||
@@ -5979,26 +5918,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json from_ubjson(const T* ptr, std::size_t len,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
|
||||
static basic_json from_ubjson(detail::span_input_adapter&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_binary_impl(i.get(), input_format_t::ubjson, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
||||
@@ -6025,20 +5956,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bjdata, strict, allow_exceptions, error_handler);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.13.0, from_bjdata(ptr, ptr + len))
|
||||
static basic_json from_bjdata(const T* ptr, std::size_t len,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_bjdata(ptr, ptr + len, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief create a JSON value from an input in BON8 format
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bon8/
|
||||
template<typename InputType>
|
||||
@@ -6062,20 +5979,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bon8, strict, allow_exceptions);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.13.0, from_bon8(ptr, ptr + len))
|
||||
static basic_json from_bon8(const T* ptr, std::size_t len,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_bon8(ptr, ptr + len, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
template<typename InputType>
|
||||
@@ -6107,26 +6010,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json from_bson(const T* ptr, std::size_t len,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_bson(ptr, ptr + len, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
|
||||
static basic_json from_bson(detail::span_input_adapter&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return from_binary_impl(i.get(), input_format_t::bson, strict, allow_exceptions);
|
||||
}
|
||||
#endif
|
||||
/// @}
|
||||
|
||||
//////////////////////////
|
||||
@@ -6146,13 +6041,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return ptr.get_unchecked(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief access specified element via JSON Pointer
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
|
||||
@@ -6164,13 +6055,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return ptr.get_unchecked(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief access specified element via JSON Pointer
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
@@ -6182,13 +6069,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return ptr.get_checked(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief access specified element via JSON Pointer
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
@@ -6200,13 +6083,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return ptr.get_checked(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief return flattened JSON value
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/flatten/
|
||||
@@ -7183,18 +7062,6 @@ std::string format_as(const NLOHMANN_BASIC_JSON_TPL& j)
|
||||
return j.dump();
|
||||
}
|
||||
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS && !defined(JSON_NO_IO)
|
||||
/// @brief serialize to stream (deleted; use operator<<(std::ostream&, const basic_json&))
|
||||
/// @sa https://json.nlohmann.me/api/operator_ltlt/
|
||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||
std::ostream& operator>>(const NLOHMANN_BASIC_JSON_TPL& j, std::ostream& o) = delete;
|
||||
|
||||
/// @brief deserialize from stream (deleted; use operator>>(std::istream&, basic_json&))
|
||||
/// @sa https://json.nlohmann.me/api/operator_gtgt/
|
||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||
std::istream& operator<<(NLOHMANN_BASIC_JSON_TPL& j, std::istream& i) = delete;
|
||||
#endif
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
///////////////////////
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+232
-598
File diff suppressed because it is too large
Load Diff
@@ -67,10 +67,6 @@
|
||||
#define JSON_STRICT_BINARY_UTF8 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
@@ -113,20 +109,14 @@
|
||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
||||
#endif
|
||||
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS _ekmo
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h) json_abi ## a ## b ## c ## d ## e ## f ## g ## h
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h)
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
@@ -136,8 +126,7 @@
|
||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
||||
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
|
||||
@@ -8,7 +8,6 @@ set(JSON_SIMDUTF_VERSION 9.1.0 CACHE STRING "The simdutf version used by JSON_Te
|
||||
|
||||
set(JSON_32bitTest AUTO CACHE STRING "Enable the 32bit unit test (ON/OFF/AUTO/ONLY).")
|
||||
set(JSON_TestStandards "" CACHE STRING "The list of standards to test explicitly.")
|
||||
set(JSON_TestShard "" CACHE STRING "Build only a part of the unit tests, given as <index>/<count> (e.g. 0/2), to split them across CI jobs with a time limit.")
|
||||
|
||||
# using an env var, since this will also affect targets executing cmake (such as "ci_test_compiler_default")
|
||||
if (NOT "" STREQUAL "$ENV{JSON_FORCED_GLOBAL_COMPILE_OPTIONS}")
|
||||
@@ -135,9 +134,6 @@ json_test_set_test_options(test-disabled_exceptions
|
||||
#$<$<CXX_COMPILER_ID:MSVC>:/EH>
|
||||
)
|
||||
|
||||
# raise timeout of expensive Unicode test
|
||||
json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000)
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -289,33 +285,6 @@ elseif(NOT json_32bit_test)
|
||||
list(FILTER files EXCLUDE REGEX src/unit-32bit.cpp)
|
||||
endif()
|
||||
|
||||
# with JSON_TestShard=<index>/<count>, keep every <count>-th unit test file,
|
||||
# starting at <index> (the glob is sorted, so the split is stable)
|
||||
set(test_shard_index 0)
|
||||
if(NOT "${JSON_TestShard}" STREQUAL "")
|
||||
if(NOT JSON_TestShard MATCHES "^([0-9]+)/([1-9][0-9]*)$")
|
||||
message(FATAL_ERROR "JSON_TestShard must be <index>/<count>, e.g. 0/2, not '${JSON_TestShard}'.")
|
||||
endif()
|
||||
set(test_shard_index ${CMAKE_MATCH_1})
|
||||
set(test_shard_count ${CMAKE_MATCH_2})
|
||||
if(NOT test_shard_index LESS test_shard_count)
|
||||
message(FATAL_ERROR "JSON_TestShard: the index must be less than the count, not '${JSON_TestShard}'.")
|
||||
endif()
|
||||
list(LENGTH files test_file_count)
|
||||
set(shard_files "")
|
||||
set(file_position 0)
|
||||
foreach(file ${files})
|
||||
math(EXPR file_shard "${file_position} % ${test_shard_count}")
|
||||
if(file_shard EQUAL test_shard_index)
|
||||
list(APPEND shard_files ${file})
|
||||
endif()
|
||||
math(EXPR file_position "${file_position} + 1")
|
||||
endforeach()
|
||||
set(files ${shard_files})
|
||||
list(LENGTH files shard_file_count)
|
||||
message(STATUS "Test shard ${JSON_TestShard}: ${shard_file_count} of ${test_file_count} unit test files")
|
||||
endif()
|
||||
|
||||
foreach(file ${files})
|
||||
json_test_add_test_for(${file} MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force})
|
||||
endforeach()
|
||||
@@ -333,9 +302,6 @@ if(json_32bit_test_only)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# the following variants of single test files are only built in the first shard
|
||||
if(test_shard_index EQUAL 0)
|
||||
|
||||
# test legacy comparison of discarded values
|
||||
json_test_set_test_options(test-comparison_legacy
|
||||
COMPILE_DEFINITIONS JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1
|
||||
@@ -363,8 +329,6 @@ json_test_add_test_for(src/unit-diagnostic-positions.cpp
|
||||
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
# *DO NOT* use json_test_set_test_options() below this line
|
||||
|
||||
#############################################################################
|
||||
|
||||
@@ -48,10 +48,6 @@ TEST_CASE("default namespace")
|
||||
expected += "_sbu8";
|
||||
#endif
|
||||
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
expected += "_ekmo";
|
||||
#endif
|
||||
|
||||
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
||||
|
||||
@@ -49,10 +49,6 @@ TEST_CASE("default namespace without version component")
|
||||
expected += "_sbu8";
|
||||
#endif
|
||||
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
expected += "_ekmo";
|
||||
#endif
|
||||
|
||||
expected += "::basic_json";
|
||||
|
||||
// fallback for Clang
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstdint> // uint8_t
|
||||
#include <cstddef> // size_t
|
||||
#include <fstream> // ifstream, ios
|
||||
@@ -43,6 +44,33 @@ T next_integer_sample(T i, T last, T stride)
|
||||
return n < last ? n : last;
|
||||
}
|
||||
|
||||
// UTF-8 continuation bytes in [lo, hi] that stand in for all of them in the
|
||||
// ill-formed UTF-8 tests. Both the lexer's range checks and the serializer's
|
||||
// decoder (detail::decode) only distinguish the classes 0x80..0x8F, 0x90..0x9F,
|
||||
// and 0xA0..0xBF, so the first and last byte of each class within [lo, hi]
|
||||
// exercise every behavior while a test sweeps another byte position through
|
||||
// all 256 values (#5418). Define JSON_TEST_UTF8_EXHAUSTIVE to get every byte.
|
||||
inline std::vector<int> utf8_continuation_bytes(int lo, int hi)
|
||||
{
|
||||
std::vector<int> result;
|
||||
#ifdef JSON_TEST_UTF8_EXHAUSTIVE
|
||||
for (int byte = lo; byte <= hi; ++byte)
|
||||
{
|
||||
result.push_back(byte);
|
||||
}
|
||||
#else
|
||||
static const std::array<int, 6> class_ends = {{0x80, 0x8F, 0x90, 0x9F, 0xA0, 0xBF}};
|
||||
for (const int byte : class_ends)
|
||||
{
|
||||
if (lo <= byte && byte <= hi)
|
||||
{
|
||||
result.push_back(byte);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
|
||||
@@ -521,10 +521,6 @@ struct countdown_allocator : std::allocator<T>
|
||||
|
||||
TEST_CASE("converting a deeply nested value from another specialization fails cleanly (#5650)")
|
||||
{
|
||||
// MSVC 2015's debug STL constructs the containers' debug proxies through
|
||||
// the allocator in noexcept constructors, so a failing construction crashes
|
||||
// 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)
|
||||
using countdown_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
@@ -562,7 +558,6 @@ TEST_CASE("converting a deeply nested value from another specialization fails cl
|
||||
}
|
||||
}
|
||||
CHECK(failures > 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace
|
||||
|
||||
@@ -11,15 +11,10 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "make_test_data_available.hpp"
|
||||
|
||||
TEST_CASE("Binary Formats" * doctest::skip())
|
||||
TEST_CASE("Binary Formats")
|
||||
{
|
||||
SECTION("canada.json")
|
||||
{
|
||||
@@ -147,48 +142,6 @@ TEST_CASE("Binary Formats" * doctest::skip())
|
||||
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(84.963));
|
||||
}
|
||||
|
||||
SECTION("jeopardy.json")
|
||||
{
|
||||
const auto* filename = TEST_DATA_DIRECTORY "/jeopardy/jeopardy.json";
|
||||
json j = json::parse(std::ifstream(filename));
|
||||
|
||||
const auto json_size = j.dump().size();
|
||||
const auto bjdata_1_size = json::to_bjdata(j).size();
|
||||
const auto bjdata_2_size = json::to_bjdata(j, true).size();
|
||||
const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
|
||||
const auto bon8_size = json::to_bon8(j).size();
|
||||
const auto bson_size = json::to_bson({{"", j}}).size(); // wrap array in object for BSON
|
||||
const auto cbor_size = json::to_cbor(j).size();
|
||||
const auto msgpack_size = json::to_msgpack(j).size();
|
||||
const auto ubjson_1_size = json::to_ubjson(j).size();
|
||||
const auto ubjson_2_size = json::to_ubjson(j, true).size();
|
||||
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
|
||||
|
||||
CHECK(json_size == 52508728);
|
||||
CHECK(bjdata_1_size == 50710965);
|
||||
CHECK(bjdata_2_size == 51144830);
|
||||
CHECK(bjdata_3_size == 51144830);
|
||||
CHECK(bon8_size == 45942080);
|
||||
CHECK(bson_size == 56008520);
|
||||
CHECK(cbor_size == 46187320);
|
||||
CHECK(msgpack_size == 46158575);
|
||||
CHECK(ubjson_1_size == 50710965);
|
||||
CHECK(ubjson_2_size == 51144830);
|
||||
CHECK(ubjson_3_size == 49861422);
|
||||
|
||||
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
|
||||
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(96.576));
|
||||
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(97.402));
|
||||
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(97.402));
|
||||
CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(87.494));
|
||||
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(106.665));
|
||||
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(87.961));
|
||||
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(87.906));
|
||||
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(96.576));
|
||||
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(97.402));
|
||||
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(94.958));
|
||||
}
|
||||
|
||||
SECTION("sample.json")
|
||||
{
|
||||
const auto* filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json";
|
||||
@@ -230,138 +183,46 @@ TEST_CASE("Binary Formats" * doctest::skip())
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
// jeopardy.json is 52 MB and produces ~500 MB of serialization output, so it
|
||||
// is kept apart from the cheap corpus files above (#5418)
|
||||
TEST_CASE("Binary Formats (jeopardy.json)" * doctest::skip())
|
||||
{
|
||||
// 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
|
||||
// to a function pointer in the braced initializer of the format table
|
||||
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>;
|
||||
const auto* filename = TEST_DATA_DIRECTORY "/jeopardy/jeopardy.json";
|
||||
json j = json::parse(std::ifstream(filename));
|
||||
|
||||
bytes encode_cbor(const json& j)
|
||||
{
|
||||
return json::to_cbor(j);
|
||||
}
|
||||
narrow_json decode_cbor(const bytes& v, bool allow_exceptions)
|
||||
{
|
||||
return narrow_json::from_cbor(v, true, allow_exceptions);
|
||||
}
|
||||
|
||||
bytes encode_msgpack(const json& j)
|
||||
{
|
||||
return json::to_msgpack(j);
|
||||
}
|
||||
narrow_json decode_msgpack(const bytes& v, bool allow_exceptions)
|
||||
{
|
||||
return narrow_json::from_msgpack(v, true, allow_exceptions);
|
||||
}
|
||||
|
||||
bytes encode_ubjson(const json& j)
|
||||
{
|
||||
return json::to_ubjson(j);
|
||||
}
|
||||
narrow_json decode_ubjson(const bytes& v, bool allow_exceptions)
|
||||
{
|
||||
return narrow_json::from_ubjson(v, true, allow_exceptions);
|
||||
}
|
||||
|
||||
bytes encode_bjdata(const json& j)
|
||||
{
|
||||
return json::to_bjdata(j);
|
||||
}
|
||||
narrow_json decode_bjdata(const bytes& v, bool allow_exceptions)
|
||||
{
|
||||
return narrow_json::from_bjdata(v, true, allow_exceptions);
|
||||
}
|
||||
|
||||
// BSON can only store numbers as object members
|
||||
bytes encode_bson(const json& j)
|
||||
{
|
||||
return json::to_bson(json{{"a", j}});
|
||||
}
|
||||
narrow_json decode_bson(const bytes& v, bool allow_exceptions)
|
||||
{
|
||||
const auto result = narrow_json::from_bson(v, true, allow_exceptions);
|
||||
return result.is_discarded() ? result : result.at("a");
|
||||
}
|
||||
|
||||
bytes encode_bon8(const json& j)
|
||||
{
|
||||
return json::to_bon8(j);
|
||||
}
|
||||
narrow_json decode_bon8(const bytes& v, bool allow_exceptions)
|
||||
{
|
||||
return narrow_json::from_bon8(v, true, allow_exceptions);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Binary formats with narrow number types")
|
||||
{
|
||||
// Numbers that do not fit the number types are handled like the lexer
|
||||
// handles them in JSON text: an integer that fits neither integer type is
|
||||
// stored as a floating-point number, and a finite floating-point number
|
||||
// that overflows number_float_t is rejected with out_of_range.406.
|
||||
struct binary_format
|
||||
{
|
||||
const char* name;
|
||||
bytes (*encode)(const json&);
|
||||
narrow_json (*decode)(const bytes&, bool);
|
||||
};
|
||||
|
||||
const std::vector<binary_format> formats =
|
||||
{
|
||||
{"CBOR", encode_cbor, decode_cbor},
|
||||
{"MessagePack", encode_msgpack, decode_msgpack},
|
||||
{"UBJSON", encode_ubjson, decode_ubjson},
|
||||
{"BJData", encode_bjdata, decode_bjdata},
|
||||
{"BSON", encode_bson, decode_bson},
|
||||
{"BON8", encode_bon8, decode_bon8},
|
||||
};
|
||||
|
||||
for (const auto& format : formats)
|
||||
{
|
||||
const std::string name = format.name;
|
||||
INFO("format := ", name);
|
||||
const auto roundtrip = [&format](const json & j)
|
||||
{
|
||||
return format.decode(format.encode(j), true);
|
||||
};
|
||||
|
||||
// integers that fit keep their type
|
||||
CHECK(roundtrip(json(-5)).is_number_integer());
|
||||
CHECK(roundtrip(json(-5)).get<std::int32_t>() == -5);
|
||||
CHECK(roundtrip(json(3000000000u)).is_number_unsigned());
|
||||
CHECK(roundtrip(json(3000000000u)).get<std::uint32_t>() == 3000000000u);
|
||||
|
||||
// integers that fit neither integer type are stored as float
|
||||
CHECK(roundtrip(json(5000000000u)).is_number_float());
|
||||
CHECK(roundtrip(json(5000000000u)).get<float>() == 5000000000.0f);
|
||||
if (name != "BON8") // BON8 cannot encode integers above INT64_MAX
|
||||
{
|
||||
CHECK(roundtrip(json(10000000000000000000u)).is_number_float());
|
||||
CHECK(roundtrip(json(10000000000000000000u)).get<float>() == 10000000000000000000.0f);
|
||||
}
|
||||
CHECK(roundtrip(json(-3000000000LL)).is_number_float());
|
||||
CHECK(roundtrip(json(-3000000000LL)).get<float>() == -3000000000.0f);
|
||||
CHECK(roundtrip(json(-5000000000LL)).is_number_float());
|
||||
CHECK(roundtrip(json(-5000000000LL)).get<float>() == -5000000000.0f);
|
||||
|
||||
// floating-point numbers that fit
|
||||
CHECK(roundtrip(json(1.5)).get<float>() == 1.5f);
|
||||
const auto just_above_max = std::nextafter(static_cast<double>((std::numeric_limits<float>::max)()),
|
||||
std::numeric_limits<double>::infinity());
|
||||
CHECK(roundtrip(json(just_above_max)).get<float>() == (std::numeric_limits<float>::max)());
|
||||
|
||||
// infinity and NaN are passed on
|
||||
CHECK(std::isinf(roundtrip(json(std::numeric_limits<double>::infinity())).get<float>()));
|
||||
CHECK(std::isnan(roundtrip(json(std::numeric_limits<double>::quiet_NaN())).get<float>()));
|
||||
|
||||
// finite floating-point numbers that overflow number_float_t are rejected
|
||||
const std::string message = "[json.exception.out_of_range.406] syntax error while parsing " + name
|
||||
+ " value: number overflow";
|
||||
CHECK_THROWS_WITH_AS(roundtrip(json(1e300)), message.c_str(), narrow_json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(roundtrip(json(-1e300)), message.c_str(), narrow_json::out_of_range&);
|
||||
CHECK(format.decode(format.encode(json(1e300)), false).is_discarded());
|
||||
}
|
||||
const auto json_size = j.dump().size();
|
||||
const auto bjdata_1_size = json::to_bjdata(j).size();
|
||||
const auto bjdata_2_size = json::to_bjdata(j, true).size();
|
||||
const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
|
||||
const auto bon8_size = json::to_bon8(j).size();
|
||||
const auto bson_size = json::to_bson({{"", j}}).size(); // wrap array in object for BSON
|
||||
const auto cbor_size = json::to_cbor(j).size();
|
||||
const auto msgpack_size = json::to_msgpack(j).size();
|
||||
const auto ubjson_1_size = json::to_ubjson(j).size();
|
||||
const auto ubjson_2_size = json::to_ubjson(j, true).size();
|
||||
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
|
||||
|
||||
CHECK(json_size == 52508728);
|
||||
CHECK(bjdata_1_size == 50710965);
|
||||
CHECK(bjdata_2_size == 51144830);
|
||||
CHECK(bjdata_3_size == 51144830);
|
||||
CHECK(bon8_size == 45942080);
|
||||
CHECK(bson_size == 56008520);
|
||||
CHECK(cbor_size == 46187320);
|
||||
CHECK(msgpack_size == 46158575);
|
||||
CHECK(ubjson_1_size == 50710965);
|
||||
CHECK(ubjson_2_size == 51144830);
|
||||
CHECK(ubjson_3_size == 49861422);
|
||||
|
||||
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
|
||||
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(96.576));
|
||||
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(97.402));
|
||||
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(97.402));
|
||||
CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(87.494));
|
||||
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(106.665));
|
||||
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(87.961));
|
||||
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(87.906));
|
||||
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(96.576));
|
||||
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(97.402));
|
||||
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(94.958));
|
||||
}
|
||||
|
||||
@@ -9,14 +9,6 @@
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
@@ -4498,33 +4490,3 @@ TEST_CASE("BJData roundtrips" * doctest::skip())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("issue #5648 - from_bjdata(ptr, len) must read len bytes, not treat ptr as a C string")
|
||||
{
|
||||
// to_bjdata() encodes the integer 0 as the two bytes 'i' 0x00 (a BJData
|
||||
// type marker followed by the value byte 0x00), so the packed data
|
||||
// below contains a 0x00 byte before its end.
|
||||
const json j = {{"a", 0}};
|
||||
const std::vector<std::uint8_t> packed = json::to_bjdata(j);
|
||||
bool contains_nul = false;
|
||||
for (const auto byte : packed)
|
||||
{
|
||||
contains_nul |= (byte == 0x00);
|
||||
}
|
||||
REQUIRE(contains_nul);
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
// before the fix, from_bjdata had no (ptr, len) overload, so this call
|
||||
// bound to from_bjdata(InputType&&, bool strict) instead: ptr was read
|
||||
// as a NUL-terminated C string (stopping at the embedded 0x00 byte), and
|
||||
// len was silently converted to the strict flag. The deprecated
|
||||
// overload added for this issue forwards to from_bjdata(ptr, ptr + len,
|
||||
// ...) instead, like from_ubjson's deprecated (ptr, len) overload does.
|
||||
json result;
|
||||
CHECK_NOTHROW(result = json::from_bjdata(packed.data(), packed.size()));
|
||||
CHECK(result == j);
|
||||
|
||||
// len must not collapse into the strict flag either
|
||||
CHECK(json::from_bjdata(packed.data(), packed.size(), false) == j);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -8,14 +8,6 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
#ifdef JSON_TEST_NO_GLOBAL_UDLS
|
||||
@@ -1022,36 +1014,6 @@ TEST_CASE("BON8 roundtrips" * doctest::skip())
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("issue #5648 - from_bon8(ptr, len) must read len bytes, not treat ptr as a C string")
|
||||
{
|
||||
// to_bon8() encodes the integer 40 as the two bytes 0xC2 0x00 (a BON8
|
||||
// lead byte followed by a continuation byte of 0x00), so the packed data
|
||||
// below contains a 0x00 byte before its end.
|
||||
const json j = {{"a", 40}, {"b", "x"}};
|
||||
const std::vector<std::uint8_t> packed = json::to_bon8(j);
|
||||
bool contains_nul = false;
|
||||
for (const auto byte : packed)
|
||||
{
|
||||
contains_nul |= (byte == 0x00);
|
||||
}
|
||||
REQUIRE(contains_nul);
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
// before the fix, from_bon8 had no (ptr, len) overload, so this call
|
||||
// bound to from_bon8(InputType&&, bool strict) instead: ptr was read as
|
||||
// a NUL-terminated C string (stopping at the embedded 0x00 byte), and
|
||||
// len was silently converted to the strict flag. The deprecated
|
||||
// overload added for this issue forwards to from_bon8(ptr, ptr + len,
|
||||
// ...) instead, like from_cbor's deprecated (ptr, len) overload does.
|
||||
json result;
|
||||
CHECK_NOTHROW(result = json::from_bon8(packed.data(), packed.size()));
|
||||
CHECK(result == j);
|
||||
|
||||
// len must not collapse into the strict flag either
|
||||
CHECK(json::from_bon8(packed.data(), packed.size(), false) == j);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
TEST_CASE("BON8 with std::byte")
|
||||
{
|
||||
|
||||
@@ -8,14 +8,6 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
@@ -1269,10 +1261,8 @@ TEST_CASE("BSON input that cannot be read is discarded by every overload")
|
||||
CHECK_THROWS_AS(_ = json::from_bson(input.begin(), input.end()), json::parse_error&);
|
||||
CHECK(json::from_bson(input, true, false).is_discarded());
|
||||
CHECK(json::from_bson(input.begin(), input.end(), true, false).is_discarded());
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
CHECK(json::from_bson(input.data(), input.size(), true, false).is_discarded());
|
||||
CHECK(json::from_bson({input.data(), input.size()}, true, false).is_discarded());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("BSON SAX parsing stops at every event")
|
||||
@@ -1750,7 +1740,6 @@ TEST_CASE("BSON roundtrips" * doctest::skip())
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": uint8_t* and size");
|
||||
// parse JSON file
|
||||
@@ -1765,7 +1754,6 @@ TEST_CASE("BSON roundtrips" * doctest::skip())
|
||||
// compare parsed JSON values
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": output to output adapters");
|
||||
|
||||
+14
-28
@@ -8,14 +8,6 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
@@ -2223,10 +2215,8 @@ TEST_CASE("CBOR input that cannot be read is discarded by every overload")
|
||||
CHECK_THROWS_AS(_ = json::from_cbor(input.begin(), input.end()), json::parse_error&);
|
||||
CHECK(json::from_cbor(input, true, false).is_discarded());
|
||||
CHECK(json::from_cbor(input.begin(), input.end(), true, false).is_discarded());
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
CHECK(json::from_cbor(input.data(), input.size(), true, false).is_discarded());
|
||||
CHECK(json::from_cbor({input.data(), input.size()}, true, false).is_discarded());
|
||||
#endif
|
||||
|
||||
// a string that ends early, read through iterators that are not
|
||||
// contiguous and have to be copied from one element at a time
|
||||
@@ -2623,14 +2613,12 @@ TEST_CASE("CBOR roundtrips" * doctest::skip())
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": uint8_t* and size");
|
||||
json j2;
|
||||
CHECK_NOTHROW(j2 = json::from_cbor({packed.data(), packed.size()}));
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": output to output adapters");
|
||||
@@ -3197,8 +3185,7 @@ TEST_CASE("Tagged values")
|
||||
// CBOR encodes negative integers as: result = -1 - n
|
||||
// For type 0x3B, n is an 8-byte uint64_t. Valid range for n with
|
||||
// the default int64_t is [0, INT64_MAX], producing results in [INT64_MIN, -1].
|
||||
// When n > INT64_MAX, the result exceeds int64_t range and is stored
|
||||
// as a floating-point number, as the lexer does for JSON text.
|
||||
// When n > INT64_MAX, the result exceeds int64_t range and is rejected.
|
||||
|
||||
SECTION("n = 0 is valid (result = -1)")
|
||||
{
|
||||
@@ -3219,34 +3206,33 @@ TEST_CASE("Tagged values")
|
||||
CHECK(result.get<int64_t>() == (std::numeric_limits<int64_t>::min)());
|
||||
}
|
||||
|
||||
SECTION("n = INT64_MAX + 1 is stored as float")
|
||||
SECTION("n = INT64_MAX + 1 is rejected (overflow)")
|
||||
{
|
||||
// n = INT64_MAX + 1 (0x8000000000000000)
|
||||
// result = -1 - n = -9223372036854775809, which exceeds int64_t range;
|
||||
// the nearest double is -9223372036854775808.0
|
||||
// result = -1 - n = -9223372036854775809, which exceeds int64_t range
|
||||
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
const auto result = json::from_cbor(input);
|
||||
CHECK(result.is_number_float());
|
||||
CHECK(result.get<double>() == -9223372036854775808.0);
|
||||
CHECK(result == json::parse("-9223372036854775809"));
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input),
|
||||
"[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow",
|
||||
json::parse_error);
|
||||
}
|
||||
|
||||
SECTION("n = UINT64_MAX is stored as float")
|
||||
SECTION("n = UINT64_MAX is rejected (overflow)")
|
||||
{
|
||||
// n = UINT64_MAX (0xFFFFFFFFFFFFFFFF)
|
||||
// result = -1 - n = -18446744073709551616, which exceeds int64_t range
|
||||
const std::vector<uint8_t> input = {0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
const auto result = json::from_cbor(input);
|
||||
CHECK(result.is_number_float());
|
||||
CHECK(result.get<double>() == -18446744073709551616.0);
|
||||
CHECK(result == json::parse("-18446744073709551616"));
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input),
|
||||
"[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow",
|
||||
json::parse_error);
|
||||
}
|
||||
|
||||
SECTION("overflow with allow_exceptions=false is not an error")
|
||||
SECTION("overflow with allow_exceptions=false returns discarded")
|
||||
{
|
||||
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
const auto result = json::from_cbor(input, true, false);
|
||||
CHECK(result.is_number_float());
|
||||
CHECK(result.is_discarded());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -191,10 +191,8 @@ TEST_CASE("value conversion")
|
||||
{
|
||||
enum class bool_enum : bool { off, on };
|
||||
|
||||
// the extra parentheses keep doctest from printing the enum via its
|
||||
// underlying type, which MSVC 2015 reports as C4800
|
||||
CHECK((json(bool_enum::off).get<bool_enum>() == bool_enum::off));
|
||||
CHECK((json(bool_enum::on).get<bool_enum>() == bool_enum::on));
|
||||
CHECK(json(bool_enum::off).get<bool_enum>() == bool_enum::off);
|
||||
CHECK(json(bool_enum::on).get<bool_enum>() == bool_enum::on);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -853,11 +851,8 @@ TEST_CASE("std::optional")
|
||||
CHECK_THROWS_WITH_AS(json(opt), "cannot serialize throwing_to_json_type", std::runtime_error&);
|
||||
|
||||
// the conversion is noexcept exactly when converting the contained value is
|
||||
// (except with MSVC 2017, where it is never noexcept, see to_json.hpp)
|
||||
#if !defined(_MSC_VER) || defined(__clang__) || _MSC_VER >= 1920
|
||||
static_assert(!std::is_nothrow_constructible<json, const std::optional<throwing_to_json_type>&>::value);
|
||||
static_assert(std::is_nothrow_constructible<json, const std::optional<int>&>::value);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,229 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// This file tests the opt-in JSON_DELETE_DEPRECATED_FUNCTIONS, so it defines the
|
||||
// macro itself instead of relying on the build configuration.
|
||||
#ifdef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
#endif
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 1
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename...>
|
||||
struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
// A deleted function is still found by overload resolution, but naming it in an
|
||||
// unevaluated operand is ill-formed, so each of the following traits is false
|
||||
// exactly if the expression selects a deleted (or no) function.
|
||||
#define JSON_TEST_DETECT(name, expr) \
|
||||
template<typename J, typename = void> \
|
||||
struct name : std::false_type {}; \
|
||||
template<typename J> \
|
||||
struct name<J, void_t<decltype(expr)>> : std::true_type {}
|
||||
|
||||
using ptr_t = const std::uint8_t*;
|
||||
|
||||
JSON_TEST_DETECT(from_cbor_ptr_len, J::from_cbor(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
||||
JSON_TEST_DETECT(from_msgpack_ptr_len, J::from_msgpack(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
||||
JSON_TEST_DETECT(from_ubjson_ptr_len, J::from_ubjson(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
||||
JSON_TEST_DETECT(from_bjdata_ptr_len, J::from_bjdata(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
||||
JSON_TEST_DETECT(from_bon8_ptr_len, J::from_bon8(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
||||
JSON_TEST_DETECT(from_bson_ptr_len, J::from_bson(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
||||
|
||||
JSON_TEST_DETECT(from_cbor_ptr_ptr, J::from_cbor(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
||||
JSON_TEST_DETECT(from_msgpack_ptr_ptr, J::from_msgpack(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
||||
JSON_TEST_DETECT(from_ubjson_ptr_ptr, J::from_ubjson(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
||||
JSON_TEST_DETECT(from_bjdata_ptr_ptr, J::from_bjdata(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
||||
JSON_TEST_DETECT(from_bon8_ptr_ptr, J::from_bon8(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
||||
JSON_TEST_DETECT(from_bson_ptr_ptr, J::from_bson(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
||||
|
||||
JSON_TEST_DETECT(from_cbor_init_list, J::from_cbor({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
||||
JSON_TEST_DETECT(from_msgpack_init_list, J::from_msgpack({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
||||
JSON_TEST_DETECT(from_ubjson_init_list, J::from_ubjson({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
||||
JSON_TEST_DETECT(from_bson_init_list, J::from_bson({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
||||
|
||||
JSON_TEST_DETECT(parse_init_list, J::parse({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
||||
JSON_TEST_DETECT(accept_init_list, J::accept({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
||||
JSON_TEST_DETECT(sax_parse_init_list, J::sax_parse({std::declval<ptr_t>(), std::declval<std::size_t>()}, std::declval<nlohmann::json_sax<J>*>()));
|
||||
JSON_TEST_DETECT(parse_ptr_ptr, J::parse(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
||||
|
||||
JSON_TEST_DETECT(iterator_wrapper, J::iterator_wrapper(std::declval<J&>()));
|
||||
JSON_TEST_DETECT(iterator_wrapper_const, J::iterator_wrapper(std::declval<const J&>()));
|
||||
JSON_TEST_DETECT(items, std::declval<J&>().items());
|
||||
|
||||
JSON_TEST_DETECT(json_ltlt_istream, std::declval<J&>() << std::declval<std::istream&>());
|
||||
JSON_TEST_DETECT(istream_gtgt_json, std::declval<std::istream&>() >> std::declval<J&>());
|
||||
JSON_TEST_DETECT(json_gtgt_ostream, std::declval<const J&>() >> std::declval<std::ostream&>());
|
||||
JSON_TEST_DETECT(ostream_ltlt_json, std::declval<std::ostream&>() << std::declval<const J&>());
|
||||
|
||||
JSON_TEST_DETECT(ptr_eq_ptr, std::declval<const typename J::json_pointer&>() == std::declval<const typename J::json_pointer&>());
|
||||
JSON_TEST_DETECT(ptr_ne_ptr, std::declval<const typename J::json_pointer&>() != std::declval<const typename J::json_pointer&>());
|
||||
JSON_TEST_DETECT(ptr_eq_string, std::declval<const typename J::json_pointer&>() == std::declval<const std::string&>());
|
||||
JSON_TEST_DETECT(string_eq_ptr, std::declval<const std::string&>() == std::declval<const typename J::json_pointer&>());
|
||||
JSON_TEST_DETECT(ptr_eq_c_string, std::declval<const typename J::json_pointer&>() == std::declval<const char*>());
|
||||
JSON_TEST_DETECT(c_string_eq_ptr, std::declval<const char*>() == std::declval<const typename J::json_pointer&>());
|
||||
JSON_TEST_DETECT(ptr_ne_string, std::declval<const typename J::json_pointer&>() != std::declval<const std::string&>());
|
||||
JSON_TEST_DETECT(string_ne_ptr, std::declval<const std::string&>() != std::declval<const typename J::json_pointer&>());
|
||||
JSON_TEST_DETECT(ptr_to_string, std::declval<const typename J::json_pointer&>().to_string());
|
||||
|
||||
// json_pointer with a basic_json type as template argument
|
||||
template<typename J>
|
||||
using legacy_ptr_t = const nlohmann::json_pointer<J>& ;
|
||||
JSON_TEST_DETECT(legacy_ptr_value, std::declval<const J&>().value(std::declval<legacy_ptr_t<J>>(), 0));
|
||||
JSON_TEST_DETECT(legacy_ptr_value_rvalue, std::declval<const J&>().value(std::declval<legacy_ptr_t<J>>(), std::string()));
|
||||
JSON_TEST_DETECT(legacy_ptr_contains, std::declval<const J&>().contains(std::declval<legacy_ptr_t<J>>()));
|
||||
JSON_TEST_DETECT(legacy_ptr_subscript, std::declval<J&>()[std::declval<legacy_ptr_t<J>>()]);
|
||||
JSON_TEST_DETECT(legacy_ptr_subscript_const, std::declval<const J&>()[std::declval<legacy_ptr_t<J>>()]);
|
||||
JSON_TEST_DETECT(legacy_ptr_at, std::declval<J&>().at(std::declval<legacy_ptr_t<J>>()));
|
||||
JSON_TEST_DETECT(legacy_ptr_at_const, std::declval<const J&>().at(std::declval<legacy_ptr_t<J>>()));
|
||||
JSON_TEST_DETECT(ptr_value, std::declval<const J&>().value(std::declval<const typename J::json_pointer&>(), 0));
|
||||
JSON_TEST_DETECT(ptr_at, std::declval<J&>().at(std::declval<const typename J::json_pointer&>()));
|
||||
|
||||
#undef JSON_TEST_DETECT
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("JSON_DELETE_DEPRECATED_FUNCTIONS")
|
||||
{
|
||||
// MSVC 2015 does not treat selecting a deleted function in decltype as a
|
||||
// substitution failure, so the traits cannot tell deleted functions apart
|
||||
// there; calling them still fails to compile
|
||||
#if !(defined(_MSC_VER) && _MSC_VER < 1910)
|
||||
SECTION("from_* with a pointer and a length")
|
||||
{
|
||||
// the overloads are deleted rather than removed, so the length cannot
|
||||
// silently bind to the strict parameter of from_*(InputType&&, bool)
|
||||
CHECK_FALSE(from_cbor_ptr_len<json>::value);
|
||||
CHECK_FALSE(from_msgpack_ptr_len<json>::value);
|
||||
CHECK_FALSE(from_ubjson_ptr_len<json>::value);
|
||||
CHECK_FALSE(from_bjdata_ptr_len<json>::value);
|
||||
CHECK_FALSE(from_bon8_ptr_len<json>::value);
|
||||
CHECK_FALSE(from_bson_ptr_len<json>::value);
|
||||
|
||||
// the replacement
|
||||
CHECK(from_cbor_ptr_ptr<json>::value);
|
||||
CHECK(from_msgpack_ptr_ptr<json>::value);
|
||||
CHECK(from_ubjson_ptr_ptr<json>::value);
|
||||
CHECK(from_bjdata_ptr_ptr<json>::value);
|
||||
CHECK(from_bon8_ptr_ptr<json>::value);
|
||||
CHECK(from_bson_ptr_ptr<json>::value);
|
||||
}
|
||||
|
||||
SECTION("initializer lists of a pointer and a length")
|
||||
{
|
||||
CHECK_FALSE(from_cbor_init_list<json>::value);
|
||||
CHECK_FALSE(from_msgpack_init_list<json>::value);
|
||||
CHECK_FALSE(from_ubjson_init_list<json>::value);
|
||||
CHECK_FALSE(from_bson_init_list<json>::value);
|
||||
CHECK_FALSE(parse_init_list<json>::value);
|
||||
CHECK_FALSE(accept_init_list<json>::value);
|
||||
CHECK_FALSE(sax_parse_init_list<json>::value);
|
||||
|
||||
// the replacement
|
||||
CHECK(parse_ptr_ptr<json>::value);
|
||||
}
|
||||
|
||||
SECTION("iterator_wrapper")
|
||||
{
|
||||
CHECK_FALSE(iterator_wrapper<json>::value);
|
||||
CHECK_FALSE(iterator_wrapper_const<json>::value);
|
||||
|
||||
// the replacement
|
||||
CHECK(items<json>::value);
|
||||
}
|
||||
|
||||
SECTION("stream operators with reversed operands")
|
||||
{
|
||||
CHECK_FALSE(json_ltlt_istream<json>::value);
|
||||
CHECK_FALSE(json_gtgt_ostream<json>::value);
|
||||
|
||||
// the replacement
|
||||
CHECK(istream_gtgt_json<json>::value);
|
||||
CHECK(ostream_ltlt_json<json>::value);
|
||||
}
|
||||
|
||||
SECTION("json_pointer")
|
||||
{
|
||||
// conversion to a string
|
||||
CHECK_FALSE(std::is_constructible<std::string, json::json_pointer>::value);
|
||||
CHECK_FALSE(std::is_convertible<json::json_pointer, std::string>::value);
|
||||
|
||||
// comparison with a string
|
||||
CHECK_FALSE(ptr_eq_string<json>::value);
|
||||
CHECK_FALSE(string_eq_ptr<json>::value);
|
||||
CHECK_FALSE(ptr_eq_c_string<json>::value);
|
||||
CHECK_FALSE(c_string_eq_ptr<json>::value);
|
||||
CHECK_FALSE(ptr_ne_string<json>::value);
|
||||
CHECK_FALSE(string_ne_ptr<json>::value);
|
||||
|
||||
// the replacement
|
||||
CHECK(ptr_to_string<json>::value);
|
||||
CHECK(ptr_eq_ptr<json>::value);
|
||||
CHECK(ptr_ne_ptr<json>::value);
|
||||
}
|
||||
|
||||
SECTION("json_pointer with a basic_json type as template argument")
|
||||
{
|
||||
CHECK_FALSE(legacy_ptr_value<json>::value);
|
||||
CHECK_FALSE(legacy_ptr_value_rvalue<json>::value);
|
||||
CHECK_FALSE(legacy_ptr_contains<json>::value);
|
||||
CHECK_FALSE(legacy_ptr_subscript<json>::value);
|
||||
CHECK_FALSE(legacy_ptr_subscript_const<json>::value);
|
||||
CHECK_FALSE(legacy_ptr_at<json>::value);
|
||||
CHECK_FALSE(legacy_ptr_at_const<json>::value);
|
||||
|
||||
// the replacement
|
||||
CHECK(ptr_value<json>::value);
|
||||
CHECK(ptr_at<json>::value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SECTION("the non-deprecated functions still work")
|
||||
{
|
||||
const json j = {{"a", {1, 2}}};
|
||||
const auto cbor = json::to_cbor(j);
|
||||
CHECK(json::from_cbor(cbor.data(), cbor.data() + cbor.size()) == j);
|
||||
|
||||
const json::json_pointer ptr("/a/1");
|
||||
CHECK(ptr == json::json_pointer("/a/1"));
|
||||
CHECK(ptr.to_string() == "/a/1");
|
||||
CHECK(j[ptr] == 2);
|
||||
CHECK(j.at(ptr) == 2);
|
||||
CHECK(j.value(ptr, 0) == 2);
|
||||
CHECK(j.contains(ptr));
|
||||
|
||||
std::ostringstream os;
|
||||
os << j;
|
||||
CHECK(os.str() == R"({"a":[1,2]})");
|
||||
std::istringstream is(os.str());
|
||||
json parsed;
|
||||
is >> parsed;
|
||||
CHECK(parsed == j);
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,6 @@
|
||||
#define JSON_TEST_STRICT_NUL_HANDLING_ENABLED 1
|
||||
#endif
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
#ifdef JSON_TEST_NO_GLOBAL_UDLS
|
||||
@@ -299,7 +291,6 @@ TEST_CASE("deserialization")
|
||||
}));
|
||||
}
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
SECTION("operator<<")
|
||||
{
|
||||
std::stringstream ss;
|
||||
@@ -308,7 +299,6 @@ TEST_CASE("deserialization")
|
||||
j << ss;
|
||||
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("operator>>")
|
||||
{
|
||||
@@ -433,7 +423,6 @@ TEST_CASE("deserialization")
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse(nullptr), "[json.exception.parse_error.101] parse error: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
||||
}
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
SECTION("operator<<")
|
||||
{
|
||||
std::stringstream ss;
|
||||
@@ -441,7 +430,6 @@ TEST_CASE("deserialization")
|
||||
json j;
|
||||
CHECK_THROWS_WITH_AS(j << ss, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("operator>>")
|
||||
{
|
||||
@@ -1160,9 +1148,9 @@ TEST_CASE("deserialization")
|
||||
{
|
||||
std::istringstream s(bom + "123 456");
|
||||
json j;
|
||||
s >> j;
|
||||
j << s;
|
||||
CHECK(j == 123);
|
||||
s >> j;
|
||||
j << s;
|
||||
CHECK(j == 456);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1551,10 +1551,6 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j
|
||||
// count(0) used to compile and then crash instead of failing to compile
|
||||
using nlohmann::detail::is_detected;
|
||||
|
||||
// MSVC 2015 does not treat selecting a deleted function in decltype as
|
||||
// a substitution failure, so it detects the deleted overloads as
|
||||
// callable; calling them still fails to compile
|
||||
#if !(defined(_MSC_VER) && _MSC_VER < 1910)
|
||||
CHECK_FALSE(is_detected<can_call_find_with_0, Json&>::value);
|
||||
CHECK_FALSE(is_detected<can_call_find_with_0, const Json&>::value);
|
||||
CHECK_FALSE(is_detected<can_call_count_with_0, Json&>::value);
|
||||
@@ -1566,7 +1562,6 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j
|
||||
|
||||
// another integral literal type must be rejected as well, not just int
|
||||
CHECK_FALSE(is_detected<can_call_contains_with_0L, Json&>::value);
|
||||
#endif
|
||||
|
||||
// the valid overloads must remain callable
|
||||
CHECK(is_detected<can_call_find, Json&, const char*>::value);
|
||||
|
||||
@@ -1,246 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// skip tests if JSON_DisableEnumSerialization=ON (#4384)
|
||||
#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1)
|
||||
#define SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
||||
#endif
|
||||
|
||||
// This file tests the opt-in JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, so it defines
|
||||
// the macro itself rather than relying on a -D flag, and runs in every build.
|
||||
// The default behavior is tested in unit-enum_keyed_maps_default.cpp.
|
||||
#ifdef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#endif
|
||||
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 1
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
using nlohmann::ordered_json;
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define STRINGIZE_EX(x) #x
|
||||
#define STRINGIZE(x) STRINGIZE_EX(x)
|
||||
|
||||
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
// std::hash is only required for enums since C++14
|
||||
struct enum_hash
|
||||
{
|
||||
template<typename T>
|
||||
std::size_t operator()(T t) const noexcept
|
||||
{
|
||||
return static_cast<std::size_t>(t);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// the example from #4378
|
||||
enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
||||
{
|
||||
TS_STOPPED,
|
||||
TS_RUNNING,
|
||||
TS_COMPLETED,
|
||||
TS_INVALID = -1,
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState,
|
||||
{
|
||||
{TS_INVALID, nullptr},
|
||||
{TS_STOPPED, "stopped"},
|
||||
{TS_RUNNING, "running"},
|
||||
{TS_COMPLETED, "completed"},
|
||||
})
|
||||
|
||||
enum class color {red, green, blue}; // blue is not mapped and falls back to "red"
|
||||
|
||||
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(color,
|
||||
{
|
||||
{color::red, "red"},
|
||||
{color::green, "green"},
|
||||
})
|
||||
|
||||
enum class strict_color {red, green, blue}; // blue is not mapped
|
||||
|
||||
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_color,
|
||||
{
|
||||
{strict_color::red, "red"},
|
||||
{strict_color::green, "green"},
|
||||
})
|
||||
|
||||
enum class digit {zero, one};
|
||||
|
||||
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(digit,
|
||||
{
|
||||
{digit::zero, 0},
|
||||
{digit::one, 1},
|
||||
})
|
||||
|
||||
#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
||||
enum class plain {zero, one}; // serialized as integer
|
||||
#endif
|
||||
|
||||
TEST_CASE("JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS")
|
||||
{
|
||||
SECTION("the macro is part of the ABI tag")
|
||||
{
|
||||
const std::string ns = STRINGIZE(NLOHMANN_JSON_NAMESPACE);
|
||||
CHECK(ns.find("_ekmo") != std::string::npos);
|
||||
}
|
||||
|
||||
SECTION("std::map (#4378)")
|
||||
{
|
||||
using task_map = std::map<TaskState, std::string>;
|
||||
const task_map m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
const json j = m;
|
||||
CHECK(j == json::parse(R"({"stopped":"aa","completed":"bb"})"));
|
||||
CHECK(j.get<task_map>() == m);
|
||||
|
||||
json j2;
|
||||
j2["x"] = m;
|
||||
CHECK(j2.dump() == R"({"x":{"completed":"bb","stopped":"aa"}})");
|
||||
}
|
||||
|
||||
SECTION("std::map with custom comparator")
|
||||
{
|
||||
using task_map = std::map<TaskState, int, std::greater<TaskState>>;
|
||||
const task_map m = {{TS_STOPPED, 1}, {TS_RUNNING, 2}};
|
||||
const json j = m;
|
||||
CHECK(j == json::parse(R"({"stopped":1,"running":2})"));
|
||||
CHECK(j.get<task_map>() == m);
|
||||
}
|
||||
|
||||
SECTION("std::unordered_map")
|
||||
{
|
||||
using task_map = std::unordered_map<TaskState, int, enum_hash>;
|
||||
const task_map m = {{TS_STOPPED, 1}, {TS_RUNNING, 2}};
|
||||
const json j = m;
|
||||
CHECK(j == json::parse(R"({"stopped":1,"running":2})"));
|
||||
CHECK(j.get<task_map>() == m);
|
||||
}
|
||||
|
||||
SECTION("nested maps")
|
||||
{
|
||||
using nested_map = std::map<color, std::map<TaskState, int>>;
|
||||
const nested_map m = {{color::green, {{TS_RUNNING, 1}}}, {color::red, {}}};
|
||||
const json j = m;
|
||||
CHECK(j == json::parse(R"({"green":{"running":1},"red":{}})"));
|
||||
CHECK(j.get<nested_map>() == m);
|
||||
}
|
||||
|
||||
SECTION("ordered_json keeps the order of the map")
|
||||
{
|
||||
using task_map = std::map<TaskState, int>;
|
||||
const task_map m = {{TS_STOPPED, 1}, {TS_RUNNING, 2}, {TS_COMPLETED, 3}};
|
||||
const ordered_json j = m;
|
||||
CHECK(j.dump() == R"({"stopped":1,"running":2,"completed":3})");
|
||||
CHECK(j.get<task_map>() == m);
|
||||
}
|
||||
|
||||
SECTION("empty map")
|
||||
{
|
||||
const json j = std::map<TaskState, int>();
|
||||
CHECK(j.is_object());
|
||||
CHECK(j.empty());
|
||||
}
|
||||
|
||||
SECTION("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT")
|
||||
{
|
||||
using color_map = std::map<strict_color, int>;
|
||||
const color_map m = {{strict_color::red, 1}, {strict_color::green, 2}};
|
||||
const json j = m;
|
||||
CHECK(j == json::parse(R"({"red":1,"green":2})"));
|
||||
CHECK(j.get<color_map>() == m);
|
||||
|
||||
const color_map unmapped = {{strict_color::blue, 1}};
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = unmapped,
|
||||
"[json.exception.out_of_range.410] enum value out of range for strict_color", json::out_of_range&);
|
||||
}
|
||||
|
||||
SECTION("arrays of [key, value] pairs are still read")
|
||||
{
|
||||
using task_map = std::map<TaskState, int>;
|
||||
const task_map m = {{TS_STOPPED, 1}};
|
||||
CHECK(json::parse(R"([["stopped",1]])").get<task_map>() == m);
|
||||
}
|
||||
|
||||
SECTION("other containers are not affected")
|
||||
{
|
||||
const std::vector<std::pair<TaskState, int>> pairs = {{TS_STOPPED, 1}};
|
||||
const std::map<std::string, TaskState> string_keys = {{"a", TS_STOPPED}};
|
||||
const std::map<int, int> int_keys = {{1, 2}};
|
||||
CHECK(json(pairs) == json::parse(R"([["stopped",1]])"));
|
||||
CHECK(json(string_keys) == json::parse(R"({"a":"stopped"})"));
|
||||
CHECK(json(int_keys) == json::parse("[[1,2]]"));
|
||||
}
|
||||
|
||||
SECTION("maps with non-unique keys are still stored as arrays of pairs")
|
||||
{
|
||||
const std::multimap<TaskState, int> mm = {{TS_STOPPED, 1}, {TS_STOPPED, 2}};
|
||||
const std::unordered_multimap<TaskState, int, enum_hash> umm = {{TS_RUNNING, 3}, {TS_RUNNING, 3}};
|
||||
CHECK(json(mm) == json::parse(R"([["stopped",1],["stopped",2]])"));
|
||||
CHECK(json(umm) == json::parse(R"([["running",3],["running",3]])"));
|
||||
}
|
||||
|
||||
SECTION("keys that do not serialize to strings")
|
||||
{
|
||||
const std::map<TaskState, int> null_key = {{TS_INVALID, 1}};
|
||||
const std::map<digit, int> number_key = {{digit::zero, 1}};
|
||||
json j = "unchanged";
|
||||
|
||||
// mapped to null
|
||||
CHECK_THROWS_WITH_AS(j = null_key,
|
||||
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
||||
|
||||
// mapped to a number
|
||||
CHECK_THROWS_WITH_AS(j = number_key,
|
||||
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
||||
|
||||
#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
||||
// enum without NLOHMANN_JSON_SERIALIZE_ENUM
|
||||
const std::map<plain, int> plain_key = {{plain::zero, 1}};
|
||||
CHECK_THROWS_WITH_AS(j = plain_key,
|
||||
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
||||
#endif
|
||||
|
||||
CHECK(j == "unchanged");
|
||||
}
|
||||
|
||||
SECTION("keys that serialize to the same string")
|
||||
{
|
||||
const std::map<color, int> m = {{color::red, 1}, {color::blue, 2}};
|
||||
json j = "unchanged";
|
||||
|
||||
// color::blue is not mapped and falls back to "red"
|
||||
CHECK_THROWS_WITH_AS(j = m,
|
||||
"[json.exception.type_error.318] duplicate object key 'red'", json::type_error&);
|
||||
|
||||
CHECK(j == "unchanged");
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -1,144 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// This file tests maps with enum keys with the default setting of
|
||||
// JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS (or whatever a -D flag sets it to).
|
||||
// unit-enum_keyed_maps.cpp tests JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS=1.
|
||||
// These tests are not part of unit-conversions.cpp, because that object file
|
||||
// is already too big for the MinGW linker of some compilers.
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
enum class cards {kreuz, pik, herz, karo};
|
||||
|
||||
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(cards,
|
||||
{
|
||||
{cards::kreuz, "kreuz"},
|
||||
{cards::pik, "pik"},
|
||||
{cards::herz, "herz"},
|
||||
{cards::karo, "karo"}
|
||||
})
|
||||
|
||||
enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
||||
{
|
||||
TS_STOPPED,
|
||||
TS_RUNNING,
|
||||
TS_COMPLETED,
|
||||
TS_INVALID = -1,
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState,
|
||||
{
|
||||
{TS_INVALID, nullptr},
|
||||
{TS_STOPPED, "stopped"},
|
||||
{TS_RUNNING, "running"},
|
||||
{TS_COMPLETED, "completed"},
|
||||
})
|
||||
|
||||
enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping
|
||||
|
||||
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards,
|
||||
{
|
||||
{strict_cards::kreuz, "kreuz"},
|
||||
{strict_cards::pik, "pik"},
|
||||
{strict_cards::herz, "herz"},
|
||||
{strict_cards::karo, "karo"}
|
||||
})
|
||||
|
||||
namespace
|
||||
{
|
||||
// std::hash is only required for enums since C++14
|
||||
struct enum_hash
|
||||
{
|
||||
template<typename T>
|
||||
std::size_t operator()(T t) const noexcept
|
||||
{
|
||||
return static_cast<std::size_t>(t);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// see unit-enum_keyed_maps.cpp for JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS=1
|
||||
TEST_CASE("maps with enum keys")
|
||||
{
|
||||
using task_map = std::map<TaskState, std::string>;
|
||||
using task_umap = std::unordered_map<TaskState, std::string, enum_hash>;
|
||||
using task_gmap = std::map<TaskState, std::string, std::greater<TaskState>>;
|
||||
using nested_map = std::map<cards, std::map<TaskState, int>>;
|
||||
using strict_map = std::map<strict_cards, int>;
|
||||
using int_map = std::map<int, int>;
|
||||
using int_umap = std::unordered_map<int, int>;
|
||||
|
||||
const task_map m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
|
||||
#if !JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
SECTION("stored as array of pairs")
|
||||
{
|
||||
CHECK(json(m) == json::parse(R"([["stopped","aa"],["completed","bb"]])"));
|
||||
CHECK(json(task_umap {{TS_RUNNING, "cc"}}) == json::parse(R"([["running","cc"]])"));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("read from array of pairs")
|
||||
{
|
||||
CHECK(json::parse(R"([["stopped","aa"],["completed","bb"]])").get<task_map>() == m);
|
||||
}
|
||||
|
||||
SECTION("read from object (#4378)")
|
||||
{
|
||||
const json j = json::parse(R"({"stopped":"aa","completed":"bb"})");
|
||||
CHECK(j.get<task_map>() == m);
|
||||
CHECK(j.get<task_umap>() == task_umap(m.begin(), m.end()));
|
||||
CHECK(j.get<task_gmap>() == task_gmap(m.begin(), m.end()));
|
||||
CHECK(json::parse(R"({"kreuz":{"stopped":1}})").get<nested_map>() == nested_map {{cards::kreuz, {{TS_STOPPED, 1}}}});
|
||||
CHECK(nlohmann::ordered_json::parse(R"({"stopped":"aa","completed":"bb"})").get<task_map>() == m);
|
||||
|
||||
// object keys go through the enum's from_json
|
||||
strict_map sm;
|
||||
CHECK_THROWS_WITH_AS(json::parse(R"({"what?":1})").get_to(sm),
|
||||
"[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&);
|
||||
}
|
||||
|
||||
SECTION("objects are only read for enum keys")
|
||||
{
|
||||
// built rather than parsed, so that the messages do not gain a byte
|
||||
// range with JSON_DIAGNOSTIC_POSITIONS
|
||||
const json j = {{"1", 2}};
|
||||
int_map im;
|
||||
int_umap ium;
|
||||
CHECK_THROWS_WITH_AS(j.get_to(im),
|
||||
"[json.exception.type_error.302] type must be array, but is object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j.get_to(ium),
|
||||
"[json.exception.type_error.302] type must be array, but is object", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("other types are rejected")
|
||||
{
|
||||
task_map tm;
|
||||
CHECK_THROWS_WITH_AS(json("stopped").get_to(tm),
|
||||
"[json.exception.type_error.302] type must be array, but is string", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -8,14 +8,6 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
@@ -27,7 +19,6 @@ DOCTEST_GCC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wrange-loop-construct")
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
TEST_CASE("iterator_wrapper")
|
||||
{
|
||||
SECTION("object")
|
||||
@@ -724,7 +715,6 @@ TEST_CASE("iterator_wrapper")
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("items()")
|
||||
{
|
||||
|
||||
@@ -9,14 +9,6 @@
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
#ifdef JSON_TEST_NO_GLOBAL_UDLS
|
||||
@@ -572,9 +564,7 @@ TEST_CASE("JSON pointers")
|
||||
std::stringstream ss;
|
||||
ss << ptr;
|
||||
CHECK(ptr.to_string() == ptr_str);
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
CHECK(std::string(ptr) == ptr_str);
|
||||
#endif
|
||||
CHECK(ss.str() == ptr_str);
|
||||
}
|
||||
}
|
||||
@@ -754,6 +744,8 @@ TEST_CASE("JSON pointers")
|
||||
|
||||
SECTION("equality comparison")
|
||||
{
|
||||
const char* ptr_cpstring = "/foo/bar";
|
||||
const char ptr_castring[] = "/foo/bar"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
|
||||
std::string ptr_string{"/foo/bar"};
|
||||
auto ptr1 = json::json_pointer(ptr_string);
|
||||
auto ptr2 = json::json_pointer(ptr_string);
|
||||
@@ -763,12 +755,6 @@ TEST_CASE("JSON pointers")
|
||||
|
||||
CHECK(ptr1 == ptr2);
|
||||
|
||||
CHECK_FALSE(ptr1 != ptr2);
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
const char* ptr_cpstring = "/foo/bar";
|
||||
const char ptr_castring[] = "/foo/bar"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
|
||||
|
||||
CHECK(ptr1 == "/foo/bar");
|
||||
CHECK(ptr1 == ptr_cpstring);
|
||||
CHECK(ptr1 == ptr_castring);
|
||||
@@ -779,6 +765,8 @@ TEST_CASE("JSON pointers")
|
||||
CHECK(ptr_castring == ptr1);
|
||||
CHECK(ptr_string == ptr1);
|
||||
|
||||
CHECK_FALSE(ptr1 != ptr2);
|
||||
|
||||
CHECK_FALSE(ptr1 != "/foo/bar");
|
||||
CHECK_FALSE(ptr1 != ptr_cpstring);
|
||||
CHECK_FALSE(ptr1 != ptr_castring);
|
||||
@@ -800,7 +788,6 @@ TEST_CASE("JSON pointers")
|
||||
CHECK_THROWS_WITH_AS("/~~" == ptr1,
|
||||
"[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("less-than comparison")
|
||||
@@ -852,7 +839,6 @@ TEST_CASE("JSON pointers")
|
||||
json_ptr_j ptr_j{ptr_string};
|
||||
json_ptr_oj ptr_oj{ptr_string};
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
CHECK(j.contains(ptr));
|
||||
CHECK(j.contains(ptr_j));
|
||||
CHECK(j.contains(ptr_oj));
|
||||
@@ -865,7 +851,6 @@ TEST_CASE("JSON pointers")
|
||||
|
||||
CHECK(j.value(ptr, "x") == j.value(ptr_j, "x"));
|
||||
CHECK(j.value(ptr, "x") == j.value(ptr_oj, "x"));
|
||||
#endif
|
||||
|
||||
CHECK(ptr == ptr_j);
|
||||
CHECK(ptr == ptr_oj);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// This file contains the C++17-only part of unit-msgpack.cpp (std::byte
|
||||
// input). It is kept in a separate translation unit so the (much larger)
|
||||
// unit-msgpack.cpp does not need to be compiled and run a second time just
|
||||
// for this one test case (#5418).
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
// Test suite for verifying MessagePack handling with std::byte input
|
||||
TEST_CASE("MessagePack with std::byte")
|
||||
{
|
||||
|
||||
SECTION("std::byte compatibility")
|
||||
{
|
||||
SECTION("vector roundtrip")
|
||||
{
|
||||
json original =
|
||||
{
|
||||
{"name", "test"},
|
||||
{"value", 42},
|
||||
{"array", {1, 2, 3}}
|
||||
};
|
||||
|
||||
std::vector<uint8_t> temp = json::to_msgpack(original);
|
||||
// Convert the uint8_t vector to std::byte vector
|
||||
std::vector<std::byte> msgpack_data(temp.size());
|
||||
for (size_t i = 0; i < temp.size(); ++i)
|
||||
{
|
||||
msgpack_data[i] = std::byte(temp[i]);
|
||||
}
|
||||
// Deserialize from std::byte vector back to JSON
|
||||
json from_bytes;
|
||||
CHECK_NOTHROW(from_bytes = json::from_msgpack(msgpack_data));
|
||||
|
||||
CHECK(from_bytes == original);
|
||||
}
|
||||
|
||||
SECTION("empty vector")
|
||||
{
|
||||
const std::vector<std::byte> empty_data;
|
||||
CHECK_THROWS_WITH_AS([&]()
|
||||
{
|
||||
[[maybe_unused]] auto result = json::from_msgpack(empty_data);
|
||||
return true;
|
||||
}
|
||||
(),
|
||||
"[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input",
|
||||
json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("comparison with workaround")
|
||||
{
|
||||
json original =
|
||||
{
|
||||
{"string", "hello"},
|
||||
{"integer", 42},
|
||||
{"float", 3.14},
|
||||
{"boolean", true},
|
||||
{"null", nullptr},
|
||||
{"array", {1, 2, 3}},
|
||||
{"object", {{"key", "value"}}}
|
||||
};
|
||||
|
||||
std::vector<uint8_t> temp = json::to_msgpack(original);
|
||||
|
||||
std::vector<std::byte> msgpack_data(temp.size());
|
||||
for (size_t i = 0; i < temp.size(); ++i)
|
||||
{
|
||||
msgpack_data[i] = std::byte(temp[i]);
|
||||
}
|
||||
// Attempt direct deserialization using std::byte input
|
||||
const json direct_result = json::from_msgpack(msgpack_data);
|
||||
|
||||
// Test the workaround approach: reinterpret as unsigned char* and use iterator range
|
||||
const auto* const char_start = reinterpret_cast<unsigned char const*>(msgpack_data.data());
|
||||
const auto* const char_end = char_start + msgpack_data.size();
|
||||
json workaround_result = json::from_msgpack(char_start, char_end);
|
||||
|
||||
// Verify that the final deserialized JSON matches the original JSON
|
||||
CHECK(direct_result == workaround_result);
|
||||
CHECK(direct_result == original);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -8,14 +8,6 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
#ifdef JSON_TEST_NO_GLOBAL_UDLS
|
||||
@@ -1803,10 +1795,8 @@ TEST_CASE("MessagePack input that cannot be read is discarded by every overload"
|
||||
CHECK_THROWS_AS(_ = json::from_msgpack(input.begin(), input.end()), json::parse_error&);
|
||||
CHECK(json::from_msgpack(input, true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(input.begin(), input.end(), true, false).is_discarded());
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
CHECK(json::from_msgpack(input.data(), input.size(), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack({input.data(), input.size()}, true, false).is_discarded());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("MessagePack SAX parsing stops at every event")
|
||||
@@ -2109,14 +2099,12 @@ TEST_CASE("MessagePack roundtrips" * doctest::skip())
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": uint8_t* and size");
|
||||
json j2;
|
||||
CHECK_NOTHROW(j2 = json::from_msgpack({packed.data(), packed.size()}));
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": output to output adapters");
|
||||
@@ -2134,85 +2122,6 @@ TEST_CASE("MessagePack roundtrips" * doctest::skip())
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
// Test suite for verifying MessagePack handling with std::byte input
|
||||
TEST_CASE("MessagePack with std::byte")
|
||||
{
|
||||
|
||||
SECTION("std::byte compatibility")
|
||||
{
|
||||
SECTION("vector roundtrip")
|
||||
{
|
||||
json original =
|
||||
{
|
||||
{"name", "test"},
|
||||
{"value", 42},
|
||||
{"array", {1, 2, 3}}
|
||||
};
|
||||
|
||||
std::vector<uint8_t> temp = json::to_msgpack(original);
|
||||
// Convert the uint8_t vector to std::byte vector
|
||||
std::vector<std::byte> msgpack_data(temp.size());
|
||||
for (size_t i = 0; i < temp.size(); ++i)
|
||||
{
|
||||
msgpack_data[i] = std::byte(temp[i]);
|
||||
}
|
||||
// Deserialize from std::byte vector back to JSON
|
||||
json from_bytes;
|
||||
CHECK_NOTHROW(from_bytes = json::from_msgpack(msgpack_data));
|
||||
|
||||
CHECK(from_bytes == original);
|
||||
}
|
||||
|
||||
SECTION("empty vector")
|
||||
{
|
||||
const std::vector<std::byte> empty_data;
|
||||
CHECK_THROWS_WITH_AS([&]()
|
||||
{
|
||||
[[maybe_unused]] auto result = json::from_msgpack(empty_data);
|
||||
return true;
|
||||
}
|
||||
(),
|
||||
"[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input",
|
||||
json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("comparison with workaround")
|
||||
{
|
||||
json original =
|
||||
{
|
||||
{"string", "hello"},
|
||||
{"integer", 42},
|
||||
{"float", 3.14},
|
||||
{"boolean", true},
|
||||
{"null", nullptr},
|
||||
{"array", {1, 2, 3}},
|
||||
{"object", {{"key", "value"}}}
|
||||
};
|
||||
|
||||
std::vector<uint8_t> temp = json::to_msgpack(original);
|
||||
|
||||
std::vector<std::byte> msgpack_data(temp.size());
|
||||
for (size_t i = 0; i < temp.size(); ++i)
|
||||
{
|
||||
msgpack_data[i] = std::byte(temp[i]);
|
||||
}
|
||||
// Attempt direct deserialization using std::byte input
|
||||
const json direct_result = json::from_msgpack(msgpack_data);
|
||||
|
||||
// Test the workaround approach: reinterpret as unsigned char* and use iterator range
|
||||
const auto* const char_start = reinterpret_cast<unsigned char const*>(msgpack_data.data());
|
||||
const auto* const char_end = char_start + msgpack_data.size();
|
||||
json workaround_result = json::from_msgpack(char_start, char_end);
|
||||
|
||||
// Verify that the final deserialized JSON matches the original JSON
|
||||
CHECK(direct_result == workaround_result);
|
||||
CHECK(direct_result == original);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// the fake sizes below do not fit into a 32-bit std::size_t
|
||||
// with clang and libstdc++ 10, the std::filesystem::path conversion that
|
||||
// C++17 builds consider for every string type is ambiguous for a class
|
||||
|
||||
@@ -569,7 +569,7 @@ TEST_CASE("regression tests 2")
|
||||
SECTION("issue #2067 - cannot serialize binary data to text JSON")
|
||||
{
|
||||
const std::array<unsigned char, 23> data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}};
|
||||
const json j = json::from_msgpack(data.begin(), data.end());
|
||||
const json j = json::from_msgpack(data.data(), data.size());
|
||||
// dump() is nodiscard; this only checks that dumping does not throw
|
||||
CHECK_NOTHROW(
|
||||
utils::ignore_return_value(
|
||||
|
||||
@@ -8,14 +8,6 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
@@ -56,7 +48,6 @@ TEST_CASE("serialization")
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
SECTION("operator>>")
|
||||
{
|
||||
SECTION("no given width")
|
||||
@@ -88,7 +79,6 @@ TEST_CASE("serialization")
|
||||
"[\n\t\"foo\",\n\t1,\n\t2,\n\t3,\n\tfalse,\n\t{\n\t\t\"one\": 1\n\t}\n]");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("dump")
|
||||
{
|
||||
|
||||
@@ -8,14 +8,6 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command
|
||||
// line *before* including json.hpp, since the library #undefs it once the header
|
||||
// has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the
|
||||
// tests of deprecated functions are skipped if these functions are deleted
|
||||
#if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1)
|
||||
#define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
@@ -2220,10 +2212,8 @@ TEST_CASE("UBJSON input that cannot be read is discarded by every overload")
|
||||
CHECK_THROWS_AS(_ = json::from_ubjson(input.begin(), input.end()), json::parse_error&);
|
||||
CHECK(json::from_ubjson(input, true, false).is_discarded());
|
||||
CHECK(json::from_ubjson(input.begin(), input.end(), true, false).is_discarded());
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
CHECK(json::from_ubjson(input.data(), input.size(), true, false).is_discarded());
|
||||
CHECK(json::from_ubjson({input.data(), input.size()}, true, false).is_discarded());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("UBJSON SAX parsing stops at every event")
|
||||
@@ -2971,14 +2961,12 @@ TEST_CASE("UBJSON roundtrips" * doctest::skip())
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
|
||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": uint8_t* and size");
|
||||
json j2;
|
||||
CHECK_NOTHROW(j2 = json::from_ubjson({packed.data(), packed.size()}));
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
INFO_WITH_TEMP(filename + ": output to output adapters");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,623 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "make_test_data_available.hpp"
|
||||
#include "test_utils.hpp"
|
||||
|
||||
TEST_CASE("Unicode (1/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("\\uxxxx sequences")
|
||||
{
|
||||
// create an escaped string from a code point
|
||||
const auto codepoint_to_unicode = [](std::size_t cp)
|
||||
{
|
||||
// code points are represented as a six-character sequence: a
|
||||
// reverse solidus, followed by the lowercase letter u, followed
|
||||
// by four hexadecimal digits that encode the character's code
|
||||
// point
|
||||
std::stringstream ss;
|
||||
ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp;
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
SECTION("correct sequences")
|
||||
{
|
||||
// generate all UTF-8 code points; in total, 1112064 code points are
|
||||
// generated: 0x1FFFFF code points - 2048 invalid values between
|
||||
// 0xD800 and 0xDFFF.
|
||||
for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
|
||||
{
|
||||
// string to store the code point as in \uxxxx format
|
||||
std::string json_text = "\"";
|
||||
|
||||
// decide whether to use one or two \uxxxx sequences
|
||||
if (cp < 0x10000u)
|
||||
{
|
||||
// The Unicode standard permanently reserves these code point
|
||||
// values for UTF-16 encoding of the high and low surrogates, and
|
||||
// they will never be assigned a character, so there should be no
|
||||
// reason to encode them. The official Unicode standard says that
|
||||
// no UTF forms, including UTF-16, can encode these code points.
|
||||
if (cp >= 0xD800u && cp <= 0xDFFFu)
|
||||
{
|
||||
// if we would not skip these code points, we would get a
|
||||
// "missing low surrogate" exception
|
||||
continue;
|
||||
}
|
||||
|
||||
// code points in the Basic Multilingual Plane can be
|
||||
// represented with one \uxxxx sequence
|
||||
json_text += codepoint_to_unicode(cp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// To escape an extended character that is not in the Basic
|
||||
// Multilingual Plane, the character is represented as a
|
||||
// 12-character sequence, encoding the UTF-16 surrogate pair
|
||||
const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
|
||||
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
|
||||
json_text += codepoint_to_unicode(codepoint1) + codepoint_to_unicode(codepoint2);
|
||||
}
|
||||
|
||||
json_text += "\"";
|
||||
CAPTURE(json_text)
|
||||
json _;
|
||||
CHECK_NOTHROW(_ = json::parse(json_text));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("incorrect sequences")
|
||||
{
|
||||
SECTION("incorrect surrogate values")
|
||||
{
|
||||
json _;
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uDC00\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uDC00'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD7FF\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uD7FF\\uDC00'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800]\""), "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800]'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\v\""), "[json.exception.parse_error.101] parse error at line 1, column 9: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\v'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\u123\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\uD800\\u123\"'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uDBFF\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uDBFF'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uE000\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uE000'", json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if)
|
||||
SECTION("incorrect sequences")
|
||||
{
|
||||
SECTION("high surrogate without low surrogate")
|
||||
{
|
||||
// D800..DBFF are high surrogates and must be followed by low
|
||||
// surrogates DC00..DFFF; here, nothing follows
|
||||
for (std::size_t cp = 0xD800u; cp <= 0xDBFFu; ++cp)
|
||||
{
|
||||
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
|
||||
CAPTURE(json_text)
|
||||
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("high surrogate with wrong low surrogate")
|
||||
{
|
||||
// D800..DBFF are high surrogates and must be followed by low
|
||||
// surrogates DC00..DFFF; here a different sequence follows
|
||||
for (std::size_t cp1 = 0xD800u; cp1 <= 0xDBFFu; ++cp1)
|
||||
{
|
||||
for (std::size_t cp2 = 0x0000u; cp2 <= 0xFFFFu; ++cp2)
|
||||
{
|
||||
if (0xDC00u <= cp2 && cp2 <= 0xDFFFu)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string json_text = "\"" + codepoint_to_unicode(cp1) + codepoint_to_unicode(cp2) + "\"";
|
||||
CAPTURE(json_text)
|
||||
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("low surrogate without high surrogate")
|
||||
{
|
||||
// low surrogates DC00..DFFF must follow high surrogates; here,
|
||||
// they occur alone
|
||||
for (std::size_t cp = 0xDC00u; cp <= 0xDFFFu; ++cp)
|
||||
{
|
||||
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
|
||||
CAPTURE(json_text)
|
||||
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("read all unicode characters")
|
||||
{
|
||||
// read a file with all Unicode characters stored as single-character
|
||||
// strings in a JSON array
|
||||
std::ifstream f(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json");
|
||||
json j;
|
||||
CHECK_NOTHROW(f >> j);
|
||||
|
||||
// the array has 1112064 + 1 elements (a terminating "null" value)
|
||||
// Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between
|
||||
// 0xD800 and 0xDFFF.
|
||||
CHECK(j.size() == 1112065);
|
||||
|
||||
SECTION("check JSON Pointers")
|
||||
{
|
||||
for (const auto& s : j)
|
||||
{
|
||||
// skip non-string JSON values
|
||||
if (!s.is_string())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ptr = s.get<std::string>();
|
||||
|
||||
// tilde must be followed by 0 or 1
|
||||
if (ptr == "~")
|
||||
{
|
||||
ptr += "0";
|
||||
}
|
||||
|
||||
// JSON Pointers must begin with "/"
|
||||
ptr.insert(0, "/");
|
||||
|
||||
CHECK_NOTHROW(json::json_pointer("/" + ptr));
|
||||
|
||||
// check escape/unescape roundtrip
|
||||
auto escaped = nlohmann::detail::escape(ptr);
|
||||
nlohmann::detail::unescape(escaped);
|
||||
CHECK(escaped == ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ignore byte-order-mark")
|
||||
{
|
||||
SECTION("in a stream")
|
||||
{
|
||||
// read a file with a UTF-8 BOM
|
||||
std::ifstream f(TEST_DATA_DIRECTORY "/json_nlohmann_tests/bom.json");
|
||||
json j;
|
||||
CHECK_NOTHROW(f >> j);
|
||||
}
|
||||
|
||||
SECTION("with an iterator")
|
||||
{
|
||||
std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}";
|
||||
json _;
|
||||
CHECK_NOTHROW(_ = json::parse(i.begin(), i.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("error for incomplete/wrong BOM")
|
||||
{
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::parse("\xef\xbb"), json::parse_error&);
|
||||
CHECK_THROWS_AS(_ = json::parse("\xef\xbb\xbb"), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void roundtrip(bool success_expected, const std::string& s);
|
||||
|
||||
void roundtrip(bool success_expected, const std::string& s)
|
||||
{
|
||||
CAPTURE(s)
|
||||
json _;
|
||||
|
||||
// create JSON string value
|
||||
const json j = s;
|
||||
// create JSON text
|
||||
const std::string ps = std::string("\"") + s + "\"";
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
// serialization succeeds
|
||||
// dump() is nodiscard; this only checks that dumping does not throw
|
||||
CHECK_NOTHROW(utils::ignore_return_value(j.dump()));
|
||||
|
||||
// exclude parse test for U+0000
|
||||
if (s[0] != '\0')
|
||||
{
|
||||
// parsing JSON text succeeds
|
||||
CHECK_NOTHROW(_ = json::parse(ps));
|
||||
}
|
||||
|
||||
// roundtrip succeeds
|
||||
CHECK_NOTHROW(_ = json::parse(j.dump()));
|
||||
|
||||
// after roundtrip, the same string is stored
|
||||
const json jr = json::parse(j.dump());
|
||||
CHECK(jr.get<std::string>() == s);
|
||||
}
|
||||
else
|
||||
{
|
||||
// serialization fails
|
||||
// dump() is nodiscard; the exception is thrown by dump() itself before it would return
|
||||
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||
|
||||
// parsing JSON text fails
|
||||
CHECK_THROWS_AS(_ = json::parse(ps), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Markus Kuhn's UTF-8 decoder capability and stress test")
|
||||
{
|
||||
// Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> - 2015-08-28 - CC BY 4.0
|
||||
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
||||
|
||||
SECTION("1 Some correct UTF-8 text")
|
||||
{
|
||||
roundtrip(true, "κόσμε");
|
||||
}
|
||||
|
||||
SECTION("2 Boundary condition test cases")
|
||||
{
|
||||
SECTION("2.1 First possible sequence of a certain length")
|
||||
{
|
||||
// 2.1.1 1 byte (U-00000000)
|
||||
roundtrip(true, std::string("\0", 1));
|
||||
// 2.1.2 2 bytes (U-00000080)
|
||||
roundtrip(true, "\xc2\x80");
|
||||
// 2.1.3 3 bytes (U-00000800)
|
||||
roundtrip(true, "\xe0\xa0\x80");
|
||||
// 2.1.4 4 bytes (U-00010000)
|
||||
roundtrip(true, "\xf0\x90\x80\x80");
|
||||
|
||||
// 2.1.5 5 bytes (U-00200000)
|
||||
roundtrip(false, "\xF8\x88\x80\x80\x80");
|
||||
// 2.1.6 6 bytes (U-04000000)
|
||||
roundtrip(false, "\xFC\x84\x80\x80\x80\x80");
|
||||
}
|
||||
|
||||
SECTION("2.2 Last possible sequence of a certain length")
|
||||
{
|
||||
// 2.2.1 1 byte (U-0000007F)
|
||||
roundtrip(true, "\x7f");
|
||||
// 2.2.2 2 bytes (U-000007FF)
|
||||
roundtrip(true, "\xdf\xbf");
|
||||
// 2.2.3 3 bytes (U-0000FFFF)
|
||||
roundtrip(true, "\xef\xbf\xbf");
|
||||
|
||||
// 2.2.4 4 bytes (U-001FFFFF)
|
||||
roundtrip(false, "\xF7\xBF\xBF\xBF");
|
||||
// 2.2.5 5 bytes (U-03FFFFFF)
|
||||
roundtrip(false, "\xFB\xBF\xBF\xBF\xBF");
|
||||
// 2.2.6 6 bytes (U-7FFFFFFF)
|
||||
roundtrip(false, "\xFD\xBF\xBF\xBF\xBF\xBF");
|
||||
}
|
||||
|
||||
SECTION("2.3 Other boundary conditions")
|
||||
{
|
||||
// 2.3.1 U-0000D7FF = ed 9f bf
|
||||
roundtrip(true, "\xed\x9f\xbf");
|
||||
// 2.3.2 U-0000E000 = ee 80 80
|
||||
roundtrip(true, "\xee\x80\x80");
|
||||
// 2.3.3 U-0000FFFD = ef bf bd
|
||||
roundtrip(true, "\xef\xbf\xbd");
|
||||
// 2.3.4 U-0010FFFF = f4 8f bf bf
|
||||
roundtrip(true, "\xf4\x8f\xbf\xbf");
|
||||
|
||||
// 2.3.5 U-00110000 = f4 90 80 80
|
||||
roundtrip(false, "\xf4\x90\x80\x80");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("3 Malformed sequences")
|
||||
{
|
||||
SECTION("3.1 Unexpected continuation bytes")
|
||||
{
|
||||
// Each unexpected continuation byte should be separately signalled as a
|
||||
// malformed sequence of its own.
|
||||
|
||||
// 3.1.1 First continuation byte 0x80
|
||||
roundtrip(false, "\x80");
|
||||
// 3.1.2 Last continuation byte 0xbf
|
||||
roundtrip(false, "\xbf");
|
||||
|
||||
// 3.1.3 2 continuation bytes
|
||||
roundtrip(false, "\x80\xbf");
|
||||
// 3.1.4 3 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80");
|
||||
// 3.1.5 4 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf");
|
||||
// 3.1.6 5 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf\x80");
|
||||
// 3.1.7 6 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf\x80\xbf");
|
||||
// 3.1.8 7 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf\x80\xbf\x80");
|
||||
|
||||
// 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf)
|
||||
roundtrip(false, "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf");
|
||||
}
|
||||
|
||||
SECTION("3.2 Lonely start characters")
|
||||
{
|
||||
// 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf)
|
||||
roundtrip(false, "\xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf");
|
||||
// 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef)
|
||||
roundtrip(false, "\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef");
|
||||
// 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7)
|
||||
roundtrip(false, "\xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7");
|
||||
// 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb)
|
||||
roundtrip(false, "\xf8 \xf9 \xfa \xfb");
|
||||
// 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd)
|
||||
roundtrip(false, "\xfc \xfd");
|
||||
}
|
||||
|
||||
SECTION("3.3 Sequences with last continuation byte missing")
|
||||
{
|
||||
// All bytes of an incomplete sequence should be signalled as a single
|
||||
// malformed sequence, i.e., you should see only a single replacement
|
||||
// character in each of the next 10 tests. (Characters as in section 2)
|
||||
|
||||
// 3.3.1 2-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xc0");
|
||||
// 3.3.2 3-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xe0\x80");
|
||||
// 3.3.3 4-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xf0\x80\x80");
|
||||
// 3.3.4 5-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xf8\x80\x80\x80");
|
||||
// 3.3.5 6-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xfc\x80\x80\x80\x80");
|
||||
// 3.3.6 2-byte sequence with last byte missing (U-000007FF)
|
||||
roundtrip(false, "\xdf");
|
||||
// 3.3.7 3-byte sequence with last byte missing (U-0000FFFF)
|
||||
roundtrip(false, "\xef\xbf");
|
||||
// 3.3.8 4-byte sequence with last byte missing (U-001FFFFF)
|
||||
roundtrip(false, "\xf7\xbf\xbf");
|
||||
// 3.3.9 5-byte sequence with last byte missing (U-03FFFFFF)
|
||||
roundtrip(false, "\xfb\xbf\xbf\xbf");
|
||||
// 3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF)
|
||||
roundtrip(false, "\xfd\xbf\xbf\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("3.4 Concatenation of incomplete sequences")
|
||||
{
|
||||
// All the 10 sequences of 3.3 concatenated, you should see 10 malformed
|
||||
// sequences being signalled:
|
||||
roundtrip(false, "\xc0\xe0\x80\xf0\x80\x80\xf8\x80\x80\x80\xfc\x80\x80\x80\x80\xdf\xef\xbf\xf7\xbf\xbf\xfb\xbf\xbf\xbf\xfd\xbf\xbf\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("3.5 Impossible bytes")
|
||||
{
|
||||
// The following two bytes cannot appear in a correct UTF-8 string
|
||||
|
||||
// 3.5.1 fe
|
||||
roundtrip(false, "\xfe");
|
||||
// 3.5.2 ff
|
||||
roundtrip(false, "\xff");
|
||||
// 3.5.3 fe fe ff ff
|
||||
roundtrip(false, "\xfe\xfe\xff\xff");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("4 Overlong sequences")
|
||||
{
|
||||
// The following sequences are not malformed according to the letter of
|
||||
// the Unicode 2.0 standard. However, they are longer then necessary and
|
||||
// a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8
|
||||
// decoder" should reject them just like malformed sequences for two
|
||||
// reasons: (1) It helps to debug applications if overlong sequences are
|
||||
// not treated as valid representations of characters, because this helps
|
||||
// to spot problems more quickly. (2) Overlong sequences provide
|
||||
// alternative representations of characters, that could maliciously be
|
||||
// used to bypass filters that check only for ASCII characters. For
|
||||
// instance, a 2-byte encoded line feed (LF) would not be caught by a
|
||||
// line counter that counts only 0x0a bytes, but it would still be
|
||||
// processed as a line feed by an unsafe UTF-8 decoder later in the
|
||||
// pipeline. From a security point of view, ASCII compatibility of UTF-8
|
||||
// sequences means also, that ASCII characters are *only* allowed to be
|
||||
// represented by ASCII bytes in the range 0x00-0x7f. To ensure this
|
||||
// aspect of ASCII compatibility, use only "safe UTF-8 decoders" that
|
||||
// reject overlong UTF-8 sequences for which a shorter encoding exists.
|
||||
|
||||
SECTION("4.1 Examples of an overlong ASCII character")
|
||||
{
|
||||
// With a safe UTF-8 decoder, all the following five overlong
|
||||
// representations of the ASCII character slash ("/") should be rejected
|
||||
// like a malformed UTF-8 sequence, for instance by substituting it with
|
||||
// a replacement character. If you see a slash below, you do not have a
|
||||
// safe UTF-8 decoder!
|
||||
|
||||
// 4.1.1 U+002F = c0 af
|
||||
roundtrip(false, "\xc0\xaf");
|
||||
// 4.1.2 U+002F = e0 80 af
|
||||
roundtrip(false, "\xe0\x80\xaf");
|
||||
// 4.1.3 U+002F = f0 80 80 af
|
||||
roundtrip(false, "\xf0\x80\x80\xaf");
|
||||
// 4.1.4 U+002F = f8 80 80 80 af
|
||||
roundtrip(false, "\xf8\x80\x80\x80\xaf");
|
||||
// 4.1.5 U+002F = fc 80 80 80 80 af
|
||||
roundtrip(false, "\xfc\x80\x80\x80\x80\xaf");
|
||||
}
|
||||
|
||||
SECTION("4.2 Maximum overlong sequences")
|
||||
{
|
||||
// Below you see the highest Unicode value that is still resulting in an
|
||||
// overlong sequence if represented with the given number of bytes. This
|
||||
// is a boundary test for safe UTF-8 decoders. All five characters should
|
||||
// be rejected like malformed UTF-8 sequences.
|
||||
|
||||
// 4.2.1 U-0000007F = c1 bf
|
||||
roundtrip(false, "\xc1\xbf");
|
||||
// 4.2.2 U-000007FF = e0 9f bf
|
||||
roundtrip(false, "\xe0\x9f\xbf");
|
||||
// 4.2.3 U-0000FFFF = f0 8f bf bf
|
||||
roundtrip(false, "\xf0\x8f\xbf\xbf");
|
||||
// 4.2.4 U-001FFFFF = f8 87 bf bf bf
|
||||
roundtrip(false, "\xf8\x87\xbf\xbf\xbf");
|
||||
// 4.2.5 U-03FFFFFF = fc 83 bf bf bf bf
|
||||
roundtrip(false, "\xfc\x83\xbf\xbf\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("4.3 Overlong representation of the NUL character")
|
||||
{
|
||||
// The following five sequences should also be rejected like malformed
|
||||
// UTF-8 sequences and should not be treated like the ASCII NUL
|
||||
// character.
|
||||
|
||||
// 4.3.1 U+0000 = c0 80
|
||||
roundtrip(false, "\xc0\x80");
|
||||
// 4.3.2 U+0000 = e0 80 80
|
||||
roundtrip(false, "\xe0\x80\x80");
|
||||
// 4.3.3 U+0000 = f0 80 80 80
|
||||
roundtrip(false, "\xf0\x80\x80\x80");
|
||||
// 4.3.4 U+0000 = f8 80 80 80 80
|
||||
roundtrip(false, "\xf8\x80\x80\x80\x80");
|
||||
// 4.3.5 U+0000 = fc 80 80 80 80 80
|
||||
roundtrip(false, "\xfc\x80\x80\x80\x80\x80");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("5 Illegal code positions")
|
||||
{
|
||||
// The following UTF-8 sequences should be rejected like malformed
|
||||
// sequences, because they never represent valid ISO 10646 characters and
|
||||
// a UTF-8 decoder that accepts them might introduce security problems
|
||||
// comparable to overlong UTF-8 sequences.
|
||||
|
||||
SECTION("5.1 Single UTF-16 surrogates")
|
||||
{
|
||||
// 5.1.1 U+D800 = ed a0 80
|
||||
roundtrip(false, "\xed\xa0\x80");
|
||||
// 5.1.2 U+DB7F = ed ad bf
|
||||
roundtrip(false, "\xed\xad\xbf");
|
||||
// 5.1.3 U+DB80 = ed ae 80
|
||||
roundtrip(false, "\xed\xae\x80");
|
||||
// 5.1.4 U+DBFF = ed af bf
|
||||
roundtrip(false, "\xed\xaf\xbf");
|
||||
// 5.1.5 U+DC00 = ed b0 80
|
||||
roundtrip(false, "\xed\xb0\x80");
|
||||
// 5.1.6 U+DF80 = ed be 80
|
||||
roundtrip(false, "\xed\xbe\x80");
|
||||
// 5.1.7 U+DFFF = ed bf bf
|
||||
roundtrip(false, "\xed\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("5.2 Paired UTF-16 surrogates")
|
||||
{
|
||||
// 5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80
|
||||
roundtrip(false, "\xed\xa0\x80\xed\xb0\x80");
|
||||
// 5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf
|
||||
roundtrip(false, "\xed\xa0\x80\xed\xbf\xbf");
|
||||
// 5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80
|
||||
roundtrip(false, "\xed\xad\xbf\xed\xb0\x80");
|
||||
// 5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf
|
||||
roundtrip(false, "\xed\xad\xbf\xed\xbf\xbf");
|
||||
// 5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80
|
||||
roundtrip(false, "\xed\xae\x80\xed\xb0\x80");
|
||||
// 5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf
|
||||
roundtrip(false, "\xed\xae\x80\xed\xbf\xbf");
|
||||
// 5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80
|
||||
roundtrip(false, "\xed\xaf\xbf\xed\xb0\x80");
|
||||
// 5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf
|
||||
roundtrip(false, "\xed\xaf\xbf\xed\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("5.3 Noncharacter code positions")
|
||||
{
|
||||
// The following "noncharacters" are "reserved for internal use" by
|
||||
// applications, and according to older versions of the Unicode Standard
|
||||
// "should never be interchanged". Unicode Corrigendum #9 dropped the
|
||||
// latter restriction. Nevertheless, their presence in incoming UTF-8 data
|
||||
// can remain a potential security risk, depending on what use is made of
|
||||
// these codes subsequently. Examples of such internal use:
|
||||
//
|
||||
// - Some file APIs with 16-bit characters may use the integer value -1
|
||||
// = U+FFFF to signal an end-of-file (EOF) or error condition.
|
||||
//
|
||||
// - In some UTF-16 receivers, code point U+FFFE might trigger a
|
||||
// byte-swap operation (to convert between UTF-16LE and UTF-16BE).
|
||||
//
|
||||
// With such internal use of noncharacters, it may be desirable and safer
|
||||
// to block those code points in UTF-8 decoders, as they should never
|
||||
// occur legitimately in incoming UTF-8 data, and could trigger unsafe
|
||||
// behaviour in subsequent processing.
|
||||
|
||||
// Particularly problematic noncharacters in 16-bit applications:
|
||||
|
||||
// 5.3.1 U+FFFE = ef bf be
|
||||
roundtrip(true, "\xef\xbf\xbe");
|
||||
// 5.3.2 U+FFFF = ef bf bf
|
||||
roundtrip(true, "\xef\xbf\xbf");
|
||||
|
||||
// 5.3.3 U+FDD0 .. U+FDEF
|
||||
roundtrip(true, "\xEF\xB7\x90");
|
||||
roundtrip(true, "\xEF\xB7\x91");
|
||||
roundtrip(true, "\xEF\xB7\x92");
|
||||
roundtrip(true, "\xEF\xB7\x93");
|
||||
roundtrip(true, "\xEF\xB7\x94");
|
||||
roundtrip(true, "\xEF\xB7\x95");
|
||||
roundtrip(true, "\xEF\xB7\x96");
|
||||
roundtrip(true, "\xEF\xB7\x97");
|
||||
roundtrip(true, "\xEF\xB7\x98");
|
||||
roundtrip(true, "\xEF\xB7\x99");
|
||||
roundtrip(true, "\xEF\xB7\x9A");
|
||||
roundtrip(true, "\xEF\xB7\x9B");
|
||||
roundtrip(true, "\xEF\xB7\x9C");
|
||||
roundtrip(true, "\xEF\xB7\x9D");
|
||||
roundtrip(true, "\xEF\xB7\x9E");
|
||||
roundtrip(true, "\xEF\xB7\x9F");
|
||||
roundtrip(true, "\xEF\xB7\xA0");
|
||||
roundtrip(true, "\xEF\xB7\xA1");
|
||||
roundtrip(true, "\xEF\xB7\xA2");
|
||||
roundtrip(true, "\xEF\xB7\xA3");
|
||||
roundtrip(true, "\xEF\xB7\xA4");
|
||||
roundtrip(true, "\xEF\xB7\xA5");
|
||||
roundtrip(true, "\xEF\xB7\xA6");
|
||||
roundtrip(true, "\xEF\xB7\xA7");
|
||||
roundtrip(true, "\xEF\xB7\xA8");
|
||||
roundtrip(true, "\xEF\xB7\xA9");
|
||||
roundtrip(true, "\xEF\xB7\xAA");
|
||||
roundtrip(true, "\xEF\xB7\xAB");
|
||||
roundtrip(true, "\xEF\xB7\xAC");
|
||||
roundtrip(true, "\xEF\xB7\xAD");
|
||||
roundtrip(true, "\xEF\xB7\xAE");
|
||||
roundtrip(true, "\xEF\xB7\xAF");
|
||||
|
||||
// 5.3.4 U+nFFFE U+nFFFF (for n = 1..10)
|
||||
roundtrip(true, "\xF0\x9F\xBF\xBF");
|
||||
roundtrip(true, "\xF0\xAF\xBF\xBF");
|
||||
roundtrip(true, "\xF0\xBF\xBF\xBF");
|
||||
roundtrip(true, "\xF1\x8F\xBF\xBF");
|
||||
roundtrip(true, "\xF1\x9F\xBF\xBF");
|
||||
roundtrip(true, "\xF1\xAF\xBF\xBF");
|
||||
roundtrip(true, "\xF1\xBF\xBF\xBF");
|
||||
roundtrip(true, "\xF2\x8F\xBF\xBF");
|
||||
roundtrip(true, "\xF2\x9F\xBF\xBF");
|
||||
roundtrip(true, "\xF2\xAF\xBF\xBF");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,612 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "make_test_data_available.hpp"
|
||||
#include "test_utils.hpp"
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
// dump() is nodiscard; the exception is thrown by dump() itself before it would return
|
||||
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 455355 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (2/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("ill-formed first byte")
|
||||
{
|
||||
for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
|
||||
for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-1 (x00-x7F)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1)
|
||||
{
|
||||
// unescaped control characters are parse errors in JSON
|
||||
if (0x00 <= byte1 && byte1 <= 0x1F)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// a single quote is a parse error in JSON
|
||||
if (byte1 == 0x22)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// a single backslash is a parse error in JSON
|
||||
if (byte1 == 0x5C)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// all other characters are OK
|
||||
check_utf8string(true, byte1);
|
||||
check_utf8dump(true, byte1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-2 (xC2-xDF UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2);
|
||||
check_utf8dump(true, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xE0 xA0-BF UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0xA0 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xED x80-9F UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0x9F)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -1,326 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "make_test_data_available.hpp"
|
||||
#include "test_utils.hpp"
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
// dump() is nodiscard; the exception is thrown by dump() itself before it would return
|
||||
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 1641521 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (3/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("UTF8-4 (xF0 x90-BF UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(true, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x90 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
|
||||
{
|
||||
// skip correct fourth byte
|
||||
if (0x80 <= byte4 && byte4 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -1,326 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "make_test_data_available.hpp"
|
||||
#include "test_utils.hpp"
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
// dump() is nodiscard; the exception is thrown by dump() itself before it would return
|
||||
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 5517507 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (4/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("UTF8-4 (xF1-F3 UTF8-tail UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(true, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
|
||||
{
|
||||
// skip correct fourth byte
|
||||
if (0x80 <= byte4 && byte4 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -1,326 +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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "make_test_data_available.hpp"
|
||||
#include "test_utils.hpp"
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
// dump() is nodiscard; the exception is thrown by dump() itself before it would return
|
||||
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 1246225 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (5/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("UTF8-4 (xF4 x80-8F UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(true, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0x8F)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
|
||||
{
|
||||
// skip correct fourth byte
|
||||
if (0x80 <= byte4 && byte4 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
Reference in New Issue
Block a user