Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92dc52162e | ||
|
|
67cc56e9e8 | ||
|
|
9d15d23131 | ||
|
|
5eff50a5f9 | ||
|
|
649961c8fe | ||
|
|
fe8900bd92 | ||
|
|
39565f62d7 | ||
|
|
1b84568e5e | ||
|
|
2e81630c47 | ||
|
|
48d5e59adf | ||
|
|
547e745289 | ||
|
|
1457bfb948 | ||
|
|
57424ab0b5 | ||
|
|
c6742f9761 | ||
|
|
5423dd8de3 | ||
|
|
32ca00cdfa | ||
|
|
598fbd0417 | ||
|
|
898084b386 | ||
|
|
f13fbd2577 | ||
|
|
c391abccb1 | ||
|
|
d5631dccbd | ||
|
|
80d00eda50 |
No files matched your search
@@ -166,21 +166,19 @@ jobs:
|
|||||||
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
||||||
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
||||||
# Keep the objects small by splitting the test files instead.
|
# Keep the objects small by splitting the test files instead.
|
||||||
# The tests link with LLD: GNU ld fails on objects with more than 32767
|
# Objects with more than 65535 sections fail to link with "relocation
|
||||||
# sections with "relocation truncated to fit: IMAGE_REL_AMD64_REL32
|
# truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'": the MinGW
|
||||||
# against `.rdata'", as it stores the section an associative COMDAT
|
# linker stores the section an associative COMDAT section belongs to in
|
||||||
# section belongs to in 16 bits (x_associated in binutils'
|
# 16 bits (x_associated in binutils' include/coff/internal.h), so it
|
||||||
# include/coff/internal.h) and so discards the jump tables of inline
|
# discards the jump tables of inline functions together with the wrong
|
||||||
# functions together with the wrong function. Each basic_json
|
# function. Each basic_json specialization a test file instantiates adds
|
||||||
# specialization and input type a test file instantiates adds many
|
# many sections, so test a second one in a file of its own.
|
||||||
# sections, so unit-msgpack.cpp and unit-regression2.cpp kept crossing
|
|
||||||
# that limit as the library grew.
|
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build ^
|
run: cmake -S . -B build ^
|
||||||
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
||||||
-DCMAKE_CXX_FLAGS="--target=x86_64-w64-mingw32 -stdlib=libstdc++ -pthread" ^
|
-DCMAKE_CXX_FLAGS="--target=x86_64-w64-mingw32 -stdlib=libstdc++ -pthread" ^
|
||||||
-DCMAKE_CXX_FLAGS_DEBUG="-g0" ^
|
-DCMAKE_CXX_FLAGS_DEBUG="-g0" ^
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld -lwinpthread" ^
|
-DCMAKE_EXE_LINKER_FLAGS="-lwinpthread" ^
|
||||||
-G"MinGW Makefiles" ^
|
-G"MinGW Makefiles" ^
|
||||||
-DCMAKE_BUILD_TYPE=Debug ^
|
-DCMAKE_BUILD_TYPE=Debug ^
|
||||||
-DJSON_BuildTests=On
|
-DJSON_BuildTests=On
|
||||||
|
|||||||
@@ -69,11 +69,12 @@ The default value is `0`: streams are scanned in bulk.
|
|||||||
changes the characters in its get area in place, does not meet these requirements. Define the macro to `1` for
|
changes the characters in its get area in place, does not meet these requirements. Define the macro to `1` for
|
||||||
such stream buffers.
|
such stream buffers.
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
!!! warning "Define consistently"
|
||||||
|
|
||||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_dsbs`), resulting in
|
The macro selects between two definitions of the library's internal stream input adapter. It must therefore be
|
||||||
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
defined identically for **every** translation unit that includes the library; mixing translation units that define
|
||||||
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
it with ones that do not is an ODR violation. Prefer setting it as a compile definition on the target rather than
|
||||||
|
with `#!cpp #define` in individual source files.
|
||||||
|
|
||||||
!!! note "Other inputs"
|
!!! note "Other inputs"
|
||||||
|
|
||||||
|
|||||||
@@ -49,11 +49,12 @@ The default value is `0`: the file is locked once and read with the unlocked std
|
|||||||
A program in which a SAX handler or parser callback waits for **another** thread that uses the **same** `FILE`
|
A program in which a SAX handler or parser callback waits for **another** thread that uses the **same** `FILE`
|
||||||
object would deadlock. Define the macro to `1` for such programs.
|
object would deadlock. Define the macro to `1` for such programs.
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
!!! warning "Define consistently"
|
||||||
|
|
||||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_dufio`), resulting in
|
The macro selects between two definitions of the library's internal `FILE*` input adapter. It must therefore be
|
||||||
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
defined identically for **every** translation unit that includes the library; mixing translation units that define
|
||||||
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
it with ones that do not is an ODR violation (and can leave a file locked). Prefer setting it as a compile
|
||||||
|
definition on the target rather than with `#!cpp #define` in individual source files.
|
||||||
|
|
||||||
!!! note "Other inputs"
|
!!! note "Other inputs"
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,5 @@ the library.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in
|
- Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in
|
||||||
version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, `_ekmo`, `_dsbs`, and `_dufio` in version
|
version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, and `_ekmo` in version 3.13.0; see
|
||||||
3.13.0; see
|
|
||||||
[`nlohmann` Namespace](../../features/namespace.md#structure).
|
[`nlohmann` Namespace](../../features/namespace.md#structure).
|
||||||
@@ -23,10 +23,6 @@ The complete default namespace name is derived as follows:
|
|||||||
- [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) defined non-zero appends `_sbu8`.
|
- [`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
|
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) defined non-zero
|
||||||
appends `_ekmo`.
|
appends `_ekmo`.
|
||||||
- [`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md) defined non-zero appends
|
|
||||||
`_dsbs`.
|
|
||||||
- [`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md) defined non-zero appends
|
|
||||||
`_dufio`.
|
|
||||||
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
- 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)
|
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
||||||
below.
|
below.
|
||||||
@@ -109,7 +105,5 @@ follows:
|
|||||||
- Added ABI tags `_bics` ([`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md)), `_psp`
|
- Added ABI tags `_bics` ([`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md)), `_psp`
|
||||||
([`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md)), `_snul`
|
([`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md)), `_snul`
|
||||||
([`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md)), `_sbu8`
|
([`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md)), `_sbu8`
|
||||||
([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), `_ekmo`
|
([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), and `_ekmo`
|
||||||
([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)), `_dsbs`
|
([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)) in version 3.13.0.
|
||||||
([`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md)), and `_dufio`
|
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md)) in version 3.13.0.
|
|
||||||
@@ -54,14 +54,6 @@
|
|||||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define JSON_DISABLE_STREAM_BULK_SCAN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define JSON_DISABLE_UNLOCKED_FILE_IO 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -110,26 +102,14 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN _dsbs
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO _dufio
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j
|
#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, i, j) \
|
#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, i, j)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -140,9 +120,7 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
||||||
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS, \
|
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO)
|
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <optional> // optional
|
#include <optional> // optional
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iterator> // begin, end, make_move_iterator, move_iterator
|
#include <iterator> // begin, end
|
||||||
#include <memory> // allocator_traits
|
#include <memory> // allocator_traits
|
||||||
#include <string> // basic_string, char_traits
|
#include <string> // basic_string, char_traits
|
||||||
#include <tuple> // tuple, get
|
#include <tuple> // tuple, get
|
||||||
@@ -48,32 +48,6 @@ namespace detail
|
|||||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// a move iterator over the elements of an rvalue container, unless they are
|
|
||||||
// const (e.g., std::set): libstdc++ before GCC 5 defines the reference of a
|
|
||||||
// std::move_iterator as value_type&&, which cannot bind to a const element
|
|
||||||
template<typename Iterator>
|
|
||||||
std::move_iterator<Iterator> make_move_iterator_if_mutable_impl(const Iterator& it, std::true_type /*mutable*/)
|
|
||||||
{
|
|
||||||
return std::make_move_iterator(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
Iterator make_move_iterator_if_mutable_impl(const Iterator& it, std::false_type /*mutable*/)
|
|
||||||
{
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
using is_mutable_iterator = std::integral_constant < bool,
|
|
||||||
!std::is_const<typename std::remove_reference<decltype(*std::declval<Iterator&>())>::type>::value >;
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
auto make_move_iterator_if_mutable(const Iterator& it)
|
|
||||||
-> decltype(make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {}))
|
|
||||||
{
|
|
||||||
return make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -200,7 +174,7 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(make_move_iterator_if_mutable(begin(arr)), make_move_iterator_if_mutable(end(arr))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(std::make_move_iterator(begin(arr)), std::make_move_iterator(end(arr))));
|
||||||
j.replace_value(value_t::array, value);
|
j.replace_value(value_t::array, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +235,7 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(make_move_iterator_if_mutable(begin(obj)), make_move_iterator_if_mutable(end(obj))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(std::make_move_iterator(begin(obj)), std::make_move_iterator(end(obj))));
|
||||||
j.replace_value(value_t::object, value);
|
j.replace_value(value_t::object, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,27 +413,25 @@ using is_movable_container_rvalue = negation<disjunction<
|
|||||||
std::is_const<T>,
|
std::is_const<T>,
|
||||||
has_dedicated_to_json<T>,
|
has_dedicated_to_json<T>,
|
||||||
is_basic_json<T>,
|
is_basic_json<T>,
|
||||||
|
is_range_view_type<T>,
|
||||||
is_enum_keyed_map_type<T>,
|
is_enum_keyed_map_type<T>,
|
||||||
std::is_same<T, std::vector<bool>>>>;
|
std::is_same<T, std::vector<bool>>>>;
|
||||||
|
|
||||||
// rvalue array-like containers; object types are ruled out before views are
|
// rvalue array-like containers
|
||||||
// checked, as std::ranges::view<T> for the object_t of an ordered_json would
|
|
||||||
// need that ordered_json to be complete (GCC 11, see #3312)
|
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
negation<is_compatible_object_type<BasicJsonType, T>>,
|
|
||||||
negation<is_range_view_type<T>>,
|
|
||||||
is_compatible_array_type<BasicJsonType, T>,
|
is_compatible_array_type<BasicJsonType, T>,
|
||||||
negation<disjunction<
|
negation<disjunction<
|
||||||
std::is_same<T, typename BasicJsonType::array_t>,
|
std::is_same<T, typename BasicJsonType::array_t>,
|
||||||
std::is_same<T, typename BasicJsonType::binary_t>,
|
std::is_same<T, typename BasicJsonType::binary_t>,
|
||||||
|
is_compatible_object_type<BasicJsonType, T>,
|
||||||
is_compatible_string_type<BasicJsonType, T>,
|
is_compatible_string_type<BasicJsonType, T>,
|
||||||
is_compatible_binary_type<BasicJsonType, T>>>>;
|
is_compatible_binary_type<BasicJsonType, T>>>>;
|
||||||
|
|
||||||
// rvalue containers: their elements are moved instead of copied
|
// rvalue containers: their elements are moved instead of copied
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||||
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr) // NOLINT(cppcoreguidelines-missing-std-forward)
|
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::array>::construct_moving(j, arr);
|
external_constructor<value_t::array>::construct_moving(j, arr);
|
||||||
}
|
}
|
||||||
@@ -537,8 +509,7 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// rvalue object-like containers (views have no key_type and mapped_type, so
|
// rvalue object-like containers
|
||||||
// they are no compatible object type)
|
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
||||||
@@ -546,7 +517,7 @@ using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
|||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleObjectType,
|
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||||
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj) // NOLINT(cppcoreguidelines-missing-std-forward)
|
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::object>::construct_moving(j, obj);
|
external_constructor<value_t::object>::construct_moving(j, obj);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ JSON_HEDLEY_ALWAYS_INLINE shortest_decimal to_shortest(std::uint64_t bits) noexc
|
|||||||
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
||||||
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
||||||
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
||||||
constexpr auto fractional_low_shift = static_cast<unsigned>(extra_shift);
|
constexpr unsigned fractional_low_shift = static_cast<unsigned>(extra_shift);
|
||||||
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
||||||
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
||||||
std::uint64_t digit = 0;
|
std::uint64_t digit = 0;
|
||||||
|
|||||||
@@ -494,7 +494,8 @@ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_missing_member(c
|
|||||||
static_cast<void>(string_type);
|
static_cast<void>(string_type);
|
||||||
static_cast<void>(val);
|
static_cast<void>(val);
|
||||||
// context-sensitive error message
|
// context-sensitive error message
|
||||||
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat((op == "op") ? std::string("operation") : concat("operation '", op, '\''), string_type ? " must have string member '" : " must have member '", member, "'"), &val));
|
const std::string error_msg = (op == "op") ? std::string("operation") : concat("operation '", op, '\'');
|
||||||
|
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat(error_msg, string_type ? " must have string member '" : " must have member '", member, "'"), &val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename StringType, typename BasicJsonType>
|
template<typename StringType, typename BasicJsonType>
|
||||||
|
|||||||
@@ -90,9 +90,12 @@ class file_input_adapter
|
|||||||
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
||||||
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
||||||
~file_input_adapter()
|
~file_input_adapter()
|
||||||
|
{
|
||||||
|
if (m_file != nullptr)
|
||||||
{
|
{
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::char_traits<char>::int_type get_character() noexcept
|
std::char_traits<char>::int_type get_character() noexcept
|
||||||
{
|
{
|
||||||
@@ -129,11 +132,6 @@ class file_input_adapter
|
|||||||
|
|
||||||
void unlock() noexcept
|
void unlock() noexcept
|
||||||
{
|
{
|
||||||
// a moved-from adapter has no file and holds no lock
|
|
||||||
if (m_file == nullptr)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if JSON_UNLOCKED_FILE_IO == 1
|
#if JSON_UNLOCKED_FILE_IO == 1
|
||||||
funlockfile(m_file);
|
funlockfile(m_file);
|
||||||
#elif JSON_UNLOCKED_FILE_IO == 2
|
#elif JSON_UNLOCKED_FILE_IO == 2
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint32_t
|
#include <cstdint> // uint32_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
|
#include <cstring> // memcmp
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <string> // char_traits, string
|
#include <string> // char_traits, string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
@@ -1854,18 +1855,23 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] literal_text the literal text to expect
|
@param[in] rest the literal text after its first character, which
|
||||||
@param[in] length the length of the passed literal text
|
is current (its length is a template parameter, so
|
||||||
|
that the comparison of the contiguous fast path is
|
||||||
|
a fixed-size load, not a call of memcmp)
|
||||||
@param[in] return_type the token type to return on success
|
@param[in] return_type the token type to return on success
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
template<std::size_t N>
|
||||||
token_type scan_literal(const char_type* literal_text, const std::size_t length,
|
JSON_HEDLEY_NEVER_INLINE token_type scan_literal(const char (&rest)[N], token_type return_type)
|
||||||
token_type return_type)
|
|
||||||
{
|
{
|
||||||
JSON_ASSERT(char_traits<char_type>::to_char_type(current) == literal_text[0]);
|
constexpr std::size_t length = N - 1;
|
||||||
for (std::size_t i = 1; i < length; ++i)
|
if (scan_literal_bulk(rest, length, std::integral_constant<bool, bulk_scan> {}))
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(char_traits<char_type>::to_char_type(get()) != literal_text[i]))
|
return return_type;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(char_traits<char_type>::to_char_type(get()) != static_cast<char_type>(rest[i])))
|
||||||
{
|
{
|
||||||
error_message = "invalid literal";
|
error_message = "invalid literal";
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
@@ -1874,6 +1880,27 @@ scan_number_done:
|
|||||||
return return_type;
|
return return_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// contiguous input: compare the rest of a literal at once; false (and
|
||||||
|
/// nothing consumed) if it does not match, so that the byte path reports
|
||||||
|
/// the error at the right character
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE bool scan_literal_bulk(const char* rest, const std::size_t length, std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
if (next_unget || ia.bulk_remaining() < length
|
||||||
|
|| std::memcmp(ia.bulk_data(), rest, length) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bulk_consume(reinterpret_cast<const char*>(ia.bulk_data()), length);
|
||||||
|
current = static_cast<char_int_type>(static_cast<unsigned char>(rest[length - 1]));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// streaming input: no bulk fast path
|
||||||
|
bool scan_literal_bulk(const char* /*rest*/, const std::size_t /*length*/, std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// input management
|
// input management
|
||||||
/////////////////////
|
/////////////////////
|
||||||
@@ -2499,21 +2526,14 @@ scan_number_done:
|
|||||||
return token_type::value_separator;
|
return token_type::value_separator;
|
||||||
|
|
||||||
// literals
|
// literals
|
||||||
|
// (the text after the first character; no local arrays, which
|
||||||
|
// would give this function a stack canary on every token)
|
||||||
case 't':
|
case 't':
|
||||||
{
|
return scan_literal("rue", token_type::literal_true);
|
||||||
std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};
|
|
||||||
return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
|
|
||||||
}
|
|
||||||
case 'f':
|
case 'f':
|
||||||
{
|
return scan_literal("alse", token_type::literal_false);
|
||||||
std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};
|
|
||||||
return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
|
|
||||||
}
|
|
||||||
case 'n':
|
case 'n':
|
||||||
{
|
return scan_literal("ull", token_type::literal_null);
|
||||||
std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};
|
|
||||||
return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// string
|
// string
|
||||||
case '\"':
|
case '\"':
|
||||||
|
|||||||
@@ -244,8 +244,9 @@ class parser
|
|||||||
bool sax_parse_internal(SAX* sax)
|
bool sax_parse_internal(SAX* sax)
|
||||||
{
|
{
|
||||||
// stack to remember the hierarchy of structured values we are parsing
|
// stack to remember the hierarchy of structured values we are parsing
|
||||||
// true = array; false = object
|
// true = array; false = object (bytes, not std::vector<bool>, whose
|
||||||
std::vector<bool> states;
|
// push_back() is a call with GCC)
|
||||||
|
std::vector<std::uint8_t> states;
|
||||||
// value to avoid a goto (see comment where set to true)
|
// value to avoid a goto (see comment where set to true)
|
||||||
bool skip_to_state_evaluation = false;
|
bool skip_to_state_evaluation = false;
|
||||||
|
|
||||||
@@ -290,7 +291,7 @@ class parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remember we are now inside an object
|
// remember we are now inside an object
|
||||||
states.push_back(false);
|
states.push_back(0);
|
||||||
|
|
||||||
// parse values
|
// parse values
|
||||||
get_token();
|
get_token();
|
||||||
@@ -315,7 +316,7 @@ class parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remember we are now inside an array
|
// remember we are now inside an array
|
||||||
states.push_back(true);
|
states.push_back(1);
|
||||||
|
|
||||||
// parse values (no need to call get_token)
|
// parse values (no need to call get_token)
|
||||||
continue;
|
continue;
|
||||||
@@ -432,7 +433,7 @@ class parser
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (states.back()) // array
|
if (states.back() != 0) // array
|
||||||
{
|
{
|
||||||
// comma -> next value
|
// comma -> next value
|
||||||
// or end of array (ignore_trailing_commas = true)
|
// or end of array (ignore_trailing_commas = true)
|
||||||
|
|||||||
@@ -49,10 +49,8 @@
|
|||||||
#else
|
#else
|
||||||
#define JSON_SCAN_SSSE3 0 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
#define JSON_SCAN_SSSE3 0 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
#endif
|
#endif
|
||||||
#if JSON_SCAN_SSE2 && !JSON_SCAN_SSSE3 && !defined(NLOHMANN_JSON_NO_TARGET_ATTRIBUTE) && ((defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && !defined(__cpp_modules)))
|
#if JSON_SCAN_SSE2 && !JSON_SCAN_SSSE3 && !defined(NLOHMANN_JSON_NO_TARGET_ATTRIBUTE) && ((defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))))
|
||||||
// (GCC before 4.9 has no SSSE3 intrinsics without -mssse3; GCC with C++20
|
// (GCC before 4.9 has no SSSE3 intrinsics without -mssse3)
|
||||||
// modules drops the target attribute from functions it reads from a module
|
|
||||||
// interface and then rejects the intrinsics: "needs isa option -mssse3")
|
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#include <tmmintrin.h>
|
#include <tmmintrin.h>
|
||||||
#define JSON_SCAN_SSSE3_DISPATCH 1 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
#define JSON_SCAN_SSSE3_DISPATCH 1 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t string_bulk_run(const unsigned char* data,
|
|||||||
return scalar_string_bulk_run(data, n);
|
return scalar_string_bulk_run(data, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JSON_SCAN_NEON && !(defined(JSON_USE_SIMDUTF) && defined(JSON_HAS_CPP_17))
|
#if JSON_SCAN_NEON
|
||||||
/// scalar_string_bulk_run() from @a start on, out of line (see
|
/// scalar_string_bulk_run() from @a start on, out of line (see
|
||||||
/// lexer_string_bulk_run())
|
/// lexer_string_bulk_run())
|
||||||
JSON_HEDLEY_NEVER_INLINE inline std::size_t string_bulk_run_from(const unsigned char* data, std::size_t start, std::size_t n) noexcept
|
JSON_HEDLEY_NEVER_INLINE inline std::size_t string_bulk_run_from(const unsigned char* data, std::size_t start, std::size_t n) noexcept
|
||||||
|
|||||||
@@ -680,12 +680,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!m_object->is_object()))
|
if (JSON_HEDLEY_LIKELY(m_object->is_object()))
|
||||||
{
|
{
|
||||||
throw_literal<invalid_iterator>(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object);
|
return m_it.object_iterator->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_it.object_iterator->first;
|
throw_literal<invalid_iterator>(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include <algorithm> // all_of
|
#include <algorithm> // all_of
|
||||||
#include <cctype> // isdigit
|
#include <cctype> // isdigit
|
||||||
#include <cerrno> // errno, ERANGE
|
#include <cerrno> // errno, ERANGE
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <cstdlib> // strtoull
|
#include <cstdlib> // strtoull
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
#include <iosfwd> // ostream
|
#include <iosfwd> // ostream
|
||||||
@@ -335,7 +334,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
break; // not a plain number: let strtoull decide
|
break; // not a plain number: let strtoull decide
|
||||||
}
|
}
|
||||||
acc = static_cast<size_type>((acc * 10) + static_cast<size_type>(c - '0'));
|
acc = static_cast<size_type>(acc * 10 + static_cast<size_type>(c - '0'));
|
||||||
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
||||||
{
|
{
|
||||||
idx = acc;
|
idx = acc;
|
||||||
@@ -530,10 +529,10 @@ class json_pointer
|
|||||||
|
|
||||||
static std::size_t hash_of(std::size_t parent, const string_t& token) noexcept
|
static std::size_t hash_of(std::size_t parent, const string_t& token) noexcept
|
||||||
{
|
{
|
||||||
std::size_t h = parent * static_cast<std::size_t>(0x9E3779B97F4A7C15ULL & (std::numeric_limits<std::size_t>::max)());
|
std::size_t h = parent * static_cast<std::size_t>(0x9E3779B97F4A7C15ULL);
|
||||||
for (std::size_t i = 0; i < token.size(); ++i)
|
for (std::size_t i = 0; i < token.size(); ++i)
|
||||||
{
|
{
|
||||||
h = (h ^ static_cast<unsigned char>(token[i])) * static_cast<std::size_t>(0x100000001B3ULL & (std::numeric_limits<std::size_t>::max)());
|
h = (h ^ static_cast<unsigned char>(token[i])) * static_cast<std::size_t>(0x100000001B3ULL);
|
||||||
}
|
}
|
||||||
return h ^ (h >> 29u);
|
return h ^ (h >> 29u);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -895,3 +895,11 @@
|
|||||||
#ifndef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
#ifndef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
#define JSON_DISABLE_STREAM_BULK_SCAN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_DISABLE_UNLOCKED_FILE_IO
|
||||||
|
#define JSON_DISABLE_UNLOCKED_FILE_IO 0
|
||||||
|
#endif
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
|
#undef JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
#undef JSON_DISABLE_UNLOCKED_FILE_IO
|
||||||
#undef JSON_UNLOCKED_FILE_IO
|
#undef JSON_UNLOCKED_FILE_IO
|
||||||
#undef JSON_DTOA_SSE2
|
#undef JSON_DTOA_SSE2
|
||||||
#undef JSON_DTOA_NEON
|
#undef JSON_DTOA_NEON
|
||||||
@@ -55,8 +57,6 @@
|
|||||||
#undef JSON_STRICT_BINARY_UTF8
|
#undef JSON_STRICT_BINARY_UTF8
|
||||||
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||||
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||||
#undef JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#undef JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
+29
-31
@@ -29,7 +29,6 @@
|
|||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <map> // map
|
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <set> // swap, operator!=
|
#include <set> // swap, operator!=
|
||||||
#include <stdexcept> // length_error
|
#include <stdexcept> // length_error
|
||||||
@@ -755,20 +754,26 @@ private:
|
|||||||
// one level deep); stops at the first structured child it meets
|
// one level deep); stops at the first structured child it meets
|
||||||
static bool all_children_leaves(const basic_json& v) noexcept
|
static bool all_children_leaves(const basic_json& v) noexcept
|
||||||
{
|
{
|
||||||
// array_t only needs forward iterators (see custom_array_type)
|
|
||||||
if (v.m_data.m_type == value_t::array)
|
if (v.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
const auto& a = *v.m_data.m_value.array;
|
const auto& a = *v.m_data.m_value.array;
|
||||||
return std::all_of(a.begin(), a.end(), [](const basic_json & el)
|
for (auto it = a.rbegin(); it != a.rend(); ++it)
|
||||||
{
|
{
|
||||||
return has_no_children(el);
|
if (!has_no_children(*it))
|
||||||
});
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
const auto& o = *v.m_data.m_value.object;
|
}
|
||||||
return std::all_of(o.begin(), o.end(), [](const typename object_t::value_type & el)
|
return true;
|
||||||
|
}
|
||||||
|
for (const auto& el : *v.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
return has_no_children(el.second);
|
if (!has_no_children(el.second))
|
||||||
});
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The walk in destroy_container() may take the children of a
|
// The walk in destroy_container() may take the children of a
|
||||||
@@ -1225,7 +1230,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename O = object_t, detail::enable_if_t < detail::has_stable_elements<O>::value, int > = 0 >
|
template < typename O = object_t, detail::enable_if_t < detail::has_stable_elements<O>::value, int > = 0 >
|
||||||
std::size_t object_capacity() const noexcept
|
static constexpr std::size_t object_capacity() noexcept
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1237,7 +1242,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename O = object_t, detail::enable_if_t < !detail::has_stable_elements<O>::value && !detail::is_nlohmann_ordered_map<O>::value, int > = 0 >
|
template < typename O = object_t, detail::enable_if_t < !detail::has_stable_elements<O>::value && !detail::is_nlohmann_ordered_map<O>::value, int > = 0 >
|
||||||
std::size_t object_capacity() const noexcept
|
static constexpr std::size_t object_capacity() noexcept
|
||||||
{
|
{
|
||||||
return detail::unknown_size();
|
return detail::unknown_size();
|
||||||
}
|
}
|
||||||
@@ -3038,21 +3043,18 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_iterator_range_of_null(const basic_json* obj)
|
static void throw_iterator_range_of_null(const basic_json* obj)
|
||||||
{
|
{
|
||||||
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_incompatible_reference_type(const basic_json& obj)
|
static void throw_incompatible_reference_type(const basic_json& obj)
|
||||||
{
|
{
|
||||||
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_exceeds_size_type(size_type idx)
|
static void throw_array_index_exceeds_size_type(size_type idx)
|
||||||
{
|
{
|
||||||
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3060,8 +3062,6 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
||||||
{
|
{
|
||||||
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(j);
|
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3070,8 +3070,6 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
||||||
{
|
{
|
||||||
static_cast<void>(key); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(j);
|
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4895,11 +4893,8 @@ public:
|
|||||||
iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_LIKELY(is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
@@ -4910,16 +4905,16 @@ public:
|
|||||||
return insert_iterator(pos, val);
|
return insert_iterator(pos, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief inserts element into array
|
/// @brief inserts element into array
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
||||||
iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_LIKELY(is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
@@ -4932,16 +4927,16 @@ public:
|
|||||||
return insert_iterator(pos, std::move(tmp));
|
return insert_iterator(pos, std::move(tmp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief inserts copies of element into array
|
/// @brief inserts copies of element into array
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
||||||
iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_LIKELY(is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
@@ -4952,6 +4947,9 @@ public:
|
|||||||
return insert_iterator(pos, cnt, val);
|
return insert_iterator(pos, cnt, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief inserts range of elements into array
|
/// @brief inserts range of elements into array
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
||||||
iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)
|
||||||
@@ -5455,7 +5453,7 @@ public:
|
|||||||
// inline fast path for two values of the same non-owning scalar type; anything
|
// inline fast path for two values of the same non-owning scalar type; anything
|
||||||
// else falls through to the out-of-line general comparison
|
// else falls through to the out-of-line general comparison
|
||||||
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
||||||
if (lhs.m_data.m_type == rhs.m_data.m_type) /* NOLINT(readability/braces) */ \
|
if (lhs.m_data.m_type == rhs.m_data.m_type) \
|
||||||
{ \
|
{ \
|
||||||
switch (lhs.m_data.m_type) \
|
switch (lhs.m_data.m_type) \
|
||||||
{ \
|
{ \
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
#include <nlohmann/thirdparty/hedley/hedley.hpp>
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
|||||||
Generated
-113664
File diff suppressed because it is too large.
Load diff
Generated
+113
-143
@@ -29,7 +29,6 @@
|
|||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <map> // map
|
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <set> // swap, operator!=
|
#include <set> // swap, operator!=
|
||||||
#include <stdexcept> // length_error
|
#include <stdexcept> // length_error
|
||||||
@@ -118,14 +117,6 @@
|
|||||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define JSON_DISABLE_STREAM_BULK_SCAN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define JSON_DISABLE_UNLOCKED_FILE_IO 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -174,26 +165,14 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN _dsbs
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO _dufio
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j
|
#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, i, j) \
|
#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, i, j)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -204,9 +183,7 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
||||||
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS, \
|
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO)
|
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
@@ -3261,6 +3238,14 @@ JSON_HEDLEY_DIAGNOSTIC_POP
|
|||||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
#define JSON_DISABLE_STREAM_BULK_SCAN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_DISABLE_UNLOCKED_FILE_IO
|
||||||
|
#define JSON_DISABLE_UNLOCKED_FILE_IO 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||||
#include <compare> // partial_ordering
|
#include <compare> // partial_ordering
|
||||||
#endif
|
#endif
|
||||||
@@ -5817,7 +5802,8 @@ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_missing_member(c
|
|||||||
static_cast<void>(string_type);
|
static_cast<void>(string_type);
|
||||||
static_cast<void>(val);
|
static_cast<void>(val);
|
||||||
// context-sensitive error message
|
// context-sensitive error message
|
||||||
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat((op == "op") ? std::string("operation") : concat("operation '", op, '\''), string_type ? " must have string member '" : " must have member '", member, "'"), &val));
|
const std::string error_msg = (op == "op") ? std::string("operation") : concat("operation '", op, '\'');
|
||||||
|
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat(error_msg, string_type ? " must have string member '" : " must have member '", member, "'"), &val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename StringType, typename BasicJsonType>
|
template<typename StringType, typename BasicJsonType>
|
||||||
@@ -6577,7 +6563,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <optional> // optional
|
#include <optional> // optional
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iterator> // begin, end, make_move_iterator, move_iterator
|
#include <iterator> // begin, end
|
||||||
#include <memory> // allocator_traits
|
#include <memory> // allocator_traits
|
||||||
#include <string> // basic_string, char_traits
|
#include <string> // basic_string, char_traits
|
||||||
#include <tuple> // tuple, get
|
#include <tuple> // tuple, get
|
||||||
@@ -7226,32 +7212,6 @@ namespace detail
|
|||||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// a move iterator over the elements of an rvalue container, unless they are
|
|
||||||
// const (e.g., std::set): libstdc++ before GCC 5 defines the reference of a
|
|
||||||
// std::move_iterator as value_type&&, which cannot bind to a const element
|
|
||||||
template<typename Iterator>
|
|
||||||
std::move_iterator<Iterator> make_move_iterator_if_mutable_impl(const Iterator& it, std::true_type /*mutable*/)
|
|
||||||
{
|
|
||||||
return std::make_move_iterator(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
Iterator make_move_iterator_if_mutable_impl(const Iterator& it, std::false_type /*mutable*/)
|
|
||||||
{
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
using is_mutable_iterator = std::integral_constant < bool,
|
|
||||||
!std::is_const<typename std::remove_reference<decltype(*std::declval<Iterator&>())>::type>::value >;
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
auto make_move_iterator_if_mutable(const Iterator& it)
|
|
||||||
-> decltype(make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {}))
|
|
||||||
{
|
|
||||||
return make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -7378,7 +7338,7 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(make_move_iterator_if_mutable(begin(arr)), make_move_iterator_if_mutable(end(arr))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(std::make_move_iterator(begin(arr)), std::make_move_iterator(end(arr))));
|
||||||
j.replace_value(value_t::array, value);
|
j.replace_value(value_t::array, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7439,7 +7399,7 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(make_move_iterator_if_mutable(begin(obj)), make_move_iterator_if_mutable(end(obj))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(std::make_move_iterator(begin(obj)), std::make_move_iterator(end(obj))));
|
||||||
j.replace_value(value_t::object, value);
|
j.replace_value(value_t::object, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7617,27 +7577,25 @@ using is_movable_container_rvalue = negation<disjunction<
|
|||||||
std::is_const<T>,
|
std::is_const<T>,
|
||||||
has_dedicated_to_json<T>,
|
has_dedicated_to_json<T>,
|
||||||
is_basic_json<T>,
|
is_basic_json<T>,
|
||||||
|
is_range_view_type<T>,
|
||||||
is_enum_keyed_map_type<T>,
|
is_enum_keyed_map_type<T>,
|
||||||
std::is_same<T, std::vector<bool>>>>;
|
std::is_same<T, std::vector<bool>>>>;
|
||||||
|
|
||||||
// rvalue array-like containers; object types are ruled out before views are
|
// rvalue array-like containers
|
||||||
// checked, as std::ranges::view<T> for the object_t of an ordered_json would
|
|
||||||
// need that ordered_json to be complete (GCC 11, see #3312)
|
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
negation<is_compatible_object_type<BasicJsonType, T>>,
|
|
||||||
negation<is_range_view_type<T>>,
|
|
||||||
is_compatible_array_type<BasicJsonType, T>,
|
is_compatible_array_type<BasicJsonType, T>,
|
||||||
negation<disjunction<
|
negation<disjunction<
|
||||||
std::is_same<T, typename BasicJsonType::array_t>,
|
std::is_same<T, typename BasicJsonType::array_t>,
|
||||||
std::is_same<T, typename BasicJsonType::binary_t>,
|
std::is_same<T, typename BasicJsonType::binary_t>,
|
||||||
|
is_compatible_object_type<BasicJsonType, T>,
|
||||||
is_compatible_string_type<BasicJsonType, T>,
|
is_compatible_string_type<BasicJsonType, T>,
|
||||||
is_compatible_binary_type<BasicJsonType, T>>>>;
|
is_compatible_binary_type<BasicJsonType, T>>>>;
|
||||||
|
|
||||||
// rvalue containers: their elements are moved instead of copied
|
// rvalue containers: their elements are moved instead of copied
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||||
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr) // NOLINT(cppcoreguidelines-missing-std-forward)
|
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::array>::construct_moving(j, arr);
|
external_constructor<value_t::array>::construct_moving(j, arr);
|
||||||
}
|
}
|
||||||
@@ -7715,8 +7673,7 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// rvalue object-like containers (views have no key_type and mapped_type, so
|
// rvalue object-like containers
|
||||||
// they are no compatible object type)
|
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
||||||
@@ -7724,7 +7681,7 @@ using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
|||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleObjectType,
|
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||||
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj) // NOLINT(cppcoreguidelines-missing-std-forward)
|
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::object>::construct_moving(j, obj);
|
external_constructor<value_t::object>::construct_moving(j, obj);
|
||||||
}
|
}
|
||||||
@@ -8410,9 +8367,12 @@ class file_input_adapter
|
|||||||
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
||||||
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
||||||
~file_input_adapter()
|
~file_input_adapter()
|
||||||
|
{
|
||||||
|
if (m_file != nullptr)
|
||||||
{
|
{
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::char_traits<char>::int_type get_character() noexcept
|
std::char_traits<char>::int_type get_character() noexcept
|
||||||
{
|
{
|
||||||
@@ -8449,11 +8409,6 @@ class file_input_adapter
|
|||||||
|
|
||||||
void unlock() noexcept
|
void unlock() noexcept
|
||||||
{
|
{
|
||||||
// a moved-from adapter has no file and holds no lock
|
|
||||||
if (m_file == nullptr)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if JSON_UNLOCKED_FILE_IO == 1
|
#if JSON_UNLOCKED_FILE_IO == 1
|
||||||
funlockfile(m_file);
|
funlockfile(m_file);
|
||||||
#elif JSON_UNLOCKED_FILE_IO == 2
|
#elif JSON_UNLOCKED_FILE_IO == 2
|
||||||
@@ -9564,6 +9519,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint32_t
|
#include <cstdint> // uint32_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
|
#include <cstring> // memcmp
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <string> // char_traits, string
|
#include <string> // char_traits, string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
@@ -11253,10 +11209,8 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#else
|
#else
|
||||||
#define JSON_SCAN_SSSE3 0 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
#define JSON_SCAN_SSSE3 0 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
#endif
|
#endif
|
||||||
#if JSON_SCAN_SSE2 && !JSON_SCAN_SSSE3 && !defined(NLOHMANN_JSON_NO_TARGET_ATTRIBUTE) && ((defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && !defined(__cpp_modules)))
|
#if JSON_SCAN_SSE2 && !JSON_SCAN_SSSE3 && !defined(NLOHMANN_JSON_NO_TARGET_ATTRIBUTE) && ((defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))))
|
||||||
// (GCC before 4.9 has no SSSE3 intrinsics without -mssse3; GCC with C++20
|
// (GCC before 4.9 has no SSSE3 intrinsics without -mssse3)
|
||||||
// modules drops the target attribute from functions it reads from a module
|
|
||||||
// interface and then rejects the intrinsics: "needs isa option -mssse3")
|
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#include <tmmintrin.h>
|
#include <tmmintrin.h>
|
||||||
#define JSON_SCAN_SSSE3_DISPATCH 1 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
#define JSON_SCAN_SSSE3_DISPATCH 1 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
@@ -11930,7 +11884,7 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t string_bulk_run(const unsigned char* data,
|
|||||||
return scalar_string_bulk_run(data, n);
|
return scalar_string_bulk_run(data, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JSON_SCAN_NEON && !(defined(JSON_USE_SIMDUTF) && defined(JSON_HAS_CPP_17))
|
#if JSON_SCAN_NEON
|
||||||
/// scalar_string_bulk_run() from @a start on, out of line (see
|
/// scalar_string_bulk_run() from @a start on, out of line (see
|
||||||
/// lexer_string_bulk_run())
|
/// lexer_string_bulk_run())
|
||||||
JSON_HEDLEY_NEVER_INLINE inline std::size_t string_bulk_run_from(const unsigned char* data, std::size_t start, std::size_t n) noexcept
|
JSON_HEDLEY_NEVER_INLINE inline std::size_t string_bulk_run_from(const unsigned char* data, std::size_t start, std::size_t n) noexcept
|
||||||
@@ -13849,18 +13803,23 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] literal_text the literal text to expect
|
@param[in] rest the literal text after its first character, which
|
||||||
@param[in] length the length of the passed literal text
|
is current (its length is a template parameter, so
|
||||||
|
that the comparison of the contiguous fast path is
|
||||||
|
a fixed-size load, not a call of memcmp)
|
||||||
@param[in] return_type the token type to return on success
|
@param[in] return_type the token type to return on success
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
template<std::size_t N>
|
||||||
token_type scan_literal(const char_type* literal_text, const std::size_t length,
|
JSON_HEDLEY_NEVER_INLINE token_type scan_literal(const char (&rest)[N], token_type return_type)
|
||||||
token_type return_type)
|
|
||||||
{
|
{
|
||||||
JSON_ASSERT(char_traits<char_type>::to_char_type(current) == literal_text[0]);
|
constexpr std::size_t length = N - 1;
|
||||||
for (std::size_t i = 1; i < length; ++i)
|
if (scan_literal_bulk(rest, length, std::integral_constant<bool, bulk_scan> {}))
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(char_traits<char_type>::to_char_type(get()) != literal_text[i]))
|
return return_type;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(char_traits<char_type>::to_char_type(get()) != static_cast<char_type>(rest[i])))
|
||||||
{
|
{
|
||||||
error_message = "invalid literal";
|
error_message = "invalid literal";
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
@@ -13869,6 +13828,27 @@ scan_number_done:
|
|||||||
return return_type;
|
return return_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// contiguous input: compare the rest of a literal at once; false (and
|
||||||
|
/// nothing consumed) if it does not match, so that the byte path reports
|
||||||
|
/// the error at the right character
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE bool scan_literal_bulk(const char* rest, const std::size_t length, std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
if (next_unget || ia.bulk_remaining() < length
|
||||||
|
|| std::memcmp(ia.bulk_data(), rest, length) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bulk_consume(reinterpret_cast<const char*>(ia.bulk_data()), length);
|
||||||
|
current = static_cast<char_int_type>(static_cast<unsigned char>(rest[length - 1]));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// streaming input: no bulk fast path
|
||||||
|
bool scan_literal_bulk(const char* /*rest*/, const std::size_t /*length*/, std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// input management
|
// input management
|
||||||
/////////////////////
|
/////////////////////
|
||||||
@@ -14494,21 +14474,14 @@ scan_number_done:
|
|||||||
return token_type::value_separator;
|
return token_type::value_separator;
|
||||||
|
|
||||||
// literals
|
// literals
|
||||||
|
// (the text after the first character; no local arrays, which
|
||||||
|
// would give this function a stack canary on every token)
|
||||||
case 't':
|
case 't':
|
||||||
{
|
return scan_literal("rue", token_type::literal_true);
|
||||||
std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};
|
|
||||||
return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
|
|
||||||
}
|
|
||||||
case 'f':
|
case 'f':
|
||||||
{
|
return scan_literal("alse", token_type::literal_false);
|
||||||
std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};
|
|
||||||
return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
|
|
||||||
}
|
|
||||||
case 'n':
|
case 'n':
|
||||||
{
|
return scan_literal("ull", token_type::literal_null);
|
||||||
std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};
|
|
||||||
return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// string
|
// string
|
||||||
case '\"':
|
case '\"':
|
||||||
@@ -20622,8 +20595,9 @@ class parser
|
|||||||
bool sax_parse_internal(SAX* sax)
|
bool sax_parse_internal(SAX* sax)
|
||||||
{
|
{
|
||||||
// stack to remember the hierarchy of structured values we are parsing
|
// stack to remember the hierarchy of structured values we are parsing
|
||||||
// true = array; false = object
|
// true = array; false = object (bytes, not std::vector<bool>, whose
|
||||||
std::vector<bool> states;
|
// push_back() is a call with GCC)
|
||||||
|
std::vector<std::uint8_t> states;
|
||||||
// value to avoid a goto (see comment where set to true)
|
// value to avoid a goto (see comment where set to true)
|
||||||
bool skip_to_state_evaluation = false;
|
bool skip_to_state_evaluation = false;
|
||||||
|
|
||||||
@@ -20668,7 +20642,7 @@ class parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remember we are now inside an object
|
// remember we are now inside an object
|
||||||
states.push_back(false);
|
states.push_back(0);
|
||||||
|
|
||||||
// parse values
|
// parse values
|
||||||
get_token();
|
get_token();
|
||||||
@@ -20693,7 +20667,7 @@ class parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remember we are now inside an array
|
// remember we are now inside an array
|
||||||
states.push_back(true);
|
states.push_back(1);
|
||||||
|
|
||||||
// parse values (no need to call get_token)
|
// parse values (no need to call get_token)
|
||||||
continue;
|
continue;
|
||||||
@@ -20810,7 +20784,7 @@ class parser
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (states.back()) // array
|
if (states.back() != 0) // array
|
||||||
{
|
{
|
||||||
// comma -> next value
|
// comma -> next value
|
||||||
// or end of array (ignore_trailing_commas = true)
|
// or end of array (ignore_trailing_commas = true)
|
||||||
@@ -21842,12 +21816,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!m_object->is_object()))
|
if (JSON_HEDLEY_LIKELY(m_object->is_object()))
|
||||||
{
|
{
|
||||||
throw_literal<invalid_iterator>(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object);
|
return m_it.object_iterator->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_it.object_iterator->first;
|
throw_literal<invalid_iterator>(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -22074,7 +22048,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <algorithm> // all_of
|
#include <algorithm> // all_of
|
||||||
#include <cctype> // isdigit
|
#include <cctype> // isdigit
|
||||||
#include <cerrno> // errno, ERANGE
|
#include <cerrno> // errno, ERANGE
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <cstdlib> // strtoull
|
#include <cstdlib> // strtoull
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
#include <iosfwd> // ostream
|
#include <iosfwd> // ostream
|
||||||
@@ -22405,7 +22378,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
break; // not a plain number: let strtoull decide
|
break; // not a plain number: let strtoull decide
|
||||||
}
|
}
|
||||||
acc = static_cast<size_type>((acc * 10) + static_cast<size_type>(c - '0'));
|
acc = static_cast<size_type>(acc * 10 + static_cast<size_type>(c - '0'));
|
||||||
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
||||||
{
|
{
|
||||||
idx = acc;
|
idx = acc;
|
||||||
@@ -22600,10 +22573,10 @@ class json_pointer
|
|||||||
|
|
||||||
static std::size_t hash_of(std::size_t parent, const string_t& token) noexcept
|
static std::size_t hash_of(std::size_t parent, const string_t& token) noexcept
|
||||||
{
|
{
|
||||||
std::size_t h = parent * static_cast<std::size_t>(0x9E3779B97F4A7C15ULL & (std::numeric_limits<std::size_t>::max)());
|
std::size_t h = parent * static_cast<std::size_t>(0x9E3779B97F4A7C15ULL);
|
||||||
for (std::size_t i = 0; i < token.size(); ++i)
|
for (std::size_t i = 0; i < token.size(); ++i)
|
||||||
{
|
{
|
||||||
h = (h ^ static_cast<unsigned char>(token[i])) * static_cast<std::size_t>(0x100000001B3ULL & (std::numeric_limits<std::size_t>::max)());
|
h = (h ^ static_cast<unsigned char>(token[i])) * static_cast<std::size_t>(0x100000001B3ULL);
|
||||||
}
|
}
|
||||||
return h ^ (h >> 29u);
|
return h ^ (h >> 29u);
|
||||||
}
|
}
|
||||||
@@ -27582,7 +27555,7 @@ JSON_HEDLEY_ALWAYS_INLINE shortest_decimal to_shortest(std::uint64_t bits) noexc
|
|||||||
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
||||||
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
||||||
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
||||||
constexpr auto fractional_low_shift = static_cast<unsigned>(extra_shift);
|
constexpr unsigned fractional_low_shift = static_cast<unsigned>(extra_shift);
|
||||||
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
||||||
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
||||||
std::uint64_t digit = 0;
|
std::uint64_t digit = 0;
|
||||||
@@ -30832,8 +30805,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
// #include <nlohmann/detail/meta/type_traits.hpp>
|
// #include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/thirdparty/hedley/hedley.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@@ -31918,20 +31889,26 @@ private:
|
|||||||
// one level deep); stops at the first structured child it meets
|
// one level deep); stops at the first structured child it meets
|
||||||
static bool all_children_leaves(const basic_json& v) noexcept
|
static bool all_children_leaves(const basic_json& v) noexcept
|
||||||
{
|
{
|
||||||
// array_t only needs forward iterators (see custom_array_type)
|
|
||||||
if (v.m_data.m_type == value_t::array)
|
if (v.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
const auto& a = *v.m_data.m_value.array;
|
const auto& a = *v.m_data.m_value.array;
|
||||||
return std::all_of(a.begin(), a.end(), [](const basic_json & el)
|
for (auto it = a.rbegin(); it != a.rend(); ++it)
|
||||||
{
|
{
|
||||||
return has_no_children(el);
|
if (!has_no_children(*it))
|
||||||
});
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
const auto& o = *v.m_data.m_value.object;
|
}
|
||||||
return std::all_of(o.begin(), o.end(), [](const typename object_t::value_type & el)
|
return true;
|
||||||
|
}
|
||||||
|
for (const auto& el : *v.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
return has_no_children(el.second);
|
if (!has_no_children(el.second))
|
||||||
});
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The walk in destroy_container() may take the children of a
|
// The walk in destroy_container() may take the children of a
|
||||||
@@ -32388,7 +32365,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename O = object_t, detail::enable_if_t < detail::has_stable_elements<O>::value, int > = 0 >
|
template < typename O = object_t, detail::enable_if_t < detail::has_stable_elements<O>::value, int > = 0 >
|
||||||
std::size_t object_capacity() const noexcept
|
static constexpr std::size_t object_capacity() noexcept
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -32400,7 +32377,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename O = object_t, detail::enable_if_t < !detail::has_stable_elements<O>::value && !detail::is_nlohmann_ordered_map<O>::value, int > = 0 >
|
template < typename O = object_t, detail::enable_if_t < !detail::has_stable_elements<O>::value && !detail::is_nlohmann_ordered_map<O>::value, int > = 0 >
|
||||||
std::size_t object_capacity() const noexcept
|
static constexpr std::size_t object_capacity() noexcept
|
||||||
{
|
{
|
||||||
return detail::unknown_size();
|
return detail::unknown_size();
|
||||||
}
|
}
|
||||||
@@ -34201,21 +34178,18 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_iterator_range_of_null(const basic_json* obj)
|
static void throw_iterator_range_of_null(const basic_json* obj)
|
||||||
{
|
{
|
||||||
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_incompatible_reference_type(const basic_json& obj)
|
static void throw_incompatible_reference_type(const basic_json& obj)
|
||||||
{
|
{
|
||||||
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_exceeds_size_type(size_type idx)
|
static void throw_array_index_exceeds_size_type(size_type idx)
|
||||||
{
|
{
|
||||||
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34223,8 +34197,6 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
||||||
{
|
{
|
||||||
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(j);
|
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34233,8 +34205,6 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
||||||
{
|
{
|
||||||
static_cast<void>(key); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(j);
|
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36058,11 +36028,8 @@ public:
|
|||||||
iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_LIKELY(is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
@@ -36073,16 +36040,16 @@ public:
|
|||||||
return insert_iterator(pos, val);
|
return insert_iterator(pos, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief inserts element into array
|
/// @brief inserts element into array
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
||||||
iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_LIKELY(is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
@@ -36095,16 +36062,16 @@ public:
|
|||||||
return insert_iterator(pos, std::move(tmp));
|
return insert_iterator(pos, std::move(tmp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief inserts copies of element into array
|
/// @brief inserts copies of element into array
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
||||||
iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_LIKELY(is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
@@ -36115,6 +36082,9 @@ public:
|
|||||||
return insert_iterator(pos, cnt, val);
|
return insert_iterator(pos, cnt, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief inserts range of elements into array
|
/// @brief inserts range of elements into array
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
/// @sa https://json.nlohmann.me/api/basic_json/insert/
|
||||||
iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)
|
iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)
|
||||||
@@ -36618,7 +36588,7 @@ public:
|
|||||||
// inline fast path for two values of the same non-owning scalar type; anything
|
// inline fast path for two values of the same non-owning scalar type; anything
|
||||||
// else falls through to the out-of-line general comparison
|
// else falls through to the out-of-line general comparison
|
||||||
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
||||||
if (lhs.m_data.m_type == rhs.m_data.m_type) /* NOLINT(readability/braces) */ \
|
if (lhs.m_data.m_type == rhs.m_data.m_type) \
|
||||||
{ \
|
{ \
|
||||||
switch (lhs.m_data.m_type) \
|
switch (lhs.m_data.m_type) \
|
||||||
{ \
|
{ \
|
||||||
@@ -39352,6 +39322,8 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
|
#undef JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
#undef JSON_DISABLE_UNLOCKED_FILE_IO
|
||||||
#undef JSON_UNLOCKED_FILE_IO
|
#undef JSON_UNLOCKED_FILE_IO
|
||||||
#undef JSON_DTOA_SSE2
|
#undef JSON_DTOA_SSE2
|
||||||
#undef JSON_DTOA_NEON
|
#undef JSON_DTOA_NEON
|
||||||
@@ -39386,8 +39358,6 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
#undef JSON_STRICT_BINARY_UTF8
|
#undef JSON_STRICT_BINARY_UTF8
|
||||||
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||||
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||||
#undef JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#undef JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
|
|||||||
Generated
+4
-26
@@ -71,14 +71,6 @@
|
|||||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define JSON_DISABLE_STREAM_BULK_SCAN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define JSON_DISABLE_UNLOCKED_FILE_IO 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -127,26 +119,14 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN _dsbs
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO _dufio
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j
|
#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, i, j) \
|
#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, i, j)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -157,9 +137,7 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
||||||
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS, \
|
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO)
|
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ endif()
|
|||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
# avoid stack overflow, see https://github.com/nlohmann/json/issues/2955
|
# avoid stack overflow, see https://github.com/nlohmann/json/issues/2955
|
||||||
json_test_set_test_options("test-bon8;test-cbor;test-msgpack;test-ubjson;test-bjdata;test-bjdata_round_trip_draft2;test-bjdata_round_trip_draft3;test-binary_formats" LINK_OPTIONS /STACK:4000000)
|
json_test_set_test_options("test-bon8;test-cbor;test-msgpack;test-ubjson;test-bjdata;test-binary_formats" LINK_OPTIONS /STACK:4000000)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# disable exceptions for test-disabled_exceptions
|
# disable exceptions for test-disabled_exceptions
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ foreach(test default noversion)
|
|||||||
target_compile_definitions(abi_config_${test}_all_tags PRIVATE
|
target_compile_definitions(abi_config_${test}_all_tags PRIVATE
|
||||||
JSON_DIAGNOSTICS=1
|
JSON_DIAGNOSTICS=1
|
||||||
JSON_DIAGNOSTIC_POSITIONS=1
|
JSON_DIAGNOSTIC_POSITIONS=1
|
||||||
JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1
|
JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)
|
||||||
JSON_DISABLE_STREAM_BULK_SCAN=1
|
|
||||||
JSON_DISABLE_UNLOCKED_FILE_IO=1)
|
|
||||||
target_link_libraries(abi_config_${test}_all_tags PRIVATE abi_compat_main)
|
target_link_libraries(abi_config_${test}_all_tags PRIVATE abi_compat_main)
|
||||||
add_test(
|
add_test(
|
||||||
NAME test-abi_config_${test}_all_tags
|
NAME test-abi_config_${test}_all_tags
|
||||||
|
|||||||
@@ -52,14 +52,6 @@ TEST_CASE("default namespace")
|
|||||||
expected += "_ekmo";
|
expected += "_ekmo";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
expected += "_dsbs";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
expected += "_dufio";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
||||||
|
|||||||
@@ -53,14 +53,6 @@ TEST_CASE("default namespace without version component")
|
|||||||
expected += "_ekmo";
|
expected += "_ekmo";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
expected += "_dsbs";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
expected += "_dufio";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
expected += "::basic_json";
|
expected += "::basic_json";
|
||||||
|
|
||||||
// fallback for Clang
|
// fallback for Clang
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint> // uint8_t
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
#include "round_trip_corpus.hpp"
|
|
||||||
|
|
||||||
// The BJData round-trip invariants, split by the BJData version that turns a
|
|
||||||
// corpus value into j1 (unit-bjdata_round_trip_draft2.cpp and
|
|
||||||
// unit-bjdata_round_trip_draft3.cpp). They were part of unit-bjdata.cpp, but
|
|
||||||
// took 80 % of its time; as separate tests, both halves run in parallel and
|
|
||||||
// stay below the ctest timeout under Valgrind.
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
|
|
||||||
// This checks what the parse_bjdata_fuzzer driver checks (see
|
|
||||||
// tests/src/fuzzer-parse_bjdata.cpp), so that a regression shows up in CI
|
|
||||||
// rather than as an OSS-Fuzz report: every value from_bjdata() returns
|
|
||||||
// (j1) can be serialized with any combination of options, the result can
|
|
||||||
// be parsed back (j2), and serializing j2 again with the same options
|
|
||||||
// yields a value-equal result.
|
|
||||||
//
|
|
||||||
// Beyond the driver, this also checks that j2 equals j1 and that
|
|
||||||
// serializing j2 reproduces the exact bytes, both except for values that
|
|
||||||
// contain a binary value: a binary value is only written as a binary
|
|
||||||
// value with Draft 3's optimized binary array, and otherwise read back as
|
|
||||||
// an array of integers, for which the writer may choose different (but
|
|
||||||
// equally valid) type markers when it is serialized again (see #5494).
|
|
||||||
//
|
|
||||||
// Values are compared with dump() rather than operator==, because a NaN
|
|
||||||
// never compares equal to itself.
|
|
||||||
//
|
|
||||||
// Only the corpus values written with @a initial_version become j1; each j1
|
|
||||||
// is then serialized with every combination of options, both versions
|
|
||||||
// included.
|
|
||||||
inline void check_bjdata_round_trip(const nlohmann::json::bjdata_version_t initial_version)
|
|
||||||
{
|
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
struct options
|
|
||||||
{
|
|
||||||
bool use_size;
|
|
||||||
bool use_type;
|
|
||||||
json::bjdata_version_t version;
|
|
||||||
};
|
|
||||||
const std::vector<options> all_options =
|
|
||||||
{
|
|
||||||
{false, false, json::bjdata_version_t::draft2},
|
|
||||||
{true, false, json::bjdata_version_t::draft2},
|
|
||||||
{true, true, json::bjdata_version_t::draft2},
|
|
||||||
{false, false, json::bjdata_version_t::draft3},
|
|
||||||
{true, false, json::bjdata_version_t::draft3},
|
|
||||||
{true, true, json::bjdata_version_t::draft3},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& j0 : utils::round_trip_corpus::values())
|
|
||||||
{
|
|
||||||
// turn the corpus value into a value as from_bjdata() returns it
|
|
||||||
for (const auto& initial : all_options)
|
|
||||||
{
|
|
||||||
if (initial.version != initial_version)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const json j1 = json::from_bjdata(json::to_bjdata(j0, initial.use_size, initial.use_type, initial.version));
|
|
||||||
const bool has_binary = utils::round_trip_corpus::contains_binary(j1);
|
|
||||||
|
|
||||||
for (const auto& o : all_options)
|
|
||||||
{
|
|
||||||
INFO("j1 = " << j1.dump() << ", use_size = " << o.use_size << ", use_type = " << o.use_type
|
|
||||||
<< ", draft3 = " << (o.version == json::bjdata_version_t::draft3));
|
|
||||||
|
|
||||||
const std::vector<std::uint8_t> vec = json::to_bjdata(j1, o.use_size, o.use_type, o.version);
|
|
||||||
json j2;
|
|
||||||
// anything the library writes must be parsable by the library
|
|
||||||
REQUIRE_NOTHROW(j2 = json::from_bjdata(vec));
|
|
||||||
const std::vector<std::uint8_t> vec2 = json::to_bjdata(j2, o.use_size, o.use_type, o.version);
|
|
||||||
CHECK(json::from_bjdata(vec2).dump() == j2.dump());
|
|
||||||
|
|
||||||
if (!has_binary)
|
|
||||||
{
|
|
||||||
CHECK(j2.dump() == j1.dump());
|
|
||||||
CHECK(vec2 == vec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace utils
|
|
||||||
@@ -28,6 +28,7 @@ using ordered_json = nlohmann::ordered_json;
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "make_test_data_available.hpp"
|
#include "make_test_data_available.hpp"
|
||||||
|
#include "round_trip_corpus.hpp"
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
#include "sax_countdown.hpp"
|
#include "sax_countdown.hpp"
|
||||||
using utils::SaxCountdown;
|
using utils::SaxCountdown;
|
||||||
@@ -4440,6 +4441,70 @@ TEST_CASE("BJData use_type requires use_size")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("BJData round-trip invariants")
|
||||||
|
{
|
||||||
|
// This checks what the parse_bjdata_fuzzer driver checks (see
|
||||||
|
// tests/src/fuzzer-parse_bjdata.cpp), so that a regression shows up in CI
|
||||||
|
// rather than as an OSS-Fuzz report: every value from_bjdata() returns
|
||||||
|
// (j1) can be serialized with any combination of options, the result can
|
||||||
|
// be parsed back (j2), and serializing j2 again with the same options
|
||||||
|
// yields a value-equal result.
|
||||||
|
//
|
||||||
|
// Beyond the driver, this also checks that j2 equals j1 and that
|
||||||
|
// serializing j2 reproduces the exact bytes, both except for values that
|
||||||
|
// contain a binary value: a binary value is only written as a binary
|
||||||
|
// value with Draft 3's optimized binary array, and otherwise read back as
|
||||||
|
// an array of integers, for which the writer may choose different (but
|
||||||
|
// equally valid) type markers when it is serialized again (see #5494).
|
||||||
|
//
|
||||||
|
// Values are compared with dump() rather than operator==, because a NaN
|
||||||
|
// never compares equal to itself.
|
||||||
|
struct options
|
||||||
|
{
|
||||||
|
bool use_size;
|
||||||
|
bool use_type;
|
||||||
|
json::bjdata_version_t version;
|
||||||
|
};
|
||||||
|
const std::vector<options> all_options =
|
||||||
|
{
|
||||||
|
{false, false, json::bjdata_version_t::draft2},
|
||||||
|
{true, false, json::bjdata_version_t::draft2},
|
||||||
|
{true, true, json::bjdata_version_t::draft2},
|
||||||
|
{false, false, json::bjdata_version_t::draft3},
|
||||||
|
{true, false, json::bjdata_version_t::draft3},
|
||||||
|
{true, true, json::bjdata_version_t::draft3},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& j0 : utils::round_trip_corpus::values())
|
||||||
|
{
|
||||||
|
// turn the corpus value into a value as from_bjdata() returns it
|
||||||
|
for (const auto& initial : all_options)
|
||||||
|
{
|
||||||
|
const json j1 = json::from_bjdata(json::to_bjdata(j0, initial.use_size, initial.use_type, initial.version));
|
||||||
|
const bool has_binary = utils::round_trip_corpus::contains_binary(j1);
|
||||||
|
|
||||||
|
for (const auto& o : all_options)
|
||||||
|
{
|
||||||
|
INFO("j1 = " << j1.dump() << ", use_size = " << o.use_size << ", use_type = " << o.use_type
|
||||||
|
<< ", draft3 = " << (o.version == json::bjdata_version_t::draft3));
|
||||||
|
|
||||||
|
const std::vector<std::uint8_t> vec = json::to_bjdata(j1, o.use_size, o.use_type, o.version);
|
||||||
|
json j2;
|
||||||
|
// anything the library writes must be parsable by the library
|
||||||
|
REQUIRE_NOTHROW(j2 = json::from_bjdata(vec));
|
||||||
|
const std::vector<std::uint8_t> vec2 = json::to_bjdata(j2, o.use_size, o.use_type, o.version);
|
||||||
|
CHECK(json::from_bjdata(vec2).dump() == j2.dump());
|
||||||
|
|
||||||
|
if (!has_binary)
|
||||||
|
{
|
||||||
|
CHECK(j2.dump() == j1.dump());
|
||||||
|
CHECK(vec2 == vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("BJData round trip of a binary value is value-stable, not byte-stable")
|
TEST_CASE("BJData round trip of a binary value is value-stable, not byte-stable")
|
||||||
{
|
{
|
||||||
// OSS-Fuzz issue 474480402: a Draft 3 optimized binary array is read as a
|
// OSS-Fuzz issue 474480402: a Draft 3 optimized binary array is read as a
|
||||||
|
|||||||
@@ -1,22 +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"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
#include "bjdata_round_trip.hpp"
|
|
||||||
|
|
||||||
// split from unit-bjdata.cpp, see bjdata_round_trip.hpp; the other half is in
|
|
||||||
// unit-bjdata_round_trip_draft3.cpp
|
|
||||||
|
|
||||||
TEST_CASE("BJData round-trip invariants (j1 written with Draft 2)")
|
|
||||||
{
|
|
||||||
utils::check_bjdata_round_trip(json::bjdata_version_t::draft2);
|
|
||||||
}
|
|
||||||
@@ -1,22 +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"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
#include "bjdata_round_trip.hpp"
|
|
||||||
|
|
||||||
// split from unit-bjdata.cpp, see bjdata_round_trip.hpp; the other half is in
|
|
||||||
// unit-bjdata_round_trip_draft2.cpp
|
|
||||||
|
|
||||||
TEST_CASE("BJData round-trip invariants (j1 written with Draft 3)")
|
|
||||||
{
|
|
||||||
utils::check_bjdata_round_trip(json::bjdata_version_t::draft3);
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
|
#include <cctype> // toupper
|
||||||
#include <cstdint> // uint32_t, uint64_t
|
#include <cstdint> // uint32_t, uint64_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
#include <cmath> // isfinite
|
#include <cmath> // isfinite
|
||||||
@@ -335,8 +336,6 @@ TEST_CASE("lexer number fast path")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
// this section reports parse errors, which abort when exceptions are off
|
|
||||||
SECTION("digits accumulated while scanning")
|
SECTION("digits accumulated while scanning")
|
||||||
{
|
{
|
||||||
// The fast path accumulates the digits while it validates them, eight
|
// The fast path accumulates the digits while it validates them, eight
|
||||||
@@ -415,7 +414,6 @@ TEST_CASE("lexer number fast path")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
SECTION("integers that do not fit a narrower type become floats")
|
SECTION("integers that do not fit a narrower type become floats")
|
||||||
{
|
{
|
||||||
@@ -599,6 +597,63 @@ TEST_CASE("lexer number fast path")
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("lexer literal fast path")
|
||||||
|
{
|
||||||
|
// For contiguous input, the rest of a literal is compared at once, and a
|
||||||
|
// mismatch is left to the byte path, which reports the error at the
|
||||||
|
// character that differs. Every truncation and every wrong character of
|
||||||
|
// each literal, at the end of the input or followed by more text, must
|
||||||
|
// give the byte path's result (a std::list is read one character at a
|
||||||
|
// time).
|
||||||
|
const auto outcome = [](const std::string & doc, bool byte_path) -> std::string
|
||||||
|
{
|
||||||
|
JSON_TRY
|
||||||
|
{
|
||||||
|
const std::list<char> chars(doc.begin(), doc.end());
|
||||||
|
return (byte_path ? json::parse(chars.begin(), chars.end()) : json::parse(doc)).dump();
|
||||||
|
}
|
||||||
|
JSON_CATCH (const json::exception & e)
|
||||||
|
{
|
||||||
|
return e.what();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> mismatches;
|
||||||
|
for (const std::string literal :
|
||||||
|
{"true", "false", "null"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
std::vector<std::string> variants;
|
||||||
|
for (std::size_t length = 1; length <= literal.size(); ++length)
|
||||||
|
{
|
||||||
|
variants.push_back(literal.substr(0, length));
|
||||||
|
}
|
||||||
|
for (std::size_t i = 1; i < literal.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string changed = literal;
|
||||||
|
changed[i] = 'X';
|
||||||
|
variants.push_back(changed);
|
||||||
|
changed[i] = static_cast<char>(std::toupper(static_cast<unsigned char>(literal[i])));
|
||||||
|
variants.push_back(changed);
|
||||||
|
}
|
||||||
|
for (const auto& variant : variants)
|
||||||
|
{
|
||||||
|
for (const std::string& doc :
|
||||||
|
{
|
||||||
|
variant, "[" + variant + "]", "[" + variant, variant + " ", variant + "1", "{\"k\":" + variant + "}"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if (outcome(doc, false) != outcome(doc, true))
|
||||||
|
{
|
||||||
|
mismatches.push_back(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CAPTURE(mismatches)
|
||||||
|
CHECK(mismatches.empty());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("lexer string fast path")
|
TEST_CASE("lexer string fast path")
|
||||||
{
|
{
|
||||||
// Build a byte string from explicit values: a hex escape in a string
|
// Build a byte string from explicit values: a hex escape in a string
|
||||||
|
|||||||
@@ -198,11 +198,11 @@ TEST_CASE("copy constructor and destructor across value types")
|
|||||||
const json src_uint = 7u;
|
const json src_uint = 7u;
|
||||||
const json src_float = 2.5;
|
const json src_float = 2.5;
|
||||||
|
|
||||||
json c_null(src_null); // NOLINT(performance-unnecessary-copy-initialization): tests the copy constructor
|
json c_null(src_null);
|
||||||
json c_bool(src_bool); // NOLINT(performance-unnecessary-copy-initialization): tests the copy constructor
|
json c_bool(src_bool);
|
||||||
json c_int(src_int); // NOLINT(performance-unnecessary-copy-initialization): tests the copy constructor
|
json c_int(src_int);
|
||||||
json c_uint(src_uint); // NOLINT(performance-unnecessary-copy-initialization): tests the copy constructor
|
json c_uint(src_uint);
|
||||||
json c_float(src_float); // NOLINT(performance-unnecessary-copy-initialization): tests the copy constructor
|
json c_float(src_float);
|
||||||
|
|
||||||
CHECK(c_null.is_null());
|
CHECK(c_null.is_null());
|
||||||
CHECK(c_bool == true);
|
CHECK(c_bool == true);
|
||||||
@@ -300,12 +300,12 @@ TEST_CASE("copy constructor and destructor across value types")
|
|||||||
REQUIRE(v.size() == 70);
|
REQUIRE(v.size() == 70);
|
||||||
|
|
||||||
std::vector<json> copy(v);
|
std::vector<json> copy(v);
|
||||||
CHECK((copy == v));
|
CHECK(copy == v);
|
||||||
|
|
||||||
// mutating the copy leaves the original alone
|
// mutating the copy leaves the original alone
|
||||||
copy[5] = "changed";
|
copy[5] = "changed";
|
||||||
copy[6].push_back(1);
|
copy[6].push_back(1);
|
||||||
CHECK((copy != v));
|
CHECK(copy != v);
|
||||||
CHECK(v[5] == std::string(5, 'x'));
|
CHECK(v[5] == std::string(5, 'x'));
|
||||||
CHECK(v[6].size() == 3);
|
CHECK(v[6].size() == 3);
|
||||||
|
|
||||||
|
|||||||
@@ -817,8 +817,8 @@ template<class Key, class T, class IgnoredLess = std::less<Key>,
|
|||||||
struct relocating_map : nlohmann::ordered_map<Key, T, IgnoredLess, Allocator>
|
struct relocating_map : nlohmann::ordered_map<Key, T, IgnoredLess, Allocator>
|
||||||
{
|
{
|
||||||
using base_type = nlohmann::ordered_map<Key, T, IgnoredLess, Allocator>;
|
using base_type = nlohmann::ordered_map<Key, T, IgnoredLess, Allocator>;
|
||||||
using iterator = typename base_type::iterator;
|
using typename base_type::iterator;
|
||||||
using Container = typename base_type::Container;
|
using typename base_type::Container;
|
||||||
using base_type::base_type;
|
using base_type::base_type;
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
|||||||
@@ -170,7 +170,6 @@ TEST_CASE("parsing from a FILE*")
|
|||||||
check_unlocked(f.get());
|
check_unlocked(f.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
SECTION("the file is unlocked after a parse error")
|
SECTION("the file is unlocked after a parse error")
|
||||||
{
|
{
|
||||||
const file_ptr f = open_with(name, "[1, 2, x]");
|
const file_ptr f = open_with(name, "[1, 2, x]");
|
||||||
@@ -187,8 +186,6 @@ TEST_CASE("parsing from a FILE*")
|
|||||||
CHECK(std::string(rest.data()) == "]");
|
CHECK(std::string(rest.data()) == "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(JSON_NOEXCEPTION)
|
|
||||||
|
|
||||||
SECTION("the file can be parsed again and read with stdio afterwards")
|
SECTION("the file can be parsed again and read with stdio afterwards")
|
||||||
{
|
{
|
||||||
const file_ptr f = open_with(name, "[1] [2] tail");
|
const file_ptr f = open_with(name, "[1] [2] tail");
|
||||||
|
|||||||
@@ -1637,7 +1637,6 @@ TEST_CASE_TEMPLATE("reverse iteration and member access", Json, nlohmann::json,
|
|||||||
{
|
{
|
||||||
Json j = {1, 2, 3, 4};
|
Json j = {1, 2, 3, 4};
|
||||||
|
|
||||||
const std::vector<int> reversed = {4, 3, 2, 1};
|
|
||||||
auto it = j.end();
|
auto it = j.end();
|
||||||
std::vector<int> seen;
|
std::vector<int> seen;
|
||||||
while (it != j.begin())
|
while (it != j.begin())
|
||||||
@@ -1645,7 +1644,7 @@ TEST_CASE_TEMPLATE("reverse iteration and member access", Json, nlohmann::json,
|
|||||||
--it;
|
--it;
|
||||||
seen.push_back(it->template get<int>());
|
seen.push_back(it->template get<int>());
|
||||||
}
|
}
|
||||||
CHECK(seen == reversed);
|
CHECK(seen == std::vector<int> {4, 3, 2, 1});
|
||||||
|
|
||||||
auto it2 = j.end();
|
auto it2 = j.end();
|
||||||
auto old = it2--;
|
auto old = it2--;
|
||||||
@@ -1657,14 +1656,14 @@ TEST_CASE_TEMPLATE("reverse iteration and member access", Json, nlohmann::json,
|
|||||||
{
|
{
|
||||||
seen.push_back(r->template get<int>());
|
seen.push_back(r->template get<int>());
|
||||||
}
|
}
|
||||||
CHECK(seen == reversed);
|
CHECK(seen == std::vector<int> {4, 3, 2, 1});
|
||||||
|
|
||||||
seen.clear();
|
seen.clear();
|
||||||
for (auto r = j.crbegin(); r != j.crend(); ++r)
|
for (auto r = j.crbegin(); r != j.crend(); ++r)
|
||||||
{
|
{
|
||||||
seen.push_back(r->template get<int>());
|
seen.push_back(*r);
|
||||||
}
|
}
|
||||||
CHECK(seen == reversed);
|
CHECK(seen == std::vector<int> {4, 3, 2, 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("object: -- and rbegin/rend")
|
SECTION("object: -- and rbegin/rend")
|
||||||
|
|||||||
@@ -1230,11 +1230,11 @@ TEST_CASE("JSON pointer array index tokens match strtoull semantics")
|
|||||||
|
|
||||||
SECTION("random tokens")
|
SECTION("random tokens")
|
||||||
{
|
{
|
||||||
std::mt19937 gen(42); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
std::mt19937 gen(42);
|
||||||
const std::string alphabet = "0123456789012345678901234567890123456789-+ ex";
|
const std::string alphabet = "0123456789012345678901234567890123456789-+ ex";
|
||||||
for (int i = 0; i < 5000; ++i)
|
for (int i = 0; i < 5000; ++i)
|
||||||
{
|
{
|
||||||
std::string token(gen() % 23, '0');
|
std::string token(static_cast<std::size_t>(gen() % 23), '0');
|
||||||
for (auto& c : token)
|
for (auto& c : token)
|
||||||
{
|
{
|
||||||
c = alphabet[gen() % alphabet.size()];
|
c = alphabet[gen() % alphabet.size()];
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ using nlohmann::json;
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
TEST_CASE("tests on very large JSONs")
|
TEST_CASE("tests on very large JSONs")
|
||||||
{
|
{
|
||||||
@@ -53,6 +55,24 @@ const json* innermost_value(const json& j, std::size_t& depth)
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The text of a value nested depth levels deep around the number 0. Level i is
|
||||||
|
// an array if pattern[i % pattern.size()] is '[', and otherwise an object with
|
||||||
|
// the single member "a", which every object type enumerates in the same order.
|
||||||
|
std::string nested_text(std::size_t depth, const std::string& pattern)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
std::string closing;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
const bool array = pattern[i % pattern.size()] == '[';
|
||||||
|
text += array ? "[" : "{\"a\":";
|
||||||
|
closing += array ? ']' : '}';
|
||||||
|
}
|
||||||
|
text += '0';
|
||||||
|
text.append(closing.rbegin(), closing.rend());
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("tests on deeply nested JSONs")
|
TEST_CASE("tests on deeply nested JSONs")
|
||||||
@@ -224,4 +244,385 @@ TEST_CASE("tests on deeply nested JSONs")
|
|||||||
CHECK(*innermost_value(j, unused) == 0);
|
CHECK(*innermost_value(j, unused) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("issue #5650 - stack overflow converting between specializations")
|
||||||
|
{
|
||||||
|
const std::vector<std::string> patterns = {"[", "{", "[{"};
|
||||||
|
|
||||||
|
SECTION("json to ordered_json")
|
||||||
|
{
|
||||||
|
for (const auto& pattern : patterns)
|
||||||
|
{
|
||||||
|
CAPTURE(pattern)
|
||||||
|
const std::string text = nested_text(depth, pattern);
|
||||||
|
const json j = json::parse(text);
|
||||||
|
|
||||||
|
const nlohmann::ordered_json converted = j;
|
||||||
|
CHECK(converted.dump() == text);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ordered_json to json")
|
||||||
|
{
|
||||||
|
for (const auto& pattern : patterns)
|
||||||
|
{
|
||||||
|
CAPTURE(pattern)
|
||||||
|
const std::string text = nested_text(depth, pattern);
|
||||||
|
const nlohmann::ordered_json o = nlohmann::ordered_json::parse(text);
|
||||||
|
|
||||||
|
const json converted = o;
|
||||||
|
CHECK(converted.dump() == text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("get<ordered_json>()")
|
||||||
|
{
|
||||||
|
for (const auto& pattern : patterns)
|
||||||
|
{
|
||||||
|
CAPTURE(pattern)
|
||||||
|
const std::string text = nested_text(depth, pattern);
|
||||||
|
const json j = json::parse(text);
|
||||||
|
|
||||||
|
CHECK(j.get<nlohmann::ordered_json>().dump() == text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("depths around the bound of the recursive descent")
|
||||||
|
{
|
||||||
|
for (std::size_t d = 1; d <= 300; ++d)
|
||||||
|
{
|
||||||
|
CAPTURE(d)
|
||||||
|
for (const auto& pattern : patterns)
|
||||||
|
{
|
||||||
|
CAPTURE(pattern)
|
||||||
|
const std::string text = nested_text(d, pattern);
|
||||||
|
const json j = json::parse(text);
|
||||||
|
|
||||||
|
const nlohmann::ordered_json converted = j;
|
||||||
|
CHECK(converted.dump() == text);
|
||||||
|
const json back = converted;
|
||||||
|
CHECK(back.dump() == text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("values below the bound are converted as values above it")
|
||||||
|
{
|
||||||
|
// Bury a value below the bound, where it is converted without the
|
||||||
|
// call stack, and compare it with the same value converted on its
|
||||||
|
// own by the containers' range constructors. Its objects have
|
||||||
|
// members that the two object types enumerate in different orders.
|
||||||
|
const auto bury = [](nlohmann::ordered_json value)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < 200; ++i)
|
||||||
|
{
|
||||||
|
value = nlohmann::ordered_json::array({std::move(value)});
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
const auto dig = [](const json & value)
|
||||||
|
{
|
||||||
|
const json* current = &value;
|
||||||
|
for (std::size_t i = 0; i < 200; ++i)
|
||||||
|
{
|
||||||
|
current = ¤t->at(0);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
};
|
||||||
|
|
||||||
|
nlohmann::ordered_json value = nlohmann::ordered_json::object();
|
||||||
|
value["z"] = {1, -2, 3U, 4.5, true, nullptr, "six", nlohmann::ordered_json::binary({7, 8}, 9),
|
||||||
|
nlohmann::ordered_json::binary({10}), nlohmann::ordered_json::array(), nlohmann::ordered_json::object()
|
||||||
|
};
|
||||||
|
value["y"] = {{"x", {{"w", 1}, {"v", 2}}}, {"u", {3, {{"t", 4}, {"s", 5}}}}};
|
||||||
|
value["r"] = nlohmann::ordered_json::array({nlohmann::ordered_json(nlohmann::ordered_json::value_t::discarded)});
|
||||||
|
|
||||||
|
const json converted_above = value;
|
||||||
|
const json buried = bury(value);
|
||||||
|
const json& converted_below = *dig(buried);
|
||||||
|
|
||||||
|
CHECK(converted_below.dump() == converted_above.dump());
|
||||||
|
CHECK(converted_below.at("z").at(7).get_binary().subtype() == 9);
|
||||||
|
CHECK_FALSE(converted_below.at("z").at(8).get_binary().has_subtype());
|
||||||
|
CHECK(converted_below.at("r").at(0).is_discarded());
|
||||||
|
|
||||||
|
// a discarded value is never equal to anything, so compare the rest
|
||||||
|
value.erase("r");
|
||||||
|
const json without_discarded_above = value;
|
||||||
|
const json without_discarded_buried = bury(value);
|
||||||
|
CHECK(*dig(without_discarded_buried) == without_discarded_above);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
json nested_array(const std::size_t depth, json leaf)
|
||||||
|
{
|
||||||
|
json j = std::move(leaf);
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
json a = json::array();
|
||||||
|
a.push_back(std::move(j));
|
||||||
|
j = std::move(a);
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
json nested_object(const std::size_t depth, json leaf)
|
||||||
|
{
|
||||||
|
json j = std::move(leaf);
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
json o = json::object();
|
||||||
|
o["k"] = std::move(j);
|
||||||
|
j = std::move(o);
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("issue #5392 - binary writers on deeply nested values")
|
||||||
|
{
|
||||||
|
// 200 is past the point where the writers stop recursing, and still
|
||||||
|
// shallow enough that from_* and operator== (which still recurse) are fine.
|
||||||
|
const json deep_array = nested_array(200, json(0));
|
||||||
|
const json deep_object = nested_object(200, json("x"));
|
||||||
|
const json empty_array = nested_array(200, json::array());
|
||||||
|
const json empty_object = nested_object(200, json::object());
|
||||||
|
const json mixed = nested_object(80, nested_array(80, json(true)));
|
||||||
|
|
||||||
|
SECTION("roundtrip past the recursion bound")
|
||||||
|
{
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(deep_array)) == deep_array);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(deep_array)) == deep_array);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(deep_array)) == deep_array);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(deep_array, true, false)) == deep_array);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(deep_array, true, true)) == deep_array);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(deep_array)) == deep_array);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(deep_array)) == deep_array);
|
||||||
|
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(deep_object)) == deep_object);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(deep_object)) == deep_object);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(deep_object)) == deep_object);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(deep_object, true, true)) == deep_object);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(deep_object)) == deep_object);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(deep_object)) == deep_object);
|
||||||
|
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(empty_array)) == empty_array);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(empty_array)) == empty_array);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(empty_array)) == empty_array);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(empty_array, true, true)) == empty_array);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(empty_array)) == empty_array);
|
||||||
|
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(empty_object)) == empty_object);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(empty_object)) == empty_object);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(empty_object)) == empty_object);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(empty_object)) == empty_object);
|
||||||
|
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(mixed)) == mixed);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(mixed)) == mixed);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(mixed)) == mixed);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(mixed)) == mixed);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(mixed)) == mixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("the two ways of writing a value meet at the bound")
|
||||||
|
{
|
||||||
|
for (std::size_t depth = 120; depth <= 140; ++depth)
|
||||||
|
{
|
||||||
|
CAPTURE(depth)
|
||||||
|
|
||||||
|
const json array = nested_array(depth, json(7));
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(array)) == array);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(array)) == array);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(array, true, true)) == array);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(array)) == array);
|
||||||
|
|
||||||
|
const json object = nested_object(depth, json(7));
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(object)) == object);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(object)) == object);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(object)) == object);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(object)) == object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("a BJData ndarray below the bound is still an ndarray")
|
||||||
|
{
|
||||||
|
const json ndarray = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 3}}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}});
|
||||||
|
const json invalid = json({{"_ArrayType_", "nope"}, {"_ArraySize_", {1}}, {"_ArrayData_", {1}}});
|
||||||
|
|
||||||
|
const json deep_ndarray = nested_array(140, ndarray);
|
||||||
|
const json deep_invalid = nested_array(140, invalid);
|
||||||
|
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(deep_ndarray)) == deep_ndarray);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(deep_invalid)) == deep_invalid);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(ndarray)) == ndarray);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("byte-exact across the switch-over")
|
||||||
|
{
|
||||||
|
// nested one-element arrays around the recursion bound: the exact
|
||||||
|
// bytes a writer produces do not depend on whether it stayed on the
|
||||||
|
// call stack or moved to the heap one partway through
|
||||||
|
for (const std::size_t depth :
|
||||||
|
{
|
||||||
|
nlohmann::detail::recursion_depth_limit() - 1, nlohmann::detail::recursion_depth_limit(),
|
||||||
|
nlohmann::detail::recursion_depth_limit() + 1, nlohmann::detail::recursion_depth_limit() + 2
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(depth)
|
||||||
|
const json array = nested_array(depth, json(0));
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> expected_cbor(depth, 0x81);
|
||||||
|
expected_cbor.push_back(0x00);
|
||||||
|
CHECK(json::to_cbor(array) == expected_cbor);
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> expected_msgpack(depth, 0x91);
|
||||||
|
expected_msgpack.push_back(0x00);
|
||||||
|
CHECK(json::to_msgpack(array) == expected_msgpack);
|
||||||
|
|
||||||
|
std::string expected_ubjson(depth, '[');
|
||||||
|
expected_ubjson += "i";
|
||||||
|
expected_ubjson += '\0';
|
||||||
|
expected_ubjson.append(depth, ']');
|
||||||
|
const auto packed_ubjson = json::to_ubjson(array);
|
||||||
|
CHECK(std::string(packed_ubjson.begin(), packed_ubjson.end()) == expected_ubjson);
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> expected_bon8(depth, 0x81);
|
||||||
|
expected_bon8.push_back(0x90);
|
||||||
|
CHECK(json::to_bon8(array) == expected_bon8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("a deep object, and a BJData ndarray, past the recursion bound")
|
||||||
|
{
|
||||||
|
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
||||||
|
|
||||||
|
const json object = nested_object(depth, json(42));
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(object)) == object);
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(object)) == object);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(object, true, true)) == object);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(object)) == object);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(object)) == object);
|
||||||
|
|
||||||
|
const json ndarray = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 3}}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}});
|
||||||
|
const json deep_ndarray = nested_array(depth, ndarray);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(deep_ndarray)) == deep_ndarray);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("a discarded value past the recursion bound still throws type_error.321")
|
||||||
|
{
|
||||||
|
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
||||||
|
const json discarded_leaf(json::value_t::discarded);
|
||||||
|
const json deep_discarded = nested_array(depth, discarded_leaf);
|
||||||
|
|
||||||
|
// with diagnostics, the message names the path to the discarded leaf
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
std::string path;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
path += "/0";
|
||||||
|
}
|
||||||
|
const std::string prefix = "[json.exception.type_error.321] (" + path + ") ";
|
||||||
|
#else
|
||||||
|
const std::string prefix = "[json.exception.type_error.321] ";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_cbor(deep_discarded), (prefix + "cannot serialize discarded value to CBOR").c_str(), json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_msgpack(deep_discarded), (prefix + "cannot serialize discarded value to MessagePack").c_str(), json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(deep_discarded), (prefix + "cannot serialize discarded value to UBJSON").c_str(), json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(deep_discarded), (prefix + "cannot serialize discarded value to BJData").c_str(), json::type_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("BON8 past the recursion bound: discarded values and errors")
|
||||||
|
{
|
||||||
|
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
||||||
|
|
||||||
|
// BON8 rejects a discarded value (type_error.321), deep or not
|
||||||
|
const json deep_discarded = nested_array(depth, json(json::value_t::discarded));
|
||||||
|
CHECK_THROWS_AS(json::to_bon8(deep_discarded), json::type_error);
|
||||||
|
|
||||||
|
// errors are thrown from below the bound as from above it
|
||||||
|
const json deep_invalid_string = nested_array(depth, json(std::string("\x80")));
|
||||||
|
CHECK_THROWS_AS(json::to_bon8(deep_invalid_string), json::type_error);
|
||||||
|
|
||||||
|
json deep_invalid_key = json::object();
|
||||||
|
deep_invalid_key[std::string("\x80")] = 1;
|
||||||
|
deep_invalid_key = nested_object(depth, deep_invalid_key);
|
||||||
|
CHECK_THROWS_AS(json::to_bon8(deep_invalid_key), json::type_error);
|
||||||
|
|
||||||
|
const json deep_too_large = nested_array(depth, json(9223372036854775808u));
|
||||||
|
CHECK_THROWS_AS(json::to_bon8(deep_too_large), json::out_of_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("does not overflow the C++ stack")
|
||||||
|
{
|
||||||
|
const std::size_t depth = 100000;
|
||||||
|
const json j = json::parse(std::string(depth, '[') + "0" + std::string(depth, ']'));
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> packed;
|
||||||
|
CHECK_NOTHROW(packed = json::to_cbor(j));
|
||||||
|
CHECK(json::from_cbor(packed) == j);
|
||||||
|
|
||||||
|
CHECK_NOTHROW(packed = json::to_msgpack(j));
|
||||||
|
CHECK(json::from_msgpack(packed) == j);
|
||||||
|
|
||||||
|
CHECK_NOTHROW(packed = json::to_ubjson(j));
|
||||||
|
CHECK(json::from_ubjson(packed) == j);
|
||||||
|
|
||||||
|
CHECK_NOTHROW(packed = json::to_ubjson(j, true, false));
|
||||||
|
CHECK(json::from_ubjson(packed) == j);
|
||||||
|
|
||||||
|
CHECK_NOTHROW(packed = json::to_bjdata(j));
|
||||||
|
CHECK(json::from_bjdata(packed) == j);
|
||||||
|
|
||||||
|
CHECK_NOTHROW(packed = json::to_bon8(j));
|
||||||
|
CHECK(json::from_bon8(packed) == j);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("BON8 containers with more than four elements past the recursion bound")
|
||||||
|
{
|
||||||
|
// such containers are closed with 0xFE, which the iterative writer
|
||||||
|
// must emit when it leaves them; strings next to each other must
|
||||||
|
// still be separated with 0xFF
|
||||||
|
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
||||||
|
json wide_array = json::array({1, "a", "b", json::array(), 5});
|
||||||
|
json wide_object = json({{"k1", "v"}, {"k2", 2}, {"k3", ""}, {"k4", json::object()}, {"k5", nullptr}});
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
wide_array = json::array({"s", wide_array, "t", wide_object, true});
|
||||||
|
wide_object = json({{"a", wide_object}, {"b", "x"}, {"c", "y"}, {"d", wide_array.size()}, {"e", "z"}});
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(wide_array)) == wide_array);
|
||||||
|
CHECK(json::from_bon8(json::to_bon8(wide_object)) == wide_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("regression test for https://issues.oss-fuzz.com/issues/566583014")
|
||||||
|
{
|
||||||
|
// 200000 nested one-element CBOR arrays, the innermost holding null;
|
||||||
|
// round-tripping this used to recurse once per level on the way back
|
||||||
|
// out through to_cbor(), deep enough to overflow the stack
|
||||||
|
std::vector<std::uint8_t> v(200000, 0x81);
|
||||||
|
v.push_back(0xf6);
|
||||||
|
const json j = json::from_cbor(v);
|
||||||
|
CHECK(json::to_cbor(j) == v);
|
||||||
|
|
||||||
|
// the MessagePack analogue: fixarray of 1 nesting down to nil
|
||||||
|
std::vector<std::uint8_t> v_msgpack(200000, 0x91);
|
||||||
|
v_msgpack.push_back(0xc0);
|
||||||
|
const json j_msgpack = json::from_msgpack(v_msgpack);
|
||||||
|
CHECK(json::to_msgpack(j_msgpack) == v_msgpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("regression test for https://issues.oss-fuzz.com/issues/572238015")
|
||||||
|
{
|
||||||
|
// the BON8 analogue: 200000 nested one-element arrays down to null
|
||||||
|
std::vector<std::uint8_t> v(200000, 0x81);
|
||||||
|
v.push_back(0xFA);
|
||||||
|
const json j = json::from_bon8(v);
|
||||||
|
CHECK(json::to_bon8(j) == v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,294 +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"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// split from unit-large_json.cpp so that both run in parallel and stay below
|
|
||||||
// the ctest timeout under Valgrind
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
json nested_array(const std::size_t depth, json leaf)
|
|
||||||
{
|
|
||||||
json j = std::move(leaf);
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
json a = json::array();
|
|
||||||
a.push_back(std::move(j));
|
|
||||||
j = std::move(a);
|
|
||||||
}
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
json nested_object(const std::size_t depth, json leaf)
|
|
||||||
{
|
|
||||||
json j = std::move(leaf);
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
json o = json::object();
|
|
||||||
o["k"] = std::move(j);
|
|
||||||
j = std::move(o);
|
|
||||||
}
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("issue #5392 - binary writers on deeply nested values")
|
|
||||||
{
|
|
||||||
// 200 is past the point where the writers stop recursing, and still
|
|
||||||
// shallow enough that from_* and operator== (which still recurse) are fine.
|
|
||||||
const json deep_array = nested_array(200, json(0));
|
|
||||||
const json deep_object = nested_object(200, json("x"));
|
|
||||||
const json empty_array = nested_array(200, json::array());
|
|
||||||
const json empty_object = nested_object(200, json::object());
|
|
||||||
const json mixed = nested_object(80, nested_array(80, json(true)));
|
|
||||||
|
|
||||||
SECTION("roundtrip past the recursion bound")
|
|
||||||
{
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(deep_array)) == deep_array);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(deep_array)) == deep_array);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(deep_array)) == deep_array);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(deep_array, true, false)) == deep_array);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(deep_array, true, true)) == deep_array);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(deep_array)) == deep_array);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(deep_array)) == deep_array);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(deep_object)) == deep_object);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(deep_object)) == deep_object);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(deep_object)) == deep_object);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(deep_object, true, true)) == deep_object);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(deep_object)) == deep_object);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(deep_object)) == deep_object);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(empty_array)) == empty_array);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(empty_array)) == empty_array);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(empty_array)) == empty_array);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(empty_array, true, true)) == empty_array);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(empty_array)) == empty_array);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(empty_object)) == empty_object);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(empty_object)) == empty_object);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(empty_object)) == empty_object);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(empty_object)) == empty_object);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(mixed)) == mixed);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(mixed)) == mixed);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(mixed)) == mixed);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(mixed)) == mixed);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(mixed)) == mixed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("the two ways of writing a value meet at the bound")
|
|
||||||
{
|
|
||||||
for (std::size_t depth = 120; depth <= 140; ++depth)
|
|
||||||
{
|
|
||||||
CAPTURE(depth)
|
|
||||||
|
|
||||||
const json array = nested_array(depth, json(7));
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(array)) == array);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(array)) == array);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(array, true, true)) == array);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(array)) == array);
|
|
||||||
|
|
||||||
const json object = nested_object(depth, json(7));
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(object)) == object);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(object)) == object);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(object)) == object);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(object)) == object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("a BJData ndarray below the bound is still an ndarray")
|
|
||||||
{
|
|
||||||
const json ndarray = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 3}}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}});
|
|
||||||
const json invalid = json({{"_ArrayType_", "nope"}, {"_ArraySize_", {1}}, {"_ArrayData_", {1}}});
|
|
||||||
|
|
||||||
const json deep_ndarray = nested_array(140, ndarray);
|
|
||||||
const json deep_invalid = nested_array(140, invalid);
|
|
||||||
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(deep_ndarray)) == deep_ndarray);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(deep_invalid)) == deep_invalid);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(ndarray)) == ndarray);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("byte-exact across the switch-over")
|
|
||||||
{
|
|
||||||
// nested one-element arrays around the recursion bound: the exact
|
|
||||||
// bytes a writer produces do not depend on whether it stayed on the
|
|
||||||
// call stack or moved to the heap one partway through
|
|
||||||
for (const std::size_t depth :
|
|
||||||
{
|
|
||||||
nlohmann::detail::recursion_depth_limit() - 1, nlohmann::detail::recursion_depth_limit(),
|
|
||||||
nlohmann::detail::recursion_depth_limit() + 1, nlohmann::detail::recursion_depth_limit() + 2
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(depth)
|
|
||||||
const json array = nested_array(depth, json(0));
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> expected_cbor(depth, 0x81);
|
|
||||||
expected_cbor.push_back(0x00);
|
|
||||||
CHECK(json::to_cbor(array) == expected_cbor);
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> expected_msgpack(depth, 0x91);
|
|
||||||
expected_msgpack.push_back(0x00);
|
|
||||||
CHECK(json::to_msgpack(array) == expected_msgpack);
|
|
||||||
|
|
||||||
std::string expected_ubjson(depth, '[');
|
|
||||||
expected_ubjson += "i";
|
|
||||||
expected_ubjson += '\0';
|
|
||||||
expected_ubjson.append(depth, ']');
|
|
||||||
const auto packed_ubjson = json::to_ubjson(array);
|
|
||||||
CHECK(std::string(packed_ubjson.begin(), packed_ubjson.end()) == expected_ubjson);
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> expected_bon8(depth, 0x81);
|
|
||||||
expected_bon8.push_back(0x90);
|
|
||||||
CHECK(json::to_bon8(array) == expected_bon8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("a deep object, and a BJData ndarray, past the recursion bound")
|
|
||||||
{
|
|
||||||
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
|
||||||
|
|
||||||
const json object = nested_object(depth, json(42));
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(object)) == object);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(object)) == object);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(object, true, true)) == object);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(object)) == object);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(object)) == object);
|
|
||||||
|
|
||||||
const json ndarray = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 3}}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}});
|
|
||||||
const json deep_ndarray = nested_array(depth, ndarray);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(deep_ndarray)) == deep_ndarray);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("a discarded value past the recursion bound still throws type_error.321")
|
|
||||||
{
|
|
||||||
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
|
||||||
const json discarded_leaf(json::value_t::discarded);
|
|
||||||
const json deep_discarded = nested_array(depth, discarded_leaf);
|
|
||||||
|
|
||||||
// with diagnostics, the message names the path to the discarded leaf
|
|
||||||
#if JSON_DIAGNOSTICS
|
|
||||||
std::string path;
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
path += "/0";
|
|
||||||
}
|
|
||||||
const std::string prefix = "[json.exception.type_error.321] (" + path + ") ";
|
|
||||||
#else
|
|
||||||
const std::string prefix = "[json.exception.type_error.321] ";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_cbor(deep_discarded), (prefix + "cannot serialize discarded value to CBOR").c_str(), json::type_error);
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_msgpack(deep_discarded), (prefix + "cannot serialize discarded value to MessagePack").c_str(), json::type_error);
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_ubjson(deep_discarded), (prefix + "cannot serialize discarded value to UBJSON").c_str(), json::type_error);
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bjdata(deep_discarded), (prefix + "cannot serialize discarded value to BJData").c_str(), json::type_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("BON8 past the recursion bound: discarded values and errors")
|
|
||||||
{
|
|
||||||
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
|
||||||
|
|
||||||
// BON8 rejects a discarded value (type_error.321), deep or not
|
|
||||||
const json deep_discarded = nested_array(depth, json(json::value_t::discarded));
|
|
||||||
CHECK_THROWS_AS(json::to_bon8(deep_discarded), json::type_error);
|
|
||||||
|
|
||||||
// errors are thrown from below the bound as from above it
|
|
||||||
const json deep_invalid_string = nested_array(depth, json(std::string("\x80")));
|
|
||||||
CHECK_THROWS_AS(json::to_bon8(deep_invalid_string), json::type_error);
|
|
||||||
|
|
||||||
json deep_invalid_key = json::object();
|
|
||||||
deep_invalid_key[std::string("\x80")] = 1;
|
|
||||||
deep_invalid_key = nested_object(depth, deep_invalid_key);
|
|
||||||
CHECK_THROWS_AS(json::to_bon8(deep_invalid_key), json::type_error);
|
|
||||||
|
|
||||||
const json deep_too_large = nested_array(depth, json(9223372036854775808u));
|
|
||||||
CHECK_THROWS_AS(json::to_bon8(deep_too_large), json::out_of_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("does not overflow the C++ stack")
|
|
||||||
{
|
|
||||||
const std::size_t depth = 100000;
|
|
||||||
const json j = json::parse(std::string(depth, '[') + "0" + std::string(depth, ']'));
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> packed;
|
|
||||||
CHECK_NOTHROW(packed = json::to_cbor(j));
|
|
||||||
CHECK(json::from_cbor(packed) == j);
|
|
||||||
|
|
||||||
CHECK_NOTHROW(packed = json::to_msgpack(j));
|
|
||||||
CHECK(json::from_msgpack(packed) == j);
|
|
||||||
|
|
||||||
CHECK_NOTHROW(packed = json::to_ubjson(j));
|
|
||||||
CHECK(json::from_ubjson(packed) == j);
|
|
||||||
|
|
||||||
CHECK_NOTHROW(packed = json::to_ubjson(j, true, false));
|
|
||||||
CHECK(json::from_ubjson(packed) == j);
|
|
||||||
|
|
||||||
CHECK_NOTHROW(packed = json::to_bjdata(j));
|
|
||||||
CHECK(json::from_bjdata(packed) == j);
|
|
||||||
|
|
||||||
CHECK_NOTHROW(packed = json::to_bon8(j));
|
|
||||||
CHECK(json::from_bon8(packed) == j);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("BON8 containers with more than four elements past the recursion bound")
|
|
||||||
{
|
|
||||||
// such containers are closed with 0xFE, which the iterative writer
|
|
||||||
// must emit when it leaves them; strings next to each other must
|
|
||||||
// still be separated with 0xFF
|
|
||||||
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 50;
|
|
||||||
json wide_array = json::array({1, "a", "b", json::array(), 5});
|
|
||||||
json wide_object = json({{"k1", "v"}, {"k2", 2}, {"k3", ""}, {"k4", json::object()}, {"k5", nullptr}});
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
wide_array = json::array({"s", wide_array, "t", wide_object, true});
|
|
||||||
wide_object = json({{"a", wide_object}, {"b", "x"}, {"c", "y"}, {"d", wide_array.size()}, {"e", "z"}});
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(wide_array)) == wide_array);
|
|
||||||
CHECK(json::from_bon8(json::to_bon8(wide_object)) == wide_object);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("regression test for https://issues.oss-fuzz.com/issues/566583014")
|
|
||||||
{
|
|
||||||
// 200000 nested one-element CBOR arrays, the innermost holding null;
|
|
||||||
// round-tripping this used to recurse once per level on the way back
|
|
||||||
// out through to_cbor(), deep enough to overflow the stack
|
|
||||||
std::vector<std::uint8_t> v(200000, 0x81);
|
|
||||||
v.push_back(0xf6);
|
|
||||||
const json j = json::from_cbor(v);
|
|
||||||
CHECK(json::to_cbor(j) == v);
|
|
||||||
|
|
||||||
// the MessagePack analogue: fixarray of 1 nesting down to nil
|
|
||||||
std::vector<std::uint8_t> v_msgpack(200000, 0x91);
|
|
||||||
v_msgpack.push_back(0xc0);
|
|
||||||
const json j_msgpack = json::from_msgpack(v_msgpack);
|
|
||||||
CHECK(json::to_msgpack(j_msgpack) == v_msgpack);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("regression test for https://issues.oss-fuzz.com/issues/572238015")
|
|
||||||
{
|
|
||||||
// the BON8 analogue: 200000 nested one-element arrays down to null
|
|
||||||
std::vector<std::uint8_t> v(200000, 0x81);
|
|
||||||
v.push_back(0xFA);
|
|
||||||
const json j = json::from_bon8(v);
|
|
||||||
CHECK(json::to_bon8(j) == v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,156 +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"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// split from unit-large_json.cpp so that both run in parallel and stay below
|
|
||||||
// the ctest timeout under Valgrind
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
// The text of a value nested depth levels deep around the number 0. Level i is
|
|
||||||
// an array if pattern[i % pattern.size()] is '[', and otherwise an object with
|
|
||||||
// the single member "a", which every object type enumerates in the same order.
|
|
||||||
std::string nested_text(std::size_t depth, const std::string& pattern)
|
|
||||||
{
|
|
||||||
std::string text;
|
|
||||||
std::string closing;
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
const bool array = pattern[i % pattern.size()] == '[';
|
|
||||||
text += array ? "[" : "{\"a\":";
|
|
||||||
closing += array ? ']' : '}';
|
|
||||||
}
|
|
||||||
text += '0';
|
|
||||||
text.append(closing.rbegin(), closing.rend());
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("issue #5650 - stack overflow converting between specializations")
|
|
||||||
{
|
|
||||||
// deep enough to exhaust the call stack, but small enough to stay cheap:
|
|
||||||
// parsing is iterative, so building the values below costs little
|
|
||||||
const std::size_t depth = 100000;
|
|
||||||
|
|
||||||
const std::vector<std::string> patterns = {"[", "{", "[{"};
|
|
||||||
|
|
||||||
SECTION("json to ordered_json")
|
|
||||||
{
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern)
|
|
||||||
const std::string text = nested_text(depth, pattern);
|
|
||||||
const json j = json::parse(text);
|
|
||||||
|
|
||||||
const nlohmann::ordered_json converted = j;
|
|
||||||
CHECK(converted.dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ordered_json to json")
|
|
||||||
{
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern)
|
|
||||||
const std::string text = nested_text(depth, pattern);
|
|
||||||
const nlohmann::ordered_json o = nlohmann::ordered_json::parse(text);
|
|
||||||
|
|
||||||
const json converted = o;
|
|
||||||
CHECK(converted.dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("get<ordered_json>()")
|
|
||||||
{
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern)
|
|
||||||
const std::string text = nested_text(depth, pattern);
|
|
||||||
const json j = json::parse(text);
|
|
||||||
|
|
||||||
CHECK(j.get<nlohmann::ordered_json>().dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("depths around the bound of the recursive descent")
|
|
||||||
{
|
|
||||||
for (std::size_t d = 1; d <= 300; ++d)
|
|
||||||
{
|
|
||||||
CAPTURE(d)
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern)
|
|
||||||
const std::string text = nested_text(d, pattern);
|
|
||||||
const json j = json::parse(text);
|
|
||||||
|
|
||||||
const nlohmann::ordered_json converted = j;
|
|
||||||
CHECK(converted.dump() == text);
|
|
||||||
const json back = converted;
|
|
||||||
CHECK(back.dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("values below the bound are converted as values above it")
|
|
||||||
{
|
|
||||||
// Bury a value below the bound, where it is converted without the
|
|
||||||
// call stack, and compare it with the same value converted on its
|
|
||||||
// own by the containers' range constructors. Its objects have
|
|
||||||
// members that the two object types enumerate in different orders.
|
|
||||||
const auto bury = [](nlohmann::ordered_json value)
|
|
||||||
{
|
|
||||||
for (std::size_t i = 0; i < 200; ++i)
|
|
||||||
{
|
|
||||||
value = nlohmann::ordered_json::array({std::move(value)});
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
const auto dig = [](const json & value)
|
|
||||||
{
|
|
||||||
const json* current = &value;
|
|
||||||
for (std::size_t i = 0; i < 200; ++i)
|
|
||||||
{
|
|
||||||
current = ¤t->at(0);
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
};
|
|
||||||
|
|
||||||
nlohmann::ordered_json value = nlohmann::ordered_json::object();
|
|
||||||
value["z"] = {1, -2, 3U, 4.5, true, nullptr, "six", nlohmann::ordered_json::binary({7, 8}, 9),
|
|
||||||
nlohmann::ordered_json::binary({10}), nlohmann::ordered_json::array(), nlohmann::ordered_json::object()
|
|
||||||
};
|
|
||||||
value["y"] = {{"x", {{"w", 1}, {"v", 2}}}, {"u", {3, {{"t", 4}, {"s", 5}}}}};
|
|
||||||
value["r"] = nlohmann::ordered_json::array({nlohmann::ordered_json(nlohmann::ordered_json::value_t::discarded)});
|
|
||||||
|
|
||||||
const json converted_above = value;
|
|
||||||
const json buried = bury(value);
|
|
||||||
const json& converted_below = *dig(buried);
|
|
||||||
|
|
||||||
CHECK(converted_below.dump() == converted_above.dump());
|
|
||||||
CHECK(converted_below.at("z").at(7).get_binary().subtype() == 9);
|
|
||||||
CHECK_FALSE(converted_below.at("z").at(8).get_binary().has_subtype());
|
|
||||||
CHECK(converted_below.at("r").at(0).is_discarded());
|
|
||||||
|
|
||||||
// a discarded value is never equal to anything, so compare the rest
|
|
||||||
value.erase("r");
|
|
||||||
const json without_discarded_above = value;
|
|
||||||
const json without_discarded_buried = bury(value);
|
|
||||||
CHECK(*dig(without_discarded_buried) == without_discarded_above);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1374,13 +1374,7 @@ void check_dump_against_reference(const std::string& s, const bool with_strict =
|
|||||||
};
|
};
|
||||||
for (const auto error_handler : error_handlers)
|
for (const auto error_handler : error_handlers)
|
||||||
{
|
{
|
||||||
#if defined(JSON_NOEXCEPTION)
|
|
||||||
// a strict dump of invalid UTF-8 calls std::abort() instead of throwing
|
|
||||||
static_cast<void>(with_strict);
|
|
||||||
if (error_handler == json::error_handler_t::strict)
|
|
||||||
#else
|
|
||||||
if (!with_strict && error_handler == json::error_handler_t::strict)
|
if (!with_strict && error_handler == json::error_handler_t::strict)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1435,7 +1429,7 @@ TEST_CASE("invalid UTF-8 at every position is handled like the reference")
|
|||||||
"\xF4\x90\x80\x80",
|
"\xF4\x90\x80\x80",
|
||||||
// sequences cut short by ASCII, a control character, a quote, a
|
// sequences cut short by ASCII, a control character, a quote, a
|
||||||
// backslash, DEL, or the lead byte of a well-formed character
|
// backslash, DEL, or the lead byte of a well-formed character
|
||||||
"\xC3" "a", "\xE2\x82" "\n", "\xE2\x82\"", "\xF0\x9F\x98\\", "\xC3\x7F", "\xC3\xC3\xA4", "\xE2\xF0\x9F\x98\x80", // NOLINT(bugprone-suspicious-missing-comma): "\xC3" "a" ends the hex escape
|
"\xC3" "a", "\xE2\x82" "\n", "\xE2\x82\"", "\xF0\x9F\x98\\", "\xC3\x7F", "\xC3\xC3\xA4", "\xE2\xF0\x9F\x98\x80",
|
||||||
// truncated sequences
|
// truncated sequences
|
||||||
"\xC3", "\xE2\x82", "\xF0\x9F\x98"
|
"\xC3", "\xE2\x82", "\xF0\x9F\x98"
|
||||||
};
|
};
|
||||||
@@ -1495,7 +1489,7 @@ TEST_CASE("invalid UTF-8 at every position is handled like the reference")
|
|||||||
{
|
{
|
||||||
for (const auto& pattern : patterns)
|
for (const auto& pattern : patterns)
|
||||||
{
|
{
|
||||||
check_dump_against_reference(make_string(filler, pos, pattern, length - pos), bytes <= (filler.size() == 1 ? 17u : 8u));
|
check_dump_against_reference(make_string(filler, pos, pattern, length - pos), bytes <= (filler.size() == 1 ? 17 : 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,8 @@ std::vector<std::string> corpus()
|
|||||||
R"("unterminated)", "\"control\x01character\"", "\"ill-formed \xc3\x28 UTF-8\"", R"("bad \x escape")",
|
R"("unterminated)", "\"control\x01character\"", "\"ill-formed \xc3\x28 UTF-8\"", R"("bad \x escape")",
|
||||||
R"("\ud800 lone surrogate")",
|
R"("\ud800 lone surrogate")",
|
||||||
// literals
|
// literals
|
||||||
"true", "false", "null", "tru", "nul", "falsey",
|
"true", "false", "null", "tru", "nul", "falsey", "[true,false,null]", "nulL", "truex", "[fals e]", "t",
|
||||||
|
"true1", "null null", "[false]",
|
||||||
// structures
|
// structures
|
||||||
"[]", "{}", "[1,2,3]", R"({"a":1,"b":[true,false,null],"c":{"d":"e"}})", "[1,]", R"({"a" 1})", "[[[[[]]]]]",
|
"[]", "{}", "[1,2,3]", R"({"a":1,"b":[true,false,null],"c":{"d":"e"}})", "[1,]", R"({"a" 1})", "[[[[[]]]]]",
|
||||||
// pretty-printed
|
// pretty-printed
|
||||||
@@ -190,7 +191,6 @@ std::vector<std::string> corpus()
|
|||||||
|
|
||||||
TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
||||||
{
|
{
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
SECTION("results do not depend on the get area")
|
SECTION("results do not depend on the get area")
|
||||||
{
|
{
|
||||||
const std::vector<read_mode> read_modes = {read_mode::extract, read_mode::parse, read_mode::parse_lenient};
|
const std::vector<read_mode> read_modes = {read_mode::extract, read_mode::parse, read_mode::parse_lenient};
|
||||||
@@ -211,8 +211,6 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(JSON_NOEXCEPTION)
|
|
||||||
|
|
||||||
SECTION("values are the same as when parsing from a string")
|
SECTION("values are the same as when parsing from a string")
|
||||||
{
|
{
|
||||||
const std::string input = R"({"name": "nlohmann::json", "numbers": [1, -2, 3.5, 4e10, 18446744073709551615],)"
|
const std::string input = R"({"name": "nlohmann::json", "numbers": [1, -2, 3.5, 4e10, 18446744073709551615],)"
|
||||||
@@ -230,7 +228,6 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
|||||||
CHECK(json::parse(ss) == expected);
|
CHECK(json::parse(ss) == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
SECTION("a token that spans several get areas is reported completely")
|
SECTION("a token that spans several get areas is reported completely")
|
||||||
{
|
{
|
||||||
// the error is found after the string has crossed many get areas; the
|
// the error is found after the string has crossed many get areas; the
|
||||||
@@ -285,8 +282,6 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(JSON_NOEXCEPTION)
|
|
||||||
|
|
||||||
SECTION("many values from one stream")
|
SECTION("many values from one stream")
|
||||||
{
|
{
|
||||||
std::string input;
|
std::string input;
|
||||||
|
|||||||
@@ -218,11 +218,7 @@ TEST_CASE("compliance tests from nativejson-benchmark")
|
|||||||
n1e308[0] = '[';
|
n1e308[0] = '[';
|
||||||
n1e308[1] = '1';
|
n1e308[1] = '1';
|
||||||
n1e308[310] = ']';
|
n1e308[310] = ']';
|
||||||
#if JSON_STRICT_NUL_HANDLING
|
|
||||||
n1e308.pop_back(); // a NUL byte after the value is a syntax error in strict mode
|
|
||||||
#else
|
|
||||||
n1e308[311] = '\0';
|
n1e308[311] = '\0';
|
||||||
#endif
|
|
||||||
TEST_DOUBLE(n1e308, 1E308);
|
TEST_DOUBLE(n1e308, 1E308);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ TEST_CASE("exception messages of out-of-line throw sites")
|
|||||||
|
|
||||||
SECTION("at")
|
SECTION("at")
|
||||||
{
|
{
|
||||||
const json& carr = arr;
|
const json carr = arr;
|
||||||
const json& cobj = obj;
|
const json cobj = obj;
|
||||||
CHECK_MSG((void)arr.at(static_cast<json::size_type>(10)), "[json.exception.out_of_range.401] array index 10 is out of range");
|
CHECK_MSG((void)arr.at(static_cast<json::size_type>(10)), "[json.exception.out_of_range.401] array index 10 is out of range");
|
||||||
CHECK_MSG((void)carr.at(static_cast<json::size_type>(3)), "[json.exception.out_of_range.401] array index 3 is out of range");
|
CHECK_MSG((void)carr.at(static_cast<json::size_type>(3)), "[json.exception.out_of_range.401] array index 3 is out of range");
|
||||||
CHECK_MSG((void)num.at(static_cast<json::size_type>(0)), "[json.exception.type_error.304] cannot use at() with number");
|
CHECK_MSG((void)num.at(static_cast<json::size_type>(0)), "[json.exception.type_error.304] cannot use at() with number");
|
||||||
@@ -65,7 +65,7 @@ TEST_CASE("exception messages of out-of-line throw sites")
|
|||||||
CHECK_MSG((void)num.at("a"), "[json.exception.type_error.304] cannot use at() with number");
|
CHECK_MSG((void)num.at("a"), "[json.exception.type_error.304] cannot use at() with number");
|
||||||
|
|
||||||
nlohmann::ordered_json oobj = {{"a", 1}};
|
nlohmann::ordered_json oobj = {{"a", 1}};
|
||||||
const nlohmann::ordered_json& coobj = oobj;
|
const nlohmann::ordered_json coobj = oobj;
|
||||||
CHECK_MSG((void)oobj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
CHECK_MSG((void)oobj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
||||||
CHECK_MSG((void)coobj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
CHECK_MSG((void)coobj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
||||||
CHECK_MSG((void)coobj.at(std::string("zz")), "[json.exception.out_of_range.403] key 'zz' not found");
|
CHECK_MSG((void)coobj.at(std::string("zz")), "[json.exception.out_of_range.403] key 'zz' not found");
|
||||||
@@ -94,10 +94,9 @@ TEST_CASE("exception messages of out-of-line throw sites")
|
|||||||
CHECK_MSG(obj.update(arr.begin(), arr2.end()), "[json.exception.invalid_iterator.210] iterators do not fit");
|
CHECK_MSG(obj.update(arr.begin(), arr2.end()), "[json.exception.invalid_iterator.210] iterators do not fit");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION) // patch_msg() catches the exceptions, which JSON_NOEXCEPTION turns into std::abort()
|
|
||||||
SECTION("patch")
|
SECTION("patch")
|
||||||
{
|
{
|
||||||
const auto patch_msg = [](const char* doc, const char* patch) -> std::string
|
const auto patch_msg = [](const char* doc, const char* patch)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -106,23 +105,16 @@ TEST_CASE("exception messages of out-of-line throw sites")
|
|||||||
}
|
}
|
||||||
catch (const json::exception& e)
|
catch (const json::exception& e)
|
||||||
{
|
{
|
||||||
// with JSON_DIAGNOSTICS, what() names the offending value like "(/0) ",
|
// with JSON_DIAGNOSTICS, what() names the offending value like "(/0) "; drop it
|
||||||
// and with JSON_DIAGNOSTIC_POSITIONS its position like "(bytes 0-2) "; drop both
|
|
||||||
std::string msg = e.what();
|
std::string msg = e.what();
|
||||||
for (const char* marker :
|
const auto pos = msg.find(" (/");
|
||||||
{
|
|
||||||
" (/", " (bytes "
|
|
||||||
})
|
|
||||||
{
|
|
||||||
const auto pos = msg.find(marker);
|
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string::npos)
|
||||||
{
|
{
|
||||||
msg.erase(pos + 1, msg.find(") ", pos) - pos + 1);
|
msg.erase(pos + 1, msg.find(") ", pos) - pos + 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
return {};
|
return std::string();
|
||||||
};
|
};
|
||||||
|
|
||||||
CHECK(patch_msg("{}", "{}") == "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects");
|
CHECK(patch_msg("{}", "{}") == "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects");
|
||||||
@@ -140,5 +132,4 @@ TEST_CASE("exception messages of out-of-line throw sites")
|
|||||||
CHECK(patch_msg("{\"a\":1}", "[{\"op\":\"test\",\"path\":\"/a\",\"value\":2}]") == "[json.exception.other_error.501] unsuccessful: {\"op\":\"test\",\"path\":\"/a\",\"value\":2}");
|
CHECK(patch_msg("{\"a\":1}", "[{\"op\":\"test\",\"path\":\"/a\",\"value\":2}]") == "[json.exception.other_error.501] unsuccessful: {\"op\":\"test\",\"path\":\"/a\",\"value\":2}");
|
||||||
CHECK(patch_msg("{\"a\":1}", "[{\"op\":\"move\",\"path\":\"/a\"}]") == "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'");
|
CHECK(patch_msg("{\"a\":1}", "[{\"op\":\"move\",\"path\":\"/a\"}]") == "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user