mirror of
https://github.com/nlohmann/json.git
synced 2026-10-09 07:57:13 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43c75bef51 | ||
|
|
39d34f30ba | ||
|
|
88ddacb84b | ||
|
|
68d61b6aef |
No files matched your search
@@ -1396,7 +1396,7 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
|||||||
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||||
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||||
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
||||||
- The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
- The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
|
|
||||||
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ type to use.
|
|||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
`NumberFloatType`
|
`NumberFloatType`
|
||||||
: the type to store floating-point numbers. Parsing and serialization are implemented in terms of
|
: the type to store floating-point numbers. The parser converts `#!cpp float`, `#!cpp double`, and a
|
||||||
`#!cpp std::strtof`/`#!cpp std::strtod`/`#!cpp std::strtold` and `#!cpp std::snprintf`, so the type must be
|
`#!cpp long double` that is IEEE 754 binary64 itself and other `#!cpp long double` formats with
|
||||||
`#!cpp float`, `#!cpp double`, or `#!cpp long double`. The
|
`#!cpp std::from_chars` or `#!cpp std::strtold`, and serialization falls back to `#!cpp std::snprintf`, so the
|
||||||
|
type must be `#!cpp float`, `#!cpp double`, or `#!cpp long double`. The
|
||||||
[binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`,
|
[binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`,
|
||||||
because they have no encoding for `#!cpp long double`. See
|
because they have no encoding for `#!cpp long double`. See
|
||||||
[Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype).
|
[Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype).
|
||||||
|
|||||||
@@ -82,12 +82,13 @@ flowchart TD
|
|||||||
|
|
||||||
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
|
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
|
||||||
- The number types can be changed, see [Template number types](#template-number-types).
|
- The number types can be changed, see [Template number types](#template-number-types).
|
||||||
- Integers are converted by the library's own digit parser. Floating-point numbers are converted with
|
- The library converts integers and floating-point numbers itself, independent of the locale. Floating-point
|
||||||
[`std::from_chars`](https://en.cppreference.com/w/cpp/utility/from_chars) if the library is compiled with C++17
|
numbers are correctly rounded (to nearest, ties to even). Only a `#!c long double` that is not IEEE 754 binary64
|
||||||
and the standard library supports it, then with an exact fast path for `#!c double` values with few significant
|
(e.g., the 80-bit x87 format) is converted with `#!cpp std::from_chars` where available, or else with
|
||||||
digits, and otherwise with the locale-aware
|
[`std::strtold`](https://en.cppreference.com/w/cpp/string/byte/strtof). For that call, the library temporarily
|
||||||
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) (`std::strtof`/`std::strtold` for the
|
replaces the `.` with the decimal point of the current locale (which may be longer than one byte, e.g., in
|
||||||
other floating-point types). Before version 3.13.0, the conversion was realized by
|
`fa_IR.UTF-8`), so the result does not depend on the locale either. Changing the locale in another thread during
|
||||||
|
parsing is undefined behavior of the C library, though. Before version 3.13.0, the conversion was realized by
|
||||||
[`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul),
|
[`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul),
|
||||||
[`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and `std::strtod`, respectively.
|
[`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and `std::strtod`, respectively.
|
||||||
|
|
||||||
@@ -100,10 +101,10 @@ flowchart TD
|
|||||||
### Number limits
|
### Number limits
|
||||||
|
|
||||||
- Any 64-bit signed or unsigned integer can be stored without loss of precision.
|
- Any 64-bit signed or unsigned integer can be stored without loss of precision.
|
||||||
- Numbers exceeding the limits of `#!c double` (i.e., numbers that after conversion via
|
- Numbers exceeding the limits of `#!c double` (i.e., numbers whose rounded value is not satisfying
|
||||||
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) are not satisfying
|
|
||||||
[`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception
|
[`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception
|
||||||
[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing.
|
[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing. Numbers too
|
||||||
|
small for `#!c double` (such as `#!c 1E-400`) become zero, with the sign of the number.
|
||||||
- Floating-point numbers are rounded to the next number representable as `double`. For instance
|
- Floating-point numbers are rounded to the next number representable as `double`. For instance
|
||||||
`#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18).
|
`#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18).
|
||||||
This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`.
|
This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`.
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ Requirements are split into two groups:
|
|||||||
diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside
|
diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside
|
||||||
the library. Four violations are not caught at compile time at all:
|
the library. Four violations are not caught at compile time at all:
|
||||||
|
|
||||||
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and can silently misparse
|
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and silently misparses numbers
|
||||||
floating-point numbers, because the lexer may hand the buffer to `#!cpp std::strtod`, which reads up to the
|
stored as a `#!cpp long double` that is not IEEE 754 binary64 (e.g., the 80-bit x87 format), because the lexer
|
||||||
terminating null character.
|
hands the buffer to `#!cpp std::strtold`.
|
||||||
- A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation,
|
- A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation,
|
||||||
and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance.
|
and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance.
|
||||||
- The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion
|
- The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion
|
||||||
@@ -541,9 +541,10 @@ therefore silently changes parse results rather than raising an error. See
|
|||||||
|
|
||||||
`NumberFloatType` must be one of `#!cpp float`, `#!cpp double`, or `#!cpp long double`:
|
`NumberFloatType` must be one of `#!cpp float`, `#!cpp double`, or `#!cpp long double`:
|
||||||
|
|
||||||
- The [parser](../parsing/index.md) converts number literals with `#!cpp std::from_chars` or, as a fallback, with
|
- The [parser](../parsing/index.md) converts number literals to `#!cpp float`, `#!cpp double`, and a
|
||||||
`#!cpp std::strtof`, `#!cpp std::strtod`, or `#!cpp std::strtold`; the library provides overloads for exactly these
|
`#!cpp long double` that is IEEE 754 binary64 itself; other `#!cpp long double` formats are converted with
|
||||||
three types.
|
`#!cpp std::from_chars` where available, or with `#!cpp std::strtold`. The library provides overloads for exactly
|
||||||
|
these three types.
|
||||||
- [`dump`](../../api/basic_json/dump.md) falls back to `#!cpp std::snprintf` with the `%g` and `%Lg` conversion
|
- [`dump`](../../api/basic_json/dump.md) falls back to `#!cpp std::snprintf` with the `%g` and `%Lg` conversion
|
||||||
specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads
|
specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads
|
||||||
(`#!cpp float` is promoted to `#!cpp double`).
|
(`#!cpp float` is promoted to `#!cpp double`).
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ The class contains a slightly modified version of the Grisu2 algorithm from Flor
|
|||||||
|
|
||||||
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||||
|
|
||||||
The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
@@ -42,6 +42,25 @@ inline int count_leading_zeros(std::uint64_t x) noexcept
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// number of trailing zero bits of x (x != 0)
|
||||||
|
inline int count_trailing_zeros(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_ctzll(x);
|
||||||
|
#else
|
||||||
|
int n = 0;
|
||||||
|
for (int shift = 32; shift != 0; shift >>= 1)
|
||||||
|
{
|
||||||
|
if ((x << (64 - shift)) == 0)
|
||||||
|
{
|
||||||
|
n += shift;
|
||||||
|
x >>= shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// the 128-bit product of two 64-bit numbers
|
/// the 128-bit product of two 64-bit numbers
|
||||||
struct uint128_parts
|
struct uint128_parts
|
||||||
{
|
{
|
||||||
@@ -77,14 +96,19 @@ inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexc
|
|||||||
|
|
||||||
/// eight bytes as a little-endian word (compilers fold this into one load on
|
/// eight bytes as a little-endian word (compilers fold this into one load on
|
||||||
/// little-endian targets)
|
/// little-endian targets)
|
||||||
inline std::uint64_t read_eight_bytes(const char* p) noexcept
|
inline std::uint64_t read_eight_bytes(const unsigned char* b) noexcept
|
||||||
{
|
{
|
||||||
const auto* b = reinterpret_cast<const unsigned char*>(p); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
||||||
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
||||||
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
||||||
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
||||||
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// eight bytes as a little-endian word
|
||||||
|
inline std::uint64_t read_eight_bytes(const char* p) noexcept
|
||||||
|
{
|
||||||
|
return read_eight_bytes(reinterpret_cast<const unsigned char*>(p)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -48,7 +48,7 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("null", j);
|
JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
n = nullptr;
|
n = nullptr;
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
|||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
throw_type_must_be("number", j);
|
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t&
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("boolean", j);
|
JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
|
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t&
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("string", j);
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ inline void from_json(const BasicJsonType& j, StringType& s)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("string", j);
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||||
@@ -183,7 +183,7 @@ inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("array", j);
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
l.clear();
|
l.clear();
|
||||||
std::transform(j.rbegin(), j.rend(),
|
std::transform(j.rbegin(), j.rend(),
|
||||||
@@ -200,7 +200,7 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("array", j);
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
l.resize(j.size());
|
l.resize(j.size());
|
||||||
std::transform(j.begin(), j.end(), std::begin(l),
|
std::transform(j.begin(), j.end(), std::begin(l),
|
||||||
@@ -305,7 +305,7 @@ void())
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("array", j);
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
from_json_array_impl(j, arr, priority_tag<3> {});
|
from_json_array_impl(j, arr, priority_tag<3> {});
|
||||||
@@ -324,7 +324,7 @@ auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("array", j);
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {});
|
return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {});
|
||||||
@@ -335,7 +335,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("binary", j);
|
JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
||||||
@@ -356,7 +356,7 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw_type_must_be("binary or array", j);
|
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +377,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("object", j);
|
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstructibleObjectType ret;
|
ConstructibleObjectType ret;
|
||||||
@@ -432,7 +432,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
|
|||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
throw_type_must_be("number", j);
|
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +504,7 @@ auto from_json(const BasicJsonType& j, TupleRelated&& t)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("array", j);
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {});
|
return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {});
|
||||||
@@ -517,14 +517,14 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("array", j);
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
m.clear();
|
m.clear();
|
||||||
for (const auto& p : j)
|
for (const auto& p : j)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("array", p);
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
||||||
}
|
}
|
||||||
m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>());
|
m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>());
|
||||||
}
|
}
|
||||||
@@ -591,7 +591,7 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("string", j);
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||||
// Checking for C++20 standard or later can be insufficient in case the
|
// Checking for C++20 standard or later can be insufficient in case the
|
||||||
|
|||||||
@@ -414,13 +414,13 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
|||||||
BasicJsonType key = p.first;
|
BasicJsonType key = p.first;
|
||||||
if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
|
if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
|
||||||
{
|
{
|
||||||
throw_type_must_be("string", key);
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
|
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
|
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(exception_id::enum_key_duplicate, concat("duplicate object key '", key_string, "'"), &key));
|
JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
external_constructor<value_t::object>::construct(j, std::move(obj));
|
external_constructor<value_t::object>::construct(j, std::move(obj));
|
||||||
|
|||||||
@@ -45,84 +45,6 @@ namespace detail
|
|||||||
// exceptions //
|
// exceptions //
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief the ids of the exceptions thrown by the library
|
|
||||||
@note The values are part of the public API: they are the `id` member of the
|
|
||||||
exceptions and appear in their `what()` messages.
|
|
||||||
@sa https://json.nlohmann.me/home/exceptions/
|
|
||||||
*/
|
|
||||||
enum class exception_id : int
|
|
||||||
{
|
|
||||||
// parse_error
|
|
||||||
syntax_error = 101, ///< unexpected token or invalid literal while parsing JSON
|
|
||||||
patch_not_an_array = 104, ///< a JSON Patch document is not an array of objects
|
|
||||||
patch_invalid_operation = 105, ///< a JSON Patch operation is malformed
|
|
||||||
pointer_index_leading_zero = 106, ///< a JSON Pointer array index has a leading zero
|
|
||||||
pointer_missing_slash = 107, ///< a JSON Pointer does not start with '/'
|
|
||||||
pointer_invalid_escape = 108, ///< a JSON Pointer contains an escape other than ~0 and ~1
|
|
||||||
pointer_index_not_a_number = 109, ///< a JSON Pointer array index is not a number
|
|
||||||
unexpected_end_of_input = 110, ///< a binary input ends early, or has bytes left at its end
|
|
||||||
unexpected_byte = 112, ///< a binary input contains an unexpected byte or invalid length
|
|
||||||
invalid_string_or_size = 113, ///< a binary input contains an invalid string or size specification
|
|
||||||
bson_unsupported_type = 114, ///< a BSON record type is not supported
|
|
||||||
invalid_high_precision_number = 115, ///< a UBJSON/BJData high-precision number cannot be parsed
|
|
||||||
// invalid_iterator
|
|
||||||
iterators_incompatible = 201, ///< the iterators of a range belong to different values
|
|
||||||
iterator_from_other_value = 202, ///< an iterator does not belong to the value it is used with
|
|
||||||
iterator_range_from_other_value = 203, ///< an iterator range does not belong to the value it is used with
|
|
||||||
iterator_range_out_of_range = 204, ///< an iterator range of a primitive value is not [begin, end)
|
|
||||||
iterator_out_of_range = 205, ///< an iterator of a primitive value is not begin()
|
|
||||||
iterator_range_of_null = 206, ///< an iterator range belongs to a null value
|
|
||||||
iterator_key_not_object = 207, ///< key() is called on an iterator of a non-object
|
|
||||||
iterator_subscript_on_object = 208, ///< operator[] is called on an iterator of an object
|
|
||||||
iterator_arithmetic_on_object = 209, ///< an offset operator is used on an iterator of an object
|
|
||||||
insert_range_incompatible = 210, ///< the iterators of an inserted range belong to different values
|
|
||||||
insert_range_into_itself = 211, ///< an inserted range belongs to the value it is inserted into
|
|
||||||
iterators_compare_different_values = 212, ///< iterators of different values are compared
|
|
||||||
iterator_order_on_object = 213, ///< iterators of an object are compared by order
|
|
||||||
iterator_value_unavailable = 214, ///< an iterator does not refer to a value
|
|
||||||
// type_error
|
|
||||||
object_from_non_pairs = 301, ///< an object is created from an initializer list that is not a list of pairs
|
|
||||||
type_mismatch = 302, ///< a value has the wrong type for a conversion
|
|
||||||
incompatible_reference_type = 303, ///< get_ref() is called with a reference type that does not match the value
|
|
||||||
at_wrong_type = 304, ///< at() is called on a value of the wrong type
|
|
||||||
subscript_wrong_type = 305, ///< operator[] is called on a value of the wrong type
|
|
||||||
value_wrong_type = 306, ///< value() is called on a value of the wrong type
|
|
||||||
erase_wrong_type = 307, ///< erase() is called on a value of the wrong type
|
|
||||||
push_back_wrong_type = 308, ///< push_back() or operator+= is called on a value of the wrong type
|
|
||||||
insert_wrong_type = 309, ///< insert() is called on a value of the wrong type
|
|
||||||
swap_wrong_type = 310, ///< swap() is called on a value of the wrong type
|
|
||||||
emplace_wrong_type = 311, ///< emplace() or emplace_back() is called on a value of the wrong type
|
|
||||||
update_wrong_type = 312, ///< update() is called on a value of the wrong type
|
|
||||||
unflatten_invalid_value = 313, ///< unflatten() finds conflicting paths
|
|
||||||
unflatten_not_object = 314, ///< unflatten() is called on a non-object
|
|
||||||
unflatten_value_not_primitive = 315, ///< unflatten() is called on an object with non-primitive values
|
|
||||||
invalid_utf8 = 316, ///< dump() finds a string that is not valid UTF-8
|
|
||||||
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
|
|
||||||
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
|
|
||||||
discarded_value_used = 321, ///< a discarded value is used
|
|
||||||
// out_of_range
|
|
||||||
array_index_out_of_range = 401, ///< an array index is out of range
|
|
||||||
pointer_past_the_end_index = 402, ///< a JSON Pointer uses the array index '-'
|
|
||||||
key_not_found = 403, ///< an object key is not found
|
|
||||||
pointer_unresolved = 404, ///< a JSON Pointer reference token cannot be resolved
|
|
||||||
patch_on_root = 405, ///< the root of a value has no parent, e.g. for a JSON Patch 'remove' or 'add' at the root
|
|
||||||
number_overflow = 406, ///< a number cannot be stored without overflowing to NaN or INF
|
|
||||||
integer_too_large = 407, ///< an integer cannot be represented in the binary format
|
|
||||||
container_too_large = 408, ///< the size of a container in a binary input exceeds the maximal capacity
|
|
||||||
bson_key_with_null = 409, ///< a BSON key contains U+0000
|
|
||||||
value_out_of_range = 410, ///< an enum value is undefined, or a JSON Pointer array index exceeds size_type
|
|
||||||
patch_add_parent_not_container = 411, ///< the parent of a JSON Patch 'add' target is not a container
|
|
||||||
length_too_large = 412, ///< a length does not fit into the length field of a binary format
|
|
||||||
patch_remove_parent_not_container = 413, ///< the parent of a JSON Patch 'remove' target is not a container
|
|
||||||
patch_move_into_child = 414, ///< a JSON Patch 'move' moves a value into one of its children
|
|
||||||
subtype_out_of_range = 415, ///< a binary subtype does not fit into one byte
|
|
||||||
// other_error
|
|
||||||
internal_error = 500, ///< unreachable code was reached
|
|
||||||
patch_test_failed = 501, ///< a JSON Patch 'test' operation failed
|
|
||||||
size_marker_required = 502 ///< UBJSON/BJData output with type markers requires size markers
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief general exception of the @ref basic_json class
|
/// @brief general exception of the @ref basic_json class
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/exception/
|
/// @sa https://json.nlohmann.me/api/basic_json/exception/
|
||||||
class exception : public std::exception
|
class exception : public std::exception
|
||||||
@@ -273,18 +195,6 @@ class parse_error : public exception
|
|||||||
return {id_, byte_, w.c_str()};
|
return {id_, byte_, w.c_str()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
||||||
static parse_error create(exception_id id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
return create(static_cast<int>(id_), pos, what_arg, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
||||||
static parse_error create(exception_id id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
return create(static_cast<int>(id_), byte_, what_arg, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief byte index of the parse error
|
@brief byte index of the parse error
|
||||||
|
|
||||||
@@ -319,12 +229,6 @@ class invalid_iterator : public exception
|
|||||||
return {id_, w.c_str()};
|
return {id_, w.c_str()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
||||||
static invalid_iterator create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
return create(static_cast<int>(id_), what_arg, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
invalid_iterator(int id_, const char* what_arg)
|
invalid_iterator(int id_, const char* what_arg)
|
||||||
@@ -343,12 +247,6 @@ class type_error : public exception
|
|||||||
return {id_, w.c_str()};
|
return {id_, w.c_str()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
||||||
static type_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
return create(static_cast<int>(id_), what_arg, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||||
@@ -366,12 +264,6 @@ class out_of_range : public exception
|
|||||||
return {id_, w.c_str()};
|
return {id_, w.c_str()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
||||||
static out_of_range create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
return create(static_cast<int>(id_), what_arg, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||||
@@ -389,12 +281,6 @@ class other_error : public exception
|
|||||||
return {id_, w.c_str()};
|
return {id_, w.c_str()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
||||||
static other_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
return create(static_cast<int>(id_), what_arg, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||||
@@ -421,36 +307,6 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
(void)exception;
|
(void)exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief throws because @a j does not have the type a conversion expects
|
|
||||||
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
|
|
||||||
@param[in] j the value with the wrong type
|
|
||||||
@throw type_error.302 always
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
|
||||||
{
|
|
||||||
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(j);
|
|
||||||
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief throws because an operation is not supported for the type of @a j
|
|
||||||
@param[in] id_ the id of the type_error exception (at_wrong_type..update_wrong_type)
|
|
||||||
@param[in] operation the operation, e.g. "erase()"
|
|
||||||
@param[in] j the value the operation was called on
|
|
||||||
@throw type_error always
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
|
||||||
{
|
|
||||||
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(operation);
|
|
||||||
static_cast<void>(j);
|
|
||||||
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <cmath> // ldexp
|
#include <cmath> // ldexp
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t, uintmax_t
|
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t, uintmax_t
|
||||||
|
#include <cstdio> // snprintf
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
#include <iterator> // back_inserter
|
#include <iterator> // back_inserter
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
@@ -195,7 +196,8 @@ class binary_reader
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))
|
if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_end_of_input, concat("expected end of input; last byte: 0x", get_token_string()), "value");
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
|
||||||
|
exception_message(concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +319,8 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document");
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document"), nullptr));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -509,7 +512,9 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(len < 1))
|
if (JSON_HEDLEY_UNLIKELY(len < 1))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat("string length must be at least 1, is ", std::to_string(len)), "string");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result)))
|
if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result)))
|
||||||
@@ -519,7 +524,10 @@ class binary_reader
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(get() != 0x00))
|
if (JSON_HEDLEY_UNLIKELY(get() != 0x00))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "BSON string is not null-terminated", "string");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message("BSON string is not null-terminated",
|
||||||
|
"string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return check_string_utf8(result, "string");
|
return check_string_utf8(result, "string");
|
||||||
@@ -539,7 +547,9 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// All BSON binary values have a subtype
|
// All BSON binary values have a subtype
|
||||||
@@ -629,9 +639,11 @@ class binary_reader
|
|||||||
|
|
||||||
default: // anything else is not supported (yet)
|
default: // anything else is not supported (yet)
|
||||||
{
|
{
|
||||||
const std::string cr_str = hex_byte(static_cast<std::uint8_t>(element_type));
|
std::array<char, 3> cr{{}};
|
||||||
|
static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
const std::string cr_str{cr.data()};
|
||||||
return sax->parse_error(element_type_parse_position, cr_str,
|
return sax->parse_error(element_type_parse_position, cr_str,
|
||||||
parse_error::create(exception_id::bson_unsupported_type, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
|
parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -955,7 +967,9 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (tag_handler == cbor_tag_handler_t::error)
|
if (tag_handler == cbor_tag_handler_t::error)
|
||||||
{
|
{
|
||||||
return invalid_byte("value");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore and store: the tag value is already in the head, so
|
// ignore and store: the tag value is already in the head, so
|
||||||
@@ -975,7 +989,9 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
case cbor_tag_handler_t::error:
|
case cbor_tag_handler_t::error:
|
||||||
{
|
{
|
||||||
return invalid_byte("value");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case cbor_tag_handler_t::ignore:
|
case cbor_tag_handler_t::ignore:
|
||||||
@@ -1049,7 +1065,9 @@ class binary_reader
|
|||||||
|
|
||||||
default: // anything else (0xFF is handled inside the other types)
|
default: // anything else (0xFF is handled inside the other types)
|
||||||
{
|
{
|
||||||
return invalid_byte("value");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1062,7 +1080,10 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool cbor_indefinite_string_error(const char* type_name, const char* context)
|
bool cbor_indefinite_string_error(const char* type_name, const char* context)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("indefinite-length ", type_name, " is not allowed inside indefinite-length ", type_name, "; last byte: 0x", get_token_string()), context);
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(concat("indefinite-length ", type_name,
|
||||||
|
" is not allowed inside indefinite-length ", type_name, "; last byte: 0x", last_token), context), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -1139,7 +1160,9 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", get_token_string()), "string");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", last_token), "string"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1269,7 +1292,9 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -1350,7 +1375,9 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", get_token_string()), "binary");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", last_token), "binary"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1496,7 +1523,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||||
exception_message(concat("excessive ", context, " size"), "size"), nullptr));
|
exception_message(concat("excessive ", context, " size"), "size"), nullptr));
|
||||||
}
|
}
|
||||||
result = conditional_static_cast<std::size_t>(len);
|
result = conditional_static_cast<std::size_t>(len);
|
||||||
@@ -1984,7 +2011,9 @@ class binary_reader
|
|||||||
|
|
||||||
default: // anything else
|
default: // anything else
|
||||||
{
|
{
|
||||||
return invalid_byte("value");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("invalid byte: 0x", last_token), "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2065,7 +2094,9 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", get_token_string()), "string");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2157,7 +2188,9 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2466,7 +2499,8 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "string length must not be negative", "string");
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message("string length must not be negative", "string"), nullptr));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2566,7 +2600,18 @@ class binary_reader
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return length_type_error("", "string");
|
auto last_token = get_token_string();
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
if (input_format != input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
|
||||||
|
}
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(message, "string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2651,11 +2696,12 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(number < 0))
|
if (JSON_HEDLEY_UNLIKELY(number < 0))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "count in an optimized container must be positive", "size");
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message("count in an optimized container must be positive", "size"), nullptr));
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||||
exception_message("integer value overflow", "size"), nullptr));
|
exception_message("integer value overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||||
@@ -2753,7 +2799,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (!value_in_range_of<std::size_t>(number))
|
if (!value_in_range_of<std::size_t>(number))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||||
exception_message("integer value overflow", "size"), nullptr));
|
exception_message("integer value overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
result = detail::conditional_static_cast<std::size_t>(number);
|
result = detail::conditional_static_cast<std::size_t>(number);
|
||||||
@@ -2768,7 +2814,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array
|
if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "ndarray dimensional vector is not allowed", "size");
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message("ndarray dimensional vector is not allowed", "size"), nullptr));
|
||||||
}
|
}
|
||||||
std::vector<size_t> dim;
|
std::vector<size_t> dim;
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
|
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
|
||||||
@@ -2804,7 +2850,9 @@ class binary_reader
|
|||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
{
|
{
|
||||||
return invalid_byte("type");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message("invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
string_t type_key = "_ArrayType_";
|
||||||
@@ -2832,7 +2880,7 @@ class binary_reader
|
|||||||
// or SIZE_MAX.
|
// or SIZE_MAX.
|
||||||
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
|
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
result *= i;
|
result *= i;
|
||||||
// the pre-check above already rules out result becoming 0
|
// the pre-check above already rules out result becoming 0
|
||||||
@@ -2841,7 +2889,7 @@ class binary_reader
|
|||||||
// unknown-size container (see get_ubjson_size_type())
|
// unknown-size container (see get_ubjson_size_type())
|
||||||
if (result == npos)
|
if (result == npos)
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i)))
|
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i)))
|
||||||
{
|
{
|
||||||
@@ -2858,7 +2906,18 @@ class binary_reader
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return length_type_error(" after '#'", "size");
|
auto last_token = get_token_string();
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
if (input_format != input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
|
||||||
|
}
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(message, "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2891,7 +2950,9 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata
|
if (input_format == input_format_t::bjdata
|
||||||
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat("marker 0x", get_token_string(), " is not a permitted optimized array type"), "type");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
||||||
@@ -2906,7 +2967,9 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat("expected '#' after type information; last byte: 0x", get_token_string()), "size");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
||||||
@@ -2925,7 +2988,8 @@ class binary_reader
|
|||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
||||||
|
exception_message("ndarray requires both type and size", "size"), nullptr));
|
||||||
}
|
}
|
||||||
return is_error;
|
return is_error;
|
||||||
}
|
}
|
||||||
@@ -3057,7 +3121,9 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(current > 127))
|
if (JSON_HEDLEY_UNLIKELY(current > 127))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", get_token_string()), "char");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
|
||||||
}
|
}
|
||||||
string_t s(1, static_cast<typename string_t::value_type>(current));
|
string_t s(1, static_cast<typename string_t::value_type>(current));
|
||||||
return sax->string(s);
|
return sax->string(s);
|
||||||
@@ -3078,7 +3144,8 @@ class binary_reader
|
|||||||
default: // anything else
|
default: // anything else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return invalid_byte("value");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message("invalid byte: 0x" + last_token, "value"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -3141,7 +3208,7 @@ class binary_reader
|
|||||||
if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F')
|
if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F')
|
||||||
&& size_and_type.first > max_valueless_container_size))
|
&& size_and_type.first > max_valueless_container_size))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||||
exception_message("excessive array size", "size"), nullptr));
|
exception_message("excessive array size", "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3177,7 +3244,9 @@ class binary_reader
|
|||||||
// do not accept ND-array size in objects in BJData
|
// do not accept ND-array size in objects in BJData
|
||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message("BJData object does not support ND-array size in optimized format", "object"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size_and_type.first != npos)
|
if (size_and_type.first != npos)
|
||||||
@@ -3214,7 +3283,7 @@ class binary_reader
|
|||||||
// the lexer would stop at a NUL and accept the digits before it
|
// the lexer would stop at a NUL and accept the digits before it
|
||||||
if (JSON_HEDLEY_UNLIKELY(current == '\0'))
|
if (JSON_HEDLEY_UNLIKELY(current == '\0'))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, "00", parse_error::create(exception_id::invalid_high_precision_number, chars_read,
|
return sax->parse_error(chars_read, "00", parse_error::create(115, chars_read,
|
||||||
exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr));
|
exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr));
|
||||||
}
|
}
|
||||||
number_vector.push_back(static_cast<char>(current));
|
number_vector.push_back(static_cast<char>(current));
|
||||||
@@ -3231,7 +3300,7 @@ class binary_reader
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
|
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, number_string, parse_error::create(exception_id::invalid_high_precision_number, chars_read,
|
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
||||||
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3249,7 +3318,7 @@ class binary_reader
|
|||||||
return sax->parse_error(
|
return sax->parse_error(
|
||||||
chars_read,
|
chars_read,
|
||||||
number_string,
|
number_string,
|
||||||
out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", number_string, '\''), nullptr));
|
out_of_range::create(406, concat("number overflow parsing '", number_string, '\''), nullptr));
|
||||||
}
|
}
|
||||||
// number_string is a std::string, while the SAX interface takes a
|
// number_string is a std::string, while the SAX interface takes a
|
||||||
// string_t; convert explicitly, as the two are only implicitly
|
// string_t; convert explicitly, as the two are only implicitly
|
||||||
@@ -3271,7 +3340,7 @@ class binary_reader
|
|||||||
case token_type::end_of_input:
|
case token_type::end_of_input:
|
||||||
case token_type::literal_or_value:
|
case token_type::literal_or_value:
|
||||||
default:
|
default:
|
||||||
return sax->parse_error(chars_read, number_string, parse_error::create(exception_id::invalid_high_precision_number, chars_read,
|
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
||||||
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
exception_message(concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3330,6 +3399,20 @@ class binary_reader
|
|||||||
return 0x80 <= c && c <= 0xBF;
|
return 0x80 <= c && c <= 0xBF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief report a parse error at the last read byte
|
||||||
|
|
||||||
|
@param[in] detail a detailed error message
|
||||||
|
@param[in] context further context information
|
||||||
|
@return false
|
||||||
|
*/
|
||||||
|
bool bon8_error(const std::string& detail, const char* context)
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(concat(detail, ": 0x", last_token), context), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a BON8 value and everything nested inside it
|
@brief read a BON8 value and everything nested inside it
|
||||||
|
|
||||||
@@ -3545,7 +3628,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0xFE: end of container where a value is expected
|
// 0xFE: end of container where a value is expected
|
||||||
return invalid_byte("value");
|
return bon8_error("invalid byte", "value");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -3672,7 +3755,7 @@ class binary_reader
|
|||||||
current = byte;
|
current = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
return unexpected_byte("expected a string; last byte", "key");
|
return bon8_error("expected a string; last byte", "key");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -3795,7 +3878,7 @@ class binary_reader
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!valid_second))
|
if (JSON_HEDLEY_UNLIKELY(!valid_second))
|
||||||
{
|
{
|
||||||
return unexpected_byte("invalid UTF-8 byte", "string");
|
return bon8_error("invalid UTF-8 byte", "string");
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push_back(static_cast<typename string_t::value_type>(byte));
|
result.push_back(static_cast<typename string_t::value_type>(byte));
|
||||||
@@ -3809,7 +3892,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_bon8_continuation(current)))
|
if (JSON_HEDLEY_UNLIKELY(!is_bon8_continuation(current)))
|
||||||
{
|
{
|
||||||
return unexpected_byte("invalid UTF-8 byte", "string");
|
return bon8_error("invalid UTF-8 byte", "string");
|
||||||
}
|
}
|
||||||
result.push_back(static_cast<typename string_t::value_type>(current));
|
result.push_back(static_cast<typename string_t::value_type>(current));
|
||||||
}
|
}
|
||||||
@@ -3854,7 +3937,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
// in case of failure, advance position by 1 to report the failing location
|
// in case of failure, advance position by 1 to report the failing location
|
||||||
++chars_read;
|
++chars_read;
|
||||||
sax->parse_error(chars_read, "<end of file>", parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr));
|
sax->parse_error(chars_read, "<end of file>", parse_error::create(110, chars_read, exception_message("unexpected end of input", context), nullptr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -4008,7 +4091,7 @@ class binary_reader
|
|||||||
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
|
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(),
|
return sax->parse_error(chars_read, get_token_string(),
|
||||||
out_of_range::create(exception_id::number_overflow, exception_message("number overflow", "value"), nullptr));
|
out_of_range::create(406, exception_message("number overflow", "value"), nullptr));
|
||||||
}
|
}
|
||||||
return sax->number_float(result, "");
|
return sax->number_float(result, "");
|
||||||
}
|
}
|
||||||
@@ -4128,7 +4211,9 @@ class binary_reader
|
|||||||
|
|
||||||
if (error_handler == error_handler_t::strict)
|
if (error_handler == error_handler_t::strict)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "invalid string: ill-formed UTF-8 byte", context);
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message("invalid string: ill-formed UTF-8 byte", context), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
result = sanitize_utf8(result, error_handler);
|
result = sanitize_utf8(result, error_handler);
|
||||||
@@ -4224,63 +4309,19 @@ class binary_reader
|
|||||||
if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))
|
if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, "<end of file>",
|
return sax->parse_error(chars_read, "<end of file>",
|
||||||
parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr));
|
parse_error::create(110, chars_read, exception_message("unexpected end of input", context), nullptr));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports a parse error at the last read byte
|
|
||||||
@param[in] id_ the id of the parse_error exception
|
|
||||||
@param[in] detail a detailed error message
|
|
||||||
@param[in] context further context information
|
|
||||||
@return the result of the SAX parser's parse_error()
|
|
||||||
*/
|
|
||||||
bool last_byte_error(const exception_id id_, const std::string& detail, const std::string& context) const
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(id_, chars_read, exception_message(detail, context), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports the last read byte as unexpected (parse_error.112)
|
|
||||||
@param[in] detail what is wrong with the byte, e.g. "invalid byte"
|
|
||||||
@param[in] context further context information
|
|
||||||
@return the result of the SAX parser's parse_error()
|
|
||||||
*/
|
|
||||||
bool unexpected_byte(const char* detail, const std::string& context) const
|
|
||||||
{
|
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat(detail, ": 0x", get_token_string()), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports the last read byte as invalid (parse_error.112)
|
|
||||||
@param[in] context further context information
|
|
||||||
@return the result of the SAX parser's parse_error()
|
|
||||||
*/
|
|
||||||
bool invalid_byte(const char* context) const
|
|
||||||
{
|
|
||||||
return unexpected_byte("invalid byte", context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports that the last read byte is not a UBJSON/BJData length type (parse_error.113)
|
|
||||||
@param[in] position where the length type was expected, e.g. " after '#'"
|
|
||||||
@param[in] context further context information
|
|
||||||
@return the result of the SAX parser's parse_error()
|
|
||||||
*/
|
|
||||||
bool length_type_error(const char* position, const char* context) const
|
|
||||||
{
|
|
||||||
const char* types = input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L";
|
|
||||||
return last_byte_error(exception_id::invalid_string_or_size,
|
|
||||||
concat("expected length type specification (", types, ")", position, "; last byte: 0x", get_token_string()), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return a string representation of the last read byte
|
@return a string representation of the last read byte
|
||||||
*/
|
*/
|
||||||
std::string get_token_string() const
|
std::string get_token_string() const
|
||||||
{
|
{
|
||||||
return hex_byte(static_cast<std::uint8_t>(current));
|
std::array<char, 3> cr{{}};
|
||||||
|
static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
return std::string{cr.data()};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ class wide_string_input_adapter
|
|||||||
template<class T>
|
template<class T>
|
||||||
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(exception_id::unexpected_byte, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -793,7 +793,7 @@ inline file_input_adapter input_adapter(std::FILE* file)
|
|||||||
{
|
{
|
||||||
if (file == nullptr)
|
if (file == nullptr)
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||||
}
|
}
|
||||||
return file_input_adapter(file);
|
return file_input_adapter(file);
|
||||||
}
|
}
|
||||||
@@ -802,7 +802,7 @@ inline input_stream_adapter input_adapter(std::istream& stream)
|
|||||||
{
|
{
|
||||||
if (stream.rdbuf() == nullptr)
|
if (stream.rdbuf() == nullptr)
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||||
}
|
}
|
||||||
return input_stream_adapter(stream);
|
return input_stream_adapter(stream);
|
||||||
}
|
}
|
||||||
@@ -827,7 +827,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b)
|
|||||||
{
|
{
|
||||||
if (b == nullptr)
|
if (b == nullptr)
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||||
}
|
}
|
||||||
auto length = std::strlen(reinterpret_cast<const char*>(b));
|
auto length = std::strlen(reinterpret_cast<const char*>(b));
|
||||||
const auto* ptr = reinterpret_cast<const char*>(b);
|
const auto* ptr = reinterpret_cast<const char*>(b);
|
||||||
|
|||||||
@@ -176,27 +176,6 @@ template<typename ArrayType>
|
|||||||
inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/)
|
inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports an object or array whose announced size exceeds max_size()
|
|
||||||
|
|
||||||
Shared by json_sax_dom_parser and json_sax_dom_callback_parser.
|
|
||||||
|
|
||||||
@param[in] sax the SAX parser to report the error to
|
|
||||||
@param[in] len the number of elements announced by the input, or unknown_size()
|
|
||||||
@param[in] kind "object" or "array"
|
|
||||||
@param[in] ref the object or array that was just created
|
|
||||||
@return whether parsing should continue (false after reporting out_of_range.408)
|
|
||||||
*/
|
|
||||||
template<typename SAX, typename BasicJsonType>
|
|
||||||
bool check_container_size(SAX& sax, std::size_t len, const char* kind, BasicJsonType* ref)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref->max_size()))
|
|
||||||
{
|
|
||||||
return sax.parse_error(0, "", out_of_range::create(exception_id::container_too_large, concat("excessive ", kind, " size: ", std::to_string(len)), ref));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
/*!
|
/*!
|
||||||
@brief set the diagnostic positions of a value the DOM SAX parsers just stored
|
@brief set the diagnostic positions of a value the DOM SAX parsers just stored
|
||||||
@@ -206,35 +185,6 @@ befriends this struct, as the position members are private.
|
|||||||
*/
|
*/
|
||||||
struct diagnostic_positions
|
struct diagnostic_positions
|
||||||
{
|
{
|
||||||
/*!
|
|
||||||
@param[in,out] v the object or array whose opening brace or bracket was just read
|
|
||||||
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType, typename LexerType>
|
|
||||||
static void set_container_start(BasicJsonType& v, LexerType* lexer)
|
|
||||||
{
|
|
||||||
if (lexer)
|
|
||||||
{
|
|
||||||
// Lexer has read the first character of the container, so
|
|
||||||
// subtract 1 from the position to get the correct start position.
|
|
||||||
v.start_position = lexer->get_position() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@param[in,out] v the object or array whose closing brace or bracket was just read
|
|
||||||
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType, typename LexerType>
|
|
||||||
static void set_container_end(BasicJsonType& v, LexerType* lexer)
|
|
||||||
{
|
|
||||||
if (lexer)
|
|
||||||
{
|
|
||||||
// Lexer's position is past the closing brace or bracket, so set that as the end position.
|
|
||||||
v.end_position = lexer->get_position();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in,out] v the value that was just parsed
|
@param[in,out] v the value that was just parsed
|
||||||
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
|
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
|
||||||
@@ -399,11 +349,22 @@ class json_sax_dom_parser
|
|||||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
|
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
// Manually set the start position of the object here.
|
||||||
// Ensure this is after the call to handle_value to ensure correct start position.
|
// Ensure this is after the call to handle_value to ensure correct start position.
|
||||||
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
// Lexer has read the first character of the object, so
|
||||||
|
// subtract 1 from the position to get the correct start position.
|
||||||
|
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return check_container_size(*this, len, "object", ref_stack.back());
|
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||||
|
{
|
||||||
|
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool key(string_t& val)
|
bool key(string_t& val)
|
||||||
@@ -422,7 +383,11 @@ class json_sax_dom_parser
|
|||||||
JSON_ASSERT(ref_stack.back()->is_object());
|
JSON_ASSERT(ref_stack.back()->is_object());
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
// Lexer's position is past the closing brace, so set that as the end position.
|
||||||
|
ref_stack.back()->end_position = m_lexer_ref->get_position();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ref_stack.back()->set_parents();
|
ref_stack.back()->set_parents();
|
||||||
@@ -435,13 +400,17 @@ class json_sax_dom_parser
|
|||||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
|
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
// Manually set the start position of the array here.
|
||||||
// Ensure this is after the call to handle_value to ensure correct start position.
|
// Ensure this is after the call to handle_value to ensure correct start position.
|
||||||
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back())))
|
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||||
{
|
{
|
||||||
return false;
|
return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len != detail::unknown_size())
|
if (len != detail::unknown_size())
|
||||||
@@ -458,7 +427,11 @@ class json_sax_dom_parser
|
|||||||
JSON_ASSERT(ref_stack.back()->is_array());
|
JSON_ASSERT(ref_stack.back()->is_array());
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
// Lexer's position is past the closing bracket, so set that as the end position.
|
||||||
|
ref_stack.back()->end_position = m_lexer_ref->get_position();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ref_stack.back()->set_parents();
|
ref_stack.back()->set_parents();
|
||||||
@@ -638,11 +611,21 @@ class json_sax_dom_callback_parser
|
|||||||
{
|
{
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
// Manually set the start position of the object here.
|
||||||
// Ensure this is after the call to handle_value to ensure correct start position.
|
// Ensure this is after the call to handle_value to ensure correct start position.
|
||||||
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
// Lexer has read the first character of the object, so
|
||||||
|
// subtract 1 from the position to get the correct start position.
|
||||||
|
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return check_container_size(*this, len, "object", ref_stack.back());
|
// check object limit
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||||
|
{
|
||||||
|
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -712,7 +695,11 @@ class json_sax_dom_callback_parser
|
|||||||
{
|
{
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
// Lexer's position is past the closing brace, so set that as the end position.
|
||||||
|
ref_stack.back()->end_position = m_lexer_ref->get_position();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ref_stack.back()->set_parents();
|
ref_stack.back()->set_parents();
|
||||||
@@ -723,7 +710,13 @@ class json_sax_dom_callback_parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const string_t object_key = pop_container();
|
JSON_ASSERT(!ref_stack.empty());
|
||||||
|
JSON_ASSERT(!keep_stack.empty());
|
||||||
|
JSON_ASSERT(!container_key_stack.empty());
|
||||||
|
ref_stack.pop_back();
|
||||||
|
keep_stack.pop_back();
|
||||||
|
const string_t object_key = std::move(container_key_stack.back());
|
||||||
|
container_key_stack.pop_back();
|
||||||
|
|
||||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
|
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
|
||||||
{
|
{
|
||||||
@@ -749,13 +742,20 @@ class json_sax_dom_callback_parser
|
|||||||
{
|
{
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
// Manually set the start position of the array here.
|
||||||
// Ensure this is after the call to handle_value to ensure correct start position.
|
// Ensure this is after the call to handle_value to ensure correct start position.
|
||||||
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
// Lexer has read the first character of the array, so
|
||||||
|
// subtract 1 from the position to get the correct start position.
|
||||||
|
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back())))
|
// check array limit
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||||
{
|
{
|
||||||
return false;
|
return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len != detail::unknown_size())
|
if (len != detail::unknown_size())
|
||||||
@@ -779,7 +779,11 @@ class json_sax_dom_callback_parser
|
|||||||
{
|
{
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref);
|
if (m_lexer_ref)
|
||||||
|
{
|
||||||
|
// Lexer's position is past the closing bracket, so set that as the end position.
|
||||||
|
ref_stack.back()->end_position = m_lexer_ref->get_position();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ref_stack.back()->set_parents();
|
ref_stack.back()->set_parents();
|
||||||
@@ -805,7 +809,13 @@ class json_sax_dom_callback_parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const string_t object_key = pop_container();
|
JSON_ASSERT(!ref_stack.empty());
|
||||||
|
JSON_ASSERT(!keep_stack.empty());
|
||||||
|
JSON_ASSERT(!container_key_stack.empty());
|
||||||
|
ref_stack.pop_back();
|
||||||
|
keep_stack.pop_back();
|
||||||
|
const string_t object_key = std::move(container_key_stack.back());
|
||||||
|
container_key_stack.pop_back();
|
||||||
|
|
||||||
// remove discarded value
|
// remove discarded value
|
||||||
if (!ref_stack.empty() && ref_stack.back())
|
if (!ref_stack.empty() && ref_stack.back())
|
||||||
@@ -892,23 +902,6 @@ class json_sax_dom_callback_parser
|
|||||||
return string_t{};
|
return string_t{};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief leave the object or array that end_object()/end_array() just closed
|
|
||||||
@return the key the container is stored under in its parent (see
|
|
||||||
container_key_stack)
|
|
||||||
*/
|
|
||||||
string_t pop_container()
|
|
||||||
{
|
|
||||||
JSON_ASSERT(!ref_stack.empty());
|
|
||||||
JSON_ASSERT(!keep_stack.empty());
|
|
||||||
JSON_ASSERT(!container_key_stack.empty());
|
|
||||||
ref_stack.pop_back();
|
|
||||||
keep_stack.pop_back();
|
|
||||||
string_t object_key = std::move(container_key_stack.back());
|
|
||||||
container_key_stack.pop_back();
|
|
||||||
return object_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief remove the discarded value the callback rejected from its parent,
|
@brief remove the discarded value the callback rejected from its parent,
|
||||||
unless it is a duplicate key's slot with a stashed previous value, in
|
unless it is a duplicate key's slot with a stashed previous value, in
|
||||||
|
|||||||
@@ -221,6 +221,44 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// scan functions
|
// scan functions
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
||||||
|
/// contiguous input: try to decode the 4 hex digits following `\\u`
|
||||||
|
/// directly from the input buffer via hex_codepoint(), instead of 4 calls
|
||||||
|
/// to get(). On success, advances the adapter and the position counters
|
||||||
|
/// exactly as those 4 get() calls would (a hex digit is never '\n', so
|
||||||
|
/// only the flat counters move) and leaves @a current holding the last of
|
||||||
|
/// the 4 digits, just as the last such get() would; the codepoint is
|
||||||
|
/// written to @a out. Makes no state change and returns false - for a
|
||||||
|
/// pending unget, fewer than 4 remaining bytes, or any of the 4 bytes not
|
||||||
|
/// being a hex digit - so the caller falls back unchanged to the
|
||||||
|
/// per-character loop, which then reports the same diagnostic (stopping
|
||||||
|
/// at the first invalid digit) as before this optimization.
|
||||||
|
bool get_codepoint_bulk(std::true_type /*bulk*/, int& out)
|
||||||
|
{
|
||||||
|
if (next_unget || ia.bulk_remaining() < 4)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char_type* const raw = ia.bulk_data();
|
||||||
|
const int codepoint = hex_codepoint(reinterpret_cast<const unsigned char*>(raw));
|
||||||
|
if (codepoint < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ia.bulk_skip(4);
|
||||||
|
// a hex digit is never a newline, so only the flat counters advance
|
||||||
|
position.chars_read_total += 4;
|
||||||
|
position.chars_read_current_line += 4;
|
||||||
|
current = char_traits<char_type>::to_int_type(raw[3]);
|
||||||
|
out = codepoint;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// streaming input: no bulk fast path
|
||||||
|
bool get_codepoint_bulk(std::false_type /*bulk*/, int& /*out*/) const noexcept
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief get codepoint from 4 hex characters following `\\u`
|
@brief get codepoint from 4 hex characters following `\\u`
|
||||||
|
|
||||||
@@ -240,6 +278,14 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
{
|
{
|
||||||
// this function only makes sense after reading `\u`
|
// this function only makes sense after reading `\u`
|
||||||
JSON_ASSERT(current == 'u');
|
JSON_ASSERT(current == 'u');
|
||||||
|
|
||||||
|
// contiguous input: decode all 4 hex digits directly from the buffer
|
||||||
|
int fast_codepoint = 0;
|
||||||
|
if (get_codepoint_bulk(std::integral_constant<bool, bulk_scan> {}, fast_codepoint))
|
||||||
|
{
|
||||||
|
return fast_codepoint;
|
||||||
|
}
|
||||||
|
|
||||||
int codepoint = 0;
|
int codepoint = 0;
|
||||||
|
|
||||||
const auto factors = { 12u, 8u, 4u, 0u };
|
const auto factors = { 12u, 8u, 4u, 0u };
|
||||||
@@ -1044,9 +1090,11 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
token_type::parse_error otherwise
|
token_type::parse_error otherwise
|
||||||
|
|
||||||
@note The scanner is independent of the current locale: token_buffer
|
@note The scanner is independent of the current locale: token_buffer
|
||||||
always holds `.`. Only the std::strtod fallback of convert_number()
|
always holds `.`. The conversion of float and double does not use
|
||||||
depends on the locale, and it looks up the decimal point right
|
the locale either. Only the std::strtold fallback of
|
||||||
before converting (see detail::convert_float_locale_aware()).
|
convert_number() for long double formats other than binary64
|
||||||
|
depends on it, and it looks up the decimal point right before
|
||||||
|
converting (see detail::convert_float_locale_aware()).
|
||||||
*/
|
*/
|
||||||
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
|
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
|
||||||
{
|
{
|
||||||
@@ -1059,7 +1107,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
|
|
||||||
// offset just past the last mantissa byte in token_buffer (i.e. the
|
// offset just past the last mantissa byte in token_buffer (i.e. the
|
||||||
// index of 'e'/'E', or the whole token when there is no exponent).
|
// index of 'e'/'E', or the whole token when there is no exponent).
|
||||||
// convert_number() uses it to count significant digits; npos means
|
// convert_number() uses it to split the token; npos means
|
||||||
// "not seen an exponent yet" and is resolved at scan_number_done
|
// "not seen an exponent yet" and is resolved at scan_number_done
|
||||||
std::size_t mantissa_end = std::string::npos;
|
std::size_t mantissa_end = std::string::npos;
|
||||||
|
|
||||||
@@ -1389,8 +1437,8 @@ scan_number_done:
|
|||||||
@param[in] mantissa_end offset just past the last mantissa byte in
|
@param[in] mantissa_end offset just past the last mantissa byte in
|
||||||
token_buffer (the index of 'e'/'E', or
|
token_buffer (the index of 'e'/'E', or
|
||||||
token_buffer.size() when there is no exponent);
|
token_buffer.size() when there is no exponent);
|
||||||
used to skip Clinger's fast path when it cannot
|
with decimal_point_position, it locates the parts
|
||||||
possibly succeed - see detail::mantissa_fits_clinger()
|
of a float token without scanning it again
|
||||||
*/
|
*/
|
||||||
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
||||||
{
|
{
|
||||||
@@ -1444,10 +1492,11 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this code is reached if we parse a floating-point number or if an
|
// this code is reached if we parse a floating-point number or if an
|
||||||
// integer conversion above overflowed. Prefer std::from_chars
|
// integer conversion above overflowed. float and double (and long
|
||||||
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
// double where it is binary64) are converted by the library itself,
|
||||||
// otherwise the exact Clinger fast path (double only); otherwise the
|
// correctly rounded and independent of the locale; other long double
|
||||||
// locale-aware strtof/strtod/strtold.
|
// formats use std::from_chars when available, otherwise the
|
||||||
|
// locale-aware strtold.
|
||||||
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
|
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
|
||||||
{
|
{
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -156,7 +156,9 @@ class parser
|
|||||||
// strict mode: next byte must be EOF
|
// strict mode: next byte must be EOF
|
||||||
if (get_token() != token_type::end_of_input)
|
if (get_token() != token_type::end_of_input)
|
||||||
{
|
{
|
||||||
return syntax_error(*sax, exception_message(token_type::end_of_input, "value"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -195,7 +197,10 @@ class parser
|
|||||||
// in strict mode, input must be completely read
|
// in strict mode, input must be completely read
|
||||||
if (get_token() != token_type::end_of_input)
|
if (get_token() != token_type::end_of_input)
|
||||||
{
|
{
|
||||||
syntax_error(sdp, exception_message(token_type::end_of_input, "value"));
|
sdp.parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(),
|
||||||
|
exception_message(token_type::end_of_input, "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -245,7 +250,9 @@ class parser
|
|||||||
// parse key
|
// parse key
|
||||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||||
{
|
{
|
||||||
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||||
{
|
{
|
||||||
@@ -255,7 +262,9 @@ class parser
|
|||||||
// parse separator (:)
|
// parse separator (:)
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
|
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
|
||||||
{
|
{
|
||||||
return syntax_error(*sax, exception_message(token_type::name_separator, "object separator"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember we are now inside an object
|
// remember we are now inside an object
|
||||||
@@ -298,7 +307,7 @@ class parser
|
|||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
m_lexer.get_token_string(),
|
m_lexer.get_token_string(),
|
||||||
out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
||||||
@@ -366,16 +375,23 @@ class parser
|
|||||||
case token_type::parse_error:
|
case token_type::parse_error:
|
||||||
{
|
{
|
||||||
// using "uninitialized" to avoid an "expected" message
|
// using "uninitialized" to avoid an "expected" message
|
||||||
return syntax_error(*sax, exception_message(token_type::uninitialized, "value"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
|
||||||
}
|
}
|
||||||
case token_type::end_of_input:
|
case token_type::end_of_input:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
|
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
|
||||||
{
|
{
|
||||||
return syntax_error(*sax, "attempting to parse an empty input; check that your input string or stream contains the expected JSON");
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(),
|
||||||
|
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return syntax_error(*sax, exception_message(token_type::literal_or_value, "value"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
||||||
}
|
}
|
||||||
case token_type::uninitialized:
|
case token_type::uninitialized:
|
||||||
case token_type::end_array:
|
case token_type::end_array:
|
||||||
@@ -385,7 +401,9 @@ class parser
|
|||||||
case token_type::literal_or_value:
|
case token_type::literal_or_value:
|
||||||
default: // the last token was unexpected
|
default: // the last token was unexpected
|
||||||
{
|
{
|
||||||
return syntax_error(*sax, exception_message(token_type::literal_or_value, "value"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,7 +454,9 @@ class parser
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return syntax_error(*sax, exception_message(token_type::end_array, "array"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// states.back() is false -> object
|
// states.back() is false -> object
|
||||||
@@ -453,7 +473,9 @@ class parser
|
|||||||
// parse key
|
// parse key
|
||||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||||
{
|
{
|
||||||
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||||
@@ -464,7 +486,9 @@ class parser
|
|||||||
// parse separator (:)
|
// parse separator (:)
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
|
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
|
||||||
{
|
{
|
||||||
return syntax_error(*sax, exception_message(token_type::name_separator, "object separator"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse values
|
// parse values
|
||||||
@@ -492,7 +516,9 @@ class parser
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return syntax_error(*sax, exception_message(token_type::end_object, "object"));
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,21 +535,6 @@ class parser
|
|||||||
return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type;
|
return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports a syntax error at the current token (parse_error.101)
|
|
||||||
@param[in] sax the SAX parser to report the error to
|
|
||||||
@param[in] message the error message
|
|
||||||
@return the result of the SAX parser's parse_error()
|
|
||||||
*/
|
|
||||||
template<typename SAX>
|
|
||||||
bool syntax_error(SAX& sax, const std::string& message)
|
|
||||||
{
|
|
||||||
// MSVC 2015 reports C4100 (unreferenced parameter) when SAX::parse_error is static
|
|
||||||
static_cast<void>(sax);
|
|
||||||
return sax.parse_error(m_lexer.get_position(), m_lexer.get_token_string(),
|
|
||||||
parse_error::create(exception_id::syntax_error, m_lexer.get_position(), message, nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string exception_message(const token_type expected, const std::string& context)
|
std::string exception_message(const token_type expected, const std::string& context)
|
||||||
{
|
{
|
||||||
std::string error_msg = "syntax error ";
|
std::string error_msg = "syntax error ";
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ namespace detail
|
|||||||
constexpr std::int64_t pow5_128_smallest_power = -342;
|
constexpr std::int64_t pow5_128_smallest_power = -342;
|
||||||
constexpr std::int64_t pow5_128_largest_power = 308;
|
constexpr std::int64_t pow5_128_largest_power = 308;
|
||||||
|
|
||||||
|
// every entry of pow5_128() holds two 64-bit halves of 5^q, one per covered power of 5
|
||||||
|
static_assert((pow5_128_largest_power - pow5_128_smallest_power + 1) * 2 == 1302,
|
||||||
|
"pow5_128_smallest_power/pow5_128_largest_power must match the size of the pow5_128() table");
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief 128-bit approximations of 5^q for q in [-342, 308]
|
@brief 128-bit approximations of 5^q for q in [-342, 308]
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint64_t
|
#include <cstdint> // uint64_t, uint8_t
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
|
|
||||||
|
#include <nlohmann/detail/bit_ops.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
|
|
||||||
// Optional SIMD backend for bulk UTF-8 validation. This is an opt-in external
|
// Optional SIMD backend for bulk UTF-8 validation. This is an opt-in external
|
||||||
@@ -69,18 +71,12 @@ inline std::size_t find_string_special(const unsigned char* data, std::size_t n)
|
|||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
for (; i + 8 <= n; i += 8)
|
for (; i + 8 <= n; i += 8)
|
||||||
{
|
{
|
||||||
std::uint64_t word = 0;
|
const std::uint64_t special = swar_string_special(read_eight_bytes(data + i));
|
||||||
std::memcpy(&word, data + i, sizeof(word));
|
if (special != 0)
|
||||||
if (swar_string_special(word) != 0)
|
|
||||||
{
|
{
|
||||||
// a special byte is in this word; locate it (endian-agnostic)
|
// the lowest flagged byte is the first special one: the borrows of
|
||||||
for (std::size_t j = 0; j < 8; ++j)
|
// the subtractions can only flag bytes above a true hit
|
||||||
{
|
return i + (static_cast<std::size_t>(count_trailing_zeros(special)) / 8);
|
||||||
if (is_string_special(data[i + j]))
|
|
||||||
{
|
|
||||||
return i + j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; i < n; ++i)
|
for (; i < n; ++i)
|
||||||
@@ -114,8 +110,7 @@ inline std::size_t find_ascii_copyable_run(const unsigned char* data, std::size_
|
|||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
for (; i + 8 <= n; i += 8)
|
for (; i + 8 <= n; i += 8)
|
||||||
{
|
{
|
||||||
std::uint64_t v = 0;
|
const std::uint64_t v = read_eight_bytes(data + i);
|
||||||
std::memcpy(&v, data + i, sizeof(v));
|
|
||||||
const std::uint64_t q = v ^ 0x2222222222222222ull; // '"' (0x22)
|
const std::uint64_t q = v ^ 0x2222222222222222ull; // '"' (0x22)
|
||||||
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull; // '\\' (0x5C)
|
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull; // '\\' (0x5C)
|
||||||
const std::uint64_t d = v ^ 0x7F7F7F7F7F7F7F7Full; // DEL (0x7F)
|
const std::uint64_t d = v ^ 0x7F7F7F7F7F7F7F7Full; // DEL (0x7F)
|
||||||
@@ -126,7 +121,9 @@ inline std::size_t find_ascii_copyable_run(const unsigned char* data, std::size_
|
|||||||
| (v & high); // >= 0x80
|
| (v & high); // >= 0x80
|
||||||
if (stop != 0)
|
if (stop != 0)
|
||||||
{
|
{
|
||||||
break;
|
// the lowest flagged byte is the first one to stop at (see
|
||||||
|
// find_string_special())
|
||||||
|
return i + (static_cast<std::size_t>(count_trailing_zeros(stop)) / 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; i < n; ++i)
|
for (; i < n; ++i)
|
||||||
@@ -253,13 +250,19 @@ inline std::size_t scalar_string_bulk_run(const unsigned char* data, std::size_t
|
|||||||
{
|
{
|
||||||
break; // end of buffer, or a quote/escape/control byte
|
break; // end of buffer, or a quote/escape/control byte
|
||||||
}
|
}
|
||||||
|
// a run of multi-byte sequences (e.g. CJK text) is validated sequence
|
||||||
|
// by sequence without searching for the next special byte in between
|
||||||
|
do
|
||||||
|
{
|
||||||
const std::size_t seq = validate_one_utf8(data + pos, n - pos);
|
const std::size_t seq = validate_one_utf8(data + pos, n - pos);
|
||||||
if (seq == 0)
|
if (seq == 0)
|
||||||
{
|
{
|
||||||
break; // ill-formed or truncated: let the byte path diagnose it
|
return pos; // ill-formed or truncated: let the byte path diagnose it
|
||||||
}
|
}
|
||||||
pos += seq;
|
pos += seq;
|
||||||
}
|
}
|
||||||
|
while (pos < n && data[pos] >= 0x80u);
|
||||||
|
}
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,8 +276,7 @@ inline std::size_t find_string_delimiter(const unsigned char* data, std::size_t
|
|||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
for (; i + 8 <= n; i += 8)
|
for (; i + 8 <= n; i += 8)
|
||||||
{
|
{
|
||||||
std::uint64_t v = 0;
|
const std::uint64_t v = read_eight_bytes(data + i);
|
||||||
std::memcpy(&v, data + i, sizeof(v));
|
|
||||||
const std::uint64_t q = v ^ 0x2222222222222222ull;
|
const std::uint64_t q = v ^ 0x2222222222222222ull;
|
||||||
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull;
|
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull;
|
||||||
const std::uint64_t hit = ((q - ones) & ~q & high)
|
const std::uint64_t hit = ((q - ones) & ~q & high)
|
||||||
@@ -282,14 +284,8 @@ inline std::size_t find_string_delimiter(const unsigned char* data, std::size_t
|
|||||||
| ((v - 0x2020202020202020ull) & ~v & high);
|
| ((v - 0x2020202020202020ull) & ~v & high);
|
||||||
if (hit != 0)
|
if (hit != 0)
|
||||||
{
|
{
|
||||||
for (std::size_t j = 0; j < 8; ++j)
|
// the lowest flagged byte is the first delimiter (see find_string_special())
|
||||||
{
|
return i + (static_cast<std::size_t>(count_trailing_zeros(hit)) / 8);
|
||||||
const unsigned char c = data[i + j];
|
|
||||||
if (c == '\"' || c == '\\' || c < 0x20u)
|
|
||||||
{
|
|
||||||
return i + j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; i < n; ++i)
|
for (; i < n; ++i)
|
||||||
@@ -320,5 +316,50 @@ inline std::size_t string_bulk_run(const unsigned char* data, std::size_t n) noe
|
|||||||
return scalar_string_bulk_run(data, n);
|
return scalar_string_bulk_run(data, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode the 4 hex digits at [data, data+4) - the digits following a `\u`
|
||||||
|
// escape - into a codepoint 0x0000..0xFFFF via one table lookup per byte
|
||||||
|
// (after yyjson's read_hex_u16), or return -1 if any of the 4 bytes is not a
|
||||||
|
// hex digit ('0'..'9', 'A'..'F', 'a'..'f'). The caller must already have
|
||||||
|
// checked that 4 bytes are available; used by lexer::get_codepoint()'s
|
||||||
|
// contiguous fast path. On -1 it falls back to the byte-at-a-time loop, which
|
||||||
|
// stops at the first invalid digit, so the reported error and position are
|
||||||
|
// unaffected by this fast path.
|
||||||
|
inline int hex_codepoint(const unsigned char* data) noexcept
|
||||||
|
{
|
||||||
|
static const std::array<std::uint8_t, 256> hex_digit_table = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 00..0F
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 10..1F
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 20..2F
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 30..3F ('0'..'9')
|
||||||
|
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 40..4F ('A'..'F')
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 50..5F
|
||||||
|
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 60..6F ('a'..'f')
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 70..7F
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 80..8F
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 90..9F
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0..AF
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // B0..BF
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // C0..CF
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // D0..DF
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E0..EF
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // F0..FF
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::uint8_t d0 = hex_digit_table[data[0]];
|
||||||
|
const std::uint8_t d1 = hex_digit_table[data[1]];
|
||||||
|
const std::uint8_t d2 = hex_digit_table[data[2]];
|
||||||
|
const std::uint8_t d3 = hex_digit_table[data[3]];
|
||||||
|
// every valid digit is <= 0xF; the combined OR only exceeds it if at
|
||||||
|
// least one of the four bytes was not a hex digit (looked up as 0xFF)
|
||||||
|
if ((d0 | d1 | d2 | d3) > 0x0F)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (d0 << 12) | (d1 << 8) | (d2 << 4) | d3;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -279,12 +279,6 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @throw invalid_iterator.214 always
|
|
||||||
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
|
|
||||||
{
|
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
@brief return a reference to the value pointed to by the iterator
|
@brief return a reference to the value pointed to by the iterator
|
||||||
@@ -309,7 +303,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
}
|
}
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
throw_cannot_get_value();
|
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
@@ -325,7 +319,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
return *m_object;
|
return *m_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_cannot_get_value();
|
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,7 +361,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
return m_object;
|
return m_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_cannot_get_value();
|
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,7 +478,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
// if objects are not the same, the comparison is undefined
|
// if objects are not the same, the comparison is undefined
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||||
@@ -533,7 +527,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
// if objects are not the same, the comparison is undefined
|
// if objects are not the same, the comparison is undefined
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||||
@@ -546,7 +540,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object));
|
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||||
@@ -602,7 +596,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
{
|
{
|
||||||
@@ -681,7 +675,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
return m_it.array_iterator - other.m_it.array_iterator;
|
return m_it.array_iterator - other.m_it.array_iterator;
|
||||||
@@ -710,13 +704,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object));
|
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
return *std::next(m_it.array_iterator, n);
|
return *std::next(m_it.array_iterator, n);
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
throw_cannot_get_value();
|
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
@@ -732,7 +726,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
return *m_object;
|
return *m_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_cannot_get_value();
|
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -750,7 +744,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
return m_it.object_iterator->first;
|
return m_it.object_iterator->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object));
|
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
throw_no_parent();
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
reference_tokens.erase(reference_tokens.begin());
|
reference_tokens.erase(reference_tokens.begin());
|
||||||
@@ -178,7 +178,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
throw_no_parent();
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return reference_tokens.front();
|
return reference_tokens.front();
|
||||||
@@ -204,7 +204,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
throw_no_parent();
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
reference_tokens.pop_back();
|
reference_tokens.pop_back();
|
||||||
@@ -216,7 +216,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
throw_no_parent();
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return reference_tokens.back();
|
return reference_tokens.back();
|
||||||
@@ -244,21 +244,6 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @throw out_of_range.405 always
|
|
||||||
JSON_HEDLEY_NO_RETURN static void throw_no_parent()
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @throw out_of_range.404 always
|
|
||||||
template<typename BasicJsonContext>
|
|
||||||
JSON_HEDLEY_NO_RETURN static void throw_unresolved(const string_t& reference_token, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
static_cast<void>(reference_token); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(context);
|
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_unresolved, detail::concat("unresolved reference token '", reference_token, "'"), context));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief result of @ref parse_array_index
|
@brief result of @ref parse_array_index
|
||||||
|
|
||||||
@@ -344,13 +329,13 @@ class json_pointer
|
|||||||
// the branches differ in their messages, not after JSON_THROW's expansion
|
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||||
// NOLINTNEXTLINE(bugprone-branch-clone)
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||||
case array_index_status::leading_zero:
|
case array_index_status::leading_zero:
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
||||||
case array_index_status::not_a_number:
|
case array_index_status::not_a_number:
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
|
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
|
||||||
case array_index_status::unresolved:
|
case array_index_status::unresolved:
|
||||||
throw_unresolved(s, nullptr);
|
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
|
||||||
case array_index_status::exceeds_size_type:
|
case array_index_status::exceeds_size_type:
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::value_out_of_range, detail::concat("array index ", s, " exceeds size_type"), nullptr));
|
JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));
|
||||||
case array_index_status::ok:
|
case array_index_status::ok:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -364,7 +349,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
throw_no_parent();
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
json_pointer result = *this;
|
json_pointer result = *this;
|
||||||
@@ -452,7 +437,7 @@ class json_pointer
|
|||||||
case detail::value_t::binary:
|
case detail::value_t::binary:
|
||||||
case detail::value_t::discarded:
|
case detail::value_t::discarded:
|
||||||
default:
|
default:
|
||||||
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_invalid_value, "invalid value to unflatten", &j));
|
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix.push_back(reference_token);
|
prefix.push_back(reference_token);
|
||||||
@@ -535,7 +520,7 @@ class json_pointer
|
|||||||
case detail::value_t::binary:
|
case detail::value_t::binary:
|
||||||
case detail::value_t::discarded:
|
case detail::value_t::discarded:
|
||||||
default:
|
default:
|
||||||
throw_unresolved(reference_token, ptr);
|
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +552,7 @@ class json_pointer
|
|||||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
|
JSON_THROW(detail::out_of_range::create(402, detail::concat(
|
||||||
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
|
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
|
||||||
") is out of range"), ptr));
|
") is out of range"), ptr));
|
||||||
}
|
}
|
||||||
@@ -576,7 +561,7 @@ class json_pointer
|
|||||||
// Bounds check before access to avoid exception with JSON_NOEXCEPTION
|
// Bounds check before access to avoid exception with JSON_NOEXCEPTION
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat(
|
JSON_THROW(detail::out_of_range::create(401, detail::concat(
|
||||||
"array index ", std::to_string(idx), " is out of range"), ptr));
|
"array index ", std::to_string(idx), " is out of range"), ptr));
|
||||||
}
|
}
|
||||||
ptr = &ptr->operator[](idx);
|
ptr = &ptr->operator[](idx);
|
||||||
@@ -592,7 +577,7 @@ class json_pointer
|
|||||||
case detail::value_t::binary:
|
case detail::value_t::binary:
|
||||||
case detail::value_t::discarded:
|
case detail::value_t::discarded:
|
||||||
default:
|
default:
|
||||||
throw_unresolved(reference_token, ptr);
|
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,7 +621,7 @@ class json_pointer
|
|||||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" cannot be used for const access
|
// "-" cannot be used for const access
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// use unchecked array access; the const operator[]
|
// use unchecked array access; the const operator[]
|
||||||
@@ -654,7 +639,7 @@ class json_pointer
|
|||||||
case detail::value_t::binary:
|
case detail::value_t::binary:
|
||||||
case detail::value_t::discarded:
|
case detail::value_t::discarded:
|
||||||
default:
|
default:
|
||||||
throw_unresolved(reference_token, ptr);
|
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,9 +694,9 @@ class json_pointer
|
|||||||
// the branches differ in their messages, not after JSON_THROW's expansion
|
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||||
// NOLINTNEXTLINE(bugprone-branch-clone)
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||||
case array_index_status::leading_zero:
|
case array_index_status::leading_zero:
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
||||||
case array_index_status::not_a_number:
|
case array_index_status::not_a_number:
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
|
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
|
||||||
case array_index_status::unresolved:
|
case array_index_status::unresolved:
|
||||||
case array_index_status::exceeds_size_type:
|
case array_index_status::exceeds_size_type:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -838,7 +823,7 @@ class json_pointer
|
|||||||
// check if a nonempty reference string begins with slash
|
// check if a nonempty reference string begins with slash
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
|
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract the reference tokens:
|
// extract the reference tokens:
|
||||||
@@ -874,7 +859,7 @@ class json_pointer
|
|||||||
(reference_token[pos + 1] != '0' &&
|
(reference_token[pos + 1] != '0' &&
|
||||||
reference_token[pos + 1] != '1')))
|
reference_token[pos + 1] != '1')))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -971,7 +956,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_not_object, "only objects can be unflattened", &value));
|
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicJsonType result;
|
BasicJsonType result;
|
||||||
@@ -999,7 +984,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
|
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_value_not_primitive, "values in object must be primitive", &element.second));
|
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the value to the reference pointed to by JSON pointer. Note
|
// Assign the value to the reference pointed to by JSON pointer. Note
|
||||||
|
|||||||
@@ -312,7 +312,7 @@
|
|||||||
return ej_pair.first == e; \
|
return ej_pair.first == e; \
|
||||||
}); \
|
}); \
|
||||||
if (it != std::end(m)) j = it->second; \
|
if (it != std::end(m)) j = it->second; \
|
||||||
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range,"enum value out of range for " #ENUM_TYPE, nullptr)); \
|
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \
|
||||||
} \
|
} \
|
||||||
template<typename BasicJsonType> \
|
template<typename BasicJsonType> \
|
||||||
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
|
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
|
||||||
@@ -327,7 +327,7 @@
|
|||||||
return ej_pair.second == j; \
|
return ej_pair.second == j; \
|
||||||
}); \
|
}); \
|
||||||
if (it != std::end(m)) e = it->first; \
|
if (it != std::end(m)) e = it->first; \
|
||||||
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
|
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ class binary_writer
|
|||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(exception_id::type_not_serializable, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
|
JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,7 +354,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length)))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length)))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(exception_id::length_too_large, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j));
|
JSON_THROW(out_of_range::create(412, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
static_cast<void>(j);
|
static_cast<void>(j);
|
||||||
@@ -613,7 +613,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(j.m_data.m_value.binary->subtype() > (std::numeric_limits<std::uint8_t>::max)()))
|
if (JSON_HEDLEY_UNLIKELY(j.m_data.m_value.binary->subtype() > (std::numeric_limits<std::uint8_t>::max)()))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(exception_id::subtype_out_of_range, concat("subtype ", std::to_string(j.m_data.m_value.binary->subtype()), " is too large for the MessagePack ext type (max 255)"), &j));
|
JSON_THROW(out_of_range::create(415, concat("subtype ", std::to_string(j.m_data.m_value.binary->subtype()), " is too large for the MessagePack ext type (max 255)"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype()));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype()));
|
||||||
@@ -767,7 +767,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
if (!use_count)
|
if (!use_count)
|
||||||
{
|
{
|
||||||
JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j));
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
}
|
}
|
||||||
oa.write_character(to_char_type('$'));
|
oa.write_character(to_char_type('$'));
|
||||||
oa.write_character(bjdata_draft3 ? 'B' : 'U');
|
oa.write_character(bjdata_draft3 ? 'B' : 'U');
|
||||||
@@ -866,7 +866,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
static_cast<void>(j); // unused when JSON_NOEXCEPTION is defined
|
static_cast<void>(j); // unused when JSON_NOEXCEPTION is defined
|
||||||
static_cast<void>(format_name);
|
static_cast<void>(format_name);
|
||||||
JSON_THROW(type_error::create(exception_id::discarded_value_used, concat("cannot serialize discarded value to ", format_name), &j));
|
JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
|
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
|
||||||
@@ -1132,7 +1132,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
if (!use_count)
|
if (!use_count)
|
||||||
{
|
{
|
||||||
JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j));
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
}
|
}
|
||||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||||
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
|
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
|
||||||
@@ -1182,7 +1182,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
if (!use_count)
|
if (!use_count)
|
||||||
{
|
{
|
||||||
JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j));
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
}
|
}
|
||||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||||
const bool same_prefix = std::all_of(j.begin(), j.end(),
|
const bool same_prefix = std::all_of(j.begin(), j.end(),
|
||||||
@@ -1397,7 +1397,7 @@ class binary_writer
|
|||||||
const auto it = name.find(static_cast<typename string_t::value_type>(0));
|
const auto it = name.find(static_cast<typename string_t::value_type>(0));
|
||||||
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
|
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(exception_id::bson_key_with_null, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t storage;
|
string_t storage;
|
||||||
@@ -1415,7 +1415,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(exception_id::length_too_large, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr));
|
JSON_THROW(out_of_range::create(412, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<std::int32_t>(size);
|
return static_cast<std::int32_t>(size);
|
||||||
@@ -1601,7 +1601,7 @@ class binary_writer
|
|||||||
|
|
||||||
if (value.has_subtype() && JSON_HEDLEY_UNLIKELY(value.subtype() > (std::numeric_limits<std::uint8_t>::max)()))
|
if (value.has_subtype() && JSON_HEDLEY_UNLIKELY(value.subtype() > (std::numeric_limits<std::uint8_t>::max)()))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(exception_id::subtype_out_of_range, concat("subtype ", std::to_string(value.subtype()), " is too large for the BSON binary subtype (max 255)"), &j));
|
JSON_THROW(out_of_range::create(415, concat("subtype ", std::to_string(value.subtype()), " is too large for the BSON binary subtype (max 255)"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sizeof(std::int32_t) + value.size() + 1ul;
|
return sizeof(std::int32_t) + value.size() + 1ul;
|
||||||
@@ -2551,7 +2551,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)()))
|
if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(exception_id::integer_too_large, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j));
|
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j));
|
||||||
}
|
}
|
||||||
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
|
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
|
||||||
string_open = false;
|
string_open = false;
|
||||||
@@ -2704,7 +2704,7 @@ class binary_writer
|
|||||||
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
||||||
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context));
|
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -888,7 +888,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
|
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
@@ -1021,7 +1021,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(exception_id::invalid_utf8, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr));
|
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
|
|||||||
+71
-80
@@ -639,7 +639,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
object = nullptr; // silence warning, see #821
|
object = nullptr; // silence warning, see #821
|
||||||
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
|
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
|
||||||
{
|
{
|
||||||
JSON_THROW(other_error::create(detail::exception_id::internal_error, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
|
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2287,7 +2287,7 @@ public:
|
|||||||
// if an object is wanted but impossible, throw an exception
|
// if an object is wanted but impossible, throw an exception
|
||||||
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
|
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(detail::exception_id::object_from_non_pairs, "cannot create object from initializer list", nullptr));
|
JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2407,7 +2407,7 @@ public:
|
|||||||
// make sure the iterator fits the current value
|
// make sure the iterator fits the current value
|
||||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterators_incompatible, "iterators are not compatible", nullptr));
|
JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy type from the first iterator
|
// copy type from the first iterator
|
||||||
@@ -2426,7 +2426,7 @@ public:
|
|||||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||||
|| !last.m_it.primitive_iterator.is_end()))
|
|| !last.m_it.primitive_iterator.is_end()))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object));
|
JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2494,7 +2494,7 @@ public:
|
|||||||
case value_t::null:
|
case value_t::null:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
|
JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -2882,7 +2882,7 @@ public:
|
|||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -3266,7 +3266,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (!is_binary())
|
if (!is_binary())
|
||||||
{
|
{
|
||||||
detail::throw_type_must_be("binary", *this);
|
JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
return *get_ptr<binary_t*>();
|
return *get_ptr<binary_t*>();
|
||||||
@@ -3278,7 +3278,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (!is_binary())
|
if (!is_binary())
|
||||||
{
|
{
|
||||||
detail::throw_type_must_be("binary", *this);
|
JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
return *get_ptr<const binary_t*>();
|
return *get_ptr<const binary_t*>();
|
||||||
@@ -3320,7 +3320,7 @@ public:
|
|||||||
// at only works for objects
|
// at only works for objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::at_wrong_type, "at()", j);
|
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = object_lookup(j, std::forward<KeyType>(key));
|
auto it = object_lookup(j, std::forward<KeyType>(key));
|
||||||
@@ -3330,7 +3330,7 @@ public:
|
|||||||
// std::map or ordered_map) never moves from its argument, so key is still
|
// std::map or ordered_map) never moves from its argument, so key is still
|
||||||
// valid here regardless of whether KeyType was deduced as an rvalue reference
|
// valid here regardless of whether KeyType was deduced as an rvalue reference
|
||||||
// NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved)
|
// NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved)
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", string_t(key), "' not found"), &j));
|
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), &j));
|
||||||
}
|
}
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
@@ -3345,12 +3345,12 @@ public:
|
|||||||
// at only works for arrays
|
// at only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::at_wrong_type, "at()", j);
|
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size()))
|
if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size()))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*j.m_data.m_value.array)[idx];
|
return (*j.m_data.m_value.array)[idx];
|
||||||
@@ -3383,15 +3383,6 @@ public:
|
|||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief throws because operator[] is not supported for the type of this value
|
|
||||||
/// @param[in] argument the kind of the operator's argument, "numeric" or "string"
|
|
||||||
/// @throw type_error.305 always
|
|
||||||
JSON_HEDLEY_NO_RETURN void throw_subscript_wrong_type(const char* argument) const
|
|
||||||
{
|
|
||||||
detail::throw_cannot_use_with(detail::exception_id::subscript_wrong_type,
|
|
||||||
detail::concat("operator[] with a ", argument, " argument").c_str(), *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
////////////////////
|
////////////////////
|
||||||
// element access //
|
// element access //
|
||||||
@@ -3496,7 +3487,7 @@ public:
|
|||||||
return m_data.m_value.array->operator[](idx);
|
return m_data.m_value.array->operator[](idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_subscript_wrong_type("numeric");
|
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief access specified array element
|
/// @brief access specified array element
|
||||||
@@ -3510,7 +3501,7 @@ public:
|
|||||||
return m_data.m_value.array->operator[](idx);
|
return m_data.m_value.array->operator[](idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_subscript_wrong_type("numeric");
|
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief access specified object element
|
/// @brief access specified object element
|
||||||
@@ -3530,7 +3521,7 @@ public:
|
|||||||
return set_parent(result.first->second);
|
return set_parent(result.first->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_subscript_wrong_type("string");
|
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief access specified object element
|
/// @brief access specified object element
|
||||||
@@ -3545,7 +3536,7 @@ public:
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_subscript_wrong_type("string");
|
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
|
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
|
||||||
@@ -3581,7 +3572,7 @@ public:
|
|||||||
return set_parent(result.first->second);
|
return set_parent(result.first->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_subscript_wrong_type("string");
|
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief access specified object element
|
/// @brief access specified object element
|
||||||
@@ -3598,7 +3589,7 @@ public:
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_subscript_wrong_type("string");
|
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -3622,7 +3613,7 @@ public:
|
|||||||
// value only works for objects
|
// value only works for objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::value_wrong_type, "value()", *this);
|
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto it = find(std::forward<KeyType>(key));
|
const auto it = find(std::forward<KeyType>(key));
|
||||||
@@ -3637,7 +3628,7 @@ public:
|
|||||||
// value only works for arrays and objects
|
// value only works for arrays and objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_structured()))
|
if (JSON_HEDLEY_UNLIKELY(!is_structured()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::value_wrong_type, "value()", *this);
|
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr.get_checked_or_null(this);
|
return ptr.get_checked_or_null(this);
|
||||||
@@ -3804,7 +3795,7 @@ public:
|
|||||||
// make sure the iterator fits the current value
|
// make sure the iterator fits the current value
|
||||||
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
|
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
IteratorType result = end();
|
IteratorType result = end();
|
||||||
@@ -3820,7 +3811,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
|
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_out_of_range, "iterator out of range", this));
|
JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data.m_value.destroy(m_data.m_type);
|
m_data.m_value.destroy(m_data.m_type);
|
||||||
@@ -3846,7 +3837,7 @@ public:
|
|||||||
case value_t::null:
|
case value_t::null:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
|
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -3862,7 +3853,7 @@ public:
|
|||||||
// make sure the iterator fits the current value
|
// make sure the iterator fits the current value
|
||||||
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
|
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_from_other_value, "iterators do not fit current value", this));
|
JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
IteratorType result = end();
|
IteratorType result = end();
|
||||||
@@ -3879,7 +3870,7 @@ public:
|
|||||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||||
|| !last.m_it.primitive_iterator.is_end()))
|
|| !last.m_it.primitive_iterator.is_end()))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this));
|
JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data.m_value.destroy(m_data.m_type);
|
m_data.m_value.destroy(m_data.m_type);
|
||||||
@@ -3907,7 +3898,7 @@ public:
|
|||||||
case value_t::null:
|
case value_t::null:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
|
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -3921,7 +3912,7 @@ public:
|
|||||||
// this erase only works for objects
|
// this erase only works for objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
|
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key));
|
const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key));
|
||||||
@@ -3936,7 +3927,7 @@ public:
|
|||||||
// this erase only works for objects
|
// this erase only works for objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
|
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto it = object_lookup(*this, std::forward<KeyType>(key));
|
const auto it = object_lookup(*this, std::forward<KeyType>(key));
|
||||||
@@ -3978,14 +3969,14 @@ public:
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= size()))
|
if (JSON_HEDLEY_UNLIKELY(idx >= size()))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
|
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
|
m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::erase_wrong_type, "erase()", *this);
|
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4474,7 +4465,7 @@ public:
|
|||||||
// push_back only works for null objects or arrays
|
// push_back only works for null objects or arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
|
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
@@ -4505,7 +4496,7 @@ public:
|
|||||||
// push_back only works for null objects or arrays
|
// push_back only works for null objects or arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
|
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
@@ -4535,7 +4526,7 @@ public:
|
|||||||
// push_back only works for null objects or objects
|
// push_back only works for null objects or objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
|
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
|
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
@@ -4589,7 +4580,7 @@ public:
|
|||||||
// emplace_back only works for null objects or arrays
|
// emplace_back only works for null objects or arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::emplace_wrong_type, "emplace_back()", *this);
|
JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
@@ -4612,7 +4603,7 @@ public:
|
|||||||
// emplace only works for null objects or arrays
|
// emplace only works for null objects or arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
|
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::emplace_wrong_type, "emplace()", *this);
|
JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
@@ -4664,14 +4655,14 @@ public:
|
|||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert to array and return iterator
|
// insert to array and return iterator
|
||||||
return insert_iterator(pos, val);
|
return insert_iterator(pos, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief inserts element into array
|
/// @brief inserts element into array
|
||||||
@@ -4684,7 +4675,7 @@ public:
|
|||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// moving into a local first keeps this safe even if val aliases
|
// moving into a local first keeps this safe even if val aliases
|
||||||
@@ -4693,7 +4684,7 @@ public:
|
|||||||
return insert_iterator(pos, std::move(tmp));
|
return insert_iterator(pos, std::move(tmp));
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief inserts copies of element into array
|
/// @brief inserts copies of element into array
|
||||||
@@ -4706,14 +4697,14 @@ public:
|
|||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert to array and return iterator
|
// insert to array and return iterator
|
||||||
return insert_iterator(pos, cnt, val);
|
return insert_iterator(pos, cnt, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief inserts range of elements into array
|
/// @brief inserts range of elements into array
|
||||||
@@ -4723,30 +4714,30 @@ public:
|
|||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if range iterators belong to the same JSON object
|
// check if range iterators belong to the same JSON object
|
||||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
|
if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_into_itself, "passed iterators may not belong to container", this));
|
JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// passed iterators must belong to arrays
|
// passed iterators must belong to arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array()))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to arrays", this));
|
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to arrays", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert to array and return iterator
|
// insert to array and return iterator
|
||||||
@@ -4760,13 +4751,13 @@ public:
|
|||||||
// insert only works for arrays
|
// insert only works for arrays
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if iterator pos fits to this JSON value
|
// check if iterator pos fits to this JSON value
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the values first: ilist may refer to elements of this array
|
// copy the values first: ilist may refer to elements of this array
|
||||||
@@ -4788,19 +4779,19 @@ public:
|
|||||||
// insert only works for objects
|
// insert only works for objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if range iterators belong to the same JSON object
|
// check if range iterators belong to the same JSON object
|
||||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// passed iterators must belong to objects
|
// passed iterators must belong to objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to objects", this));
|
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
|
m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
|
||||||
@@ -4817,7 +4808,7 @@ public:
|
|||||||
// j, not the copy made below)
|
// j, not the copy made below)
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", j);
|
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy first: j may be *this or one of its descendants, and is
|
// copy first: j may be *this or one of its descendants, and is
|
||||||
@@ -4835,13 +4826,13 @@ public:
|
|||||||
// check if range iterators belong to the same JSON object
|
// check if range iterators belong to the same JSON object
|
||||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// passed iterators must belong to objects
|
// passed iterators must belong to objects
|
||||||
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", *first.m_object);
|
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy first: the range may belong to *this or one of its
|
// copy first: the range may belong to *this or one of its
|
||||||
@@ -4877,7 +4868,7 @@ public:
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::update_wrong_type, "update()", *this);
|
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5040,7 +5031,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(array_t&)", *this);
|
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5057,7 +5048,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(object_t&)", *this);
|
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5073,7 +5064,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(string_t&)", *this);
|
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5089,7 +5080,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t&)", *this);
|
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5105,7 +5096,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t::container_type&)", *this);
|
JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6567,7 +6558,7 @@ public:
|
|||||||
if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
|
if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
|
||||||
{
|
{
|
||||||
// avoid undefined behavior
|
// avoid undefined behavior
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
|
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
// default case: insert add offset
|
// default case: insert add offset
|
||||||
@@ -6586,7 +6577,7 @@ public:
|
|||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::patch_add_parent_not_container, detail::concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
JSON_THROW(out_of_range::create(411, detail::concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -6609,7 +6600,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", last_path, "' not found"), this));
|
JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (parent.is_array())
|
else if (parent.is_array())
|
||||||
@@ -6621,7 +6612,7 @@ public:
|
|||||||
{
|
{
|
||||||
// the parent of a "remove" target must be an object or array
|
// the parent of a "remove" target must be an object or array
|
||||||
// (see #5396)
|
// (see #5396)
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::patch_remove_parent_not_container, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
JSON_THROW(out_of_range::create(413, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -6652,7 +6643,7 @@ public:
|
|||||||
// type check: top level value must be an array
|
// type check: top level value must be an array
|
||||||
if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(detail::exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &json_patch));
|
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate and apply the operations
|
// iterate and apply the operations
|
||||||
@@ -6673,14 +6664,14 @@ public:
|
|||||||
if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
|
if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
||||||
JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
|
JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the result is of type string
|
// check if the result is of type string
|
||||||
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
|
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
||||||
JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
|
JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// no error: return value
|
// no error: return value
|
||||||
@@ -6690,7 +6681,7 @@ public:
|
|||||||
// type check: every element of the array must be an object
|
// type check: every element of the array must be an object
|
||||||
if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
|
if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(detail::exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &val));
|
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect mandatory members
|
// collect mandatory members
|
||||||
@@ -6726,7 +6717,7 @@ public:
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
|
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::patch_move_into_child, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result));
|
JSON_THROW(out_of_range::create(414, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result));
|
||||||
}
|
}
|
||||||
|
|
||||||
// the "from" location must exist - use at()
|
// the "from" location must exist - use at()
|
||||||
@@ -6773,7 +6764,7 @@ public:
|
|||||||
// throw an exception if the test fails
|
// throw an exception if the test fails
|
||||||
if (JSON_HEDLEY_UNLIKELY(!success))
|
if (JSON_HEDLEY_UNLIKELY(!success))
|
||||||
{
|
{
|
||||||
JSON_THROW(other_error::create(detail::exception_id::patch_test_failed, detail::concat("unsuccessful: ", val.dump()), &val));
|
JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -6784,7 +6775,7 @@ public:
|
|||||||
{
|
{
|
||||||
// op must be "add", "remove", "replace", "move", "copy", or
|
// op must be "add", "remove", "replace", "move", "copy", or
|
||||||
// "test"
|
// "test"
|
||||||
JSON_THROW(parse_error::create(detail::exception_id::patch_invalid_operation, 0, detail::concat("operation value '", op, "' is invalid"), &val));
|
JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,19 +90,6 @@ private:
|
|||||||
return self.end();
|
return self.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief shared implementation of the const and non-const at() overloads
|
|
||||||
/// @throw std::out_of_range if @a key is not found
|
|
||||||
template<typename Self, typename KeyType>
|
|
||||||
static auto at_impl(Self& self, const KeyType& key) -> decltype((self.begin()->second))
|
|
||||||
{
|
|
||||||
const auto it = find_impl(self, key);
|
|
||||||
if (it == self.end())
|
|
||||||
{
|
|
||||||
JSON_THROW(std::out_of_range("key not found"));
|
|
||||||
}
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief remove the entry @a it points to, preserving order
|
/// @brief remove the entry @a it points to, preserving order
|
||||||
/// @note keys are not movable, so the tail is destroyed and re-constructed in place
|
/// @note keys are not movable, so the tail is destroyed and re-constructed in place
|
||||||
void erase_at(iterator it)
|
void erase_at(iterator it)
|
||||||
@@ -169,26 +156,46 @@ public:
|
|||||||
|
|
||||||
T& at(const key_type& key)
|
T& at(const key_type& key)
|
||||||
{
|
{
|
||||||
return at_impl(*this, key);
|
const auto it = find_impl(*this, key);
|
||||||
|
if (it == this->end())
|
||||||
|
{
|
||||||
|
JSON_THROW(std::out_of_range("key not found"));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||||
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
{
|
{
|
||||||
return at_impl(*this, key);
|
const auto it = find_impl(*this, key);
|
||||||
|
if (it == this->end())
|
||||||
|
{
|
||||||
|
JSON_THROW(std::out_of_range("key not found"));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const T& at(const key_type& key) const
|
const T& at(const key_type& key) const
|
||||||
{
|
{
|
||||||
return at_impl(*this, key);
|
const auto it = find_impl(*this, key);
|
||||||
|
if (it == this->end())
|
||||||
|
{
|
||||||
|
JSON_THROW(std::out_of_range("key not found"));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||||
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
{
|
{
|
||||||
return at_impl(*this, key);
|
const auto it = find_impl(*this, key);
|
||||||
|
if (it == this->end())
|
||||||
|
{
|
||||||
|
JSON_THROW(std::out_of_range("key not found"));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_type erase(const key_type& key)
|
size_type erase(const key_type& key)
|
||||||
|
|||||||
+1246
-924
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,599 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | 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 <array> // array
|
||||||
|
#include <cstdint> // uint32_t, uint64_t
|
||||||
|
|
||||||
|
// Number tokens that are hard to round correctly, with the IEEE-754 binary64
|
||||||
|
// and binary32 bits of their correctly rounded values (ties to even; infinity
|
||||||
|
// for an overflow, a signed zero for an underflow).
|
||||||
|
//
|
||||||
|
// For doubles and floats around 0, the smallest normal number, 1, 2^24, 2^53,
|
||||||
|
// 0.1, and the largest finite number, and for random ones, the exact midpoint
|
||||||
|
// m to the next number gives: m, m with one unit more and less in the last
|
||||||
|
// digit, m with "01" and "0...01" appended, m with trailing zeros, and m cut
|
||||||
|
// after 17 to 30 digits (rounded down and up, so that the rounding is decided
|
||||||
|
// after the 19th digit), in fixed and exponent notation, 30% of them negative.
|
||||||
|
// Tokens longer than 80 characters are left out, except for four of 700 digits
|
||||||
|
// and more. Zeros, underflow, overflow, huge exponents, and integers beyond 64
|
||||||
|
// bits complete the set. Of the 508 tokens, 134 (as double) and 150 (as
|
||||||
|
// float) need the exact comparison with the midpoint (detail::digit_comparison()).
|
||||||
|
//
|
||||||
|
// The expected bits were computed with exact rational arithmetic in Python
|
||||||
|
// (fractions.Fraction) and cross-checked with Python's float(); strtod_l and
|
||||||
|
// strtof_l of Apple's libc and of glibc agree. Generated by
|
||||||
|
// compact_hard_cases.py 5 (with hard_cases.py), see the pull request that
|
||||||
|
// added this file.
|
||||||
|
|
||||||
|
namespace float_hard_cases
|
||||||
|
{
|
||||||
|
|
||||||
|
struct hard_case
|
||||||
|
{
|
||||||
|
const char* token;
|
||||||
|
std::uint64_t bits64;
|
||||||
|
std::uint32_t bits32;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const std::array<hard_case, 508>& cases()
|
||||||
|
{
|
||||||
|
static const std::array<hard_case, 508> table =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{"-2.4703282292062327e-324", 0x8000000000000000u, 0x80000000u},
|
||||||
|
{"24703282292062328e-340", 0x0000000000000001u, 0x00000000u},
|
||||||
|
{"247032822920623272e-341", 0x0000000000000000u, 0x00000000u},
|
||||||
|
{"-0.2470328229206232721e-323", 0x8000000000000001u, 0x80000000u},
|
||||||
|
{"-0.24703282292062327208e-323", 0x8000000000000000u, 0x80000000u},
|
||||||
|
{"-2.4703282292062327209e-324", 0x8000000000000001u, 0x80000000u},
|
||||||
|
{"2.47032822920623272088e-324", 0x0000000000000000u, 0x00000000u},
|
||||||
|
{"247032822920623272089e-344", 0x0000000000000001u, 0x00000000u},
|
||||||
|
{"-247032822920623272088284396434e-353", 0x8000000000000000u, 0x80000000u},
|
||||||
|
{"0.247032822920623272088284396435e-323", 0x0000000000000001u, 0x00000000u},
|
||||||
|
{"-74109846876186981e-340", 0x8000000000000001u, 0x80000000u},
|
||||||
|
{"0.74109846876186982e-323", 0x0000000000000002u, 0x00000000u},
|
||||||
|
{"-0.7410984687618698162e-323", 0x8000000000000001u, 0x80000000u},
|
||||||
|
{"-7.410984687618698163e-324", 0x8000000000000002u, 0x80000000u},
|
||||||
|
{"7.4109846876186981626e-324", 0x0000000000000001u, 0x00000000u},
|
||||||
|
{"-74109846876186981627e-343", 0x8000000000000002u, 0x80000000u},
|
||||||
|
{"-741098468761869816264e-344", 0x8000000000000001u, 0x80000000u},
|
||||||
|
{"0.741098468761869816265e-323", 0x0000000000000002u, 0x00000000u},
|
||||||
|
{"0.741098468761869816264853189302e-323", 0x0000000000000001u, 0x00000000u},
|
||||||
|
{"-7.41098468761869816264853189303e-324", 0x8000000000000002u, 0x80000000u},
|
||||||
|
{"0.22250738585072006e-307", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||||
|
{"2.2250738585072007e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||||
|
{"2.225073858507200641e-308", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||||
|
{"-2225073858507200642e-326", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
||||||
|
{"22250738585072006419e-327", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||||
|
{"0.2225073858507200642e-307", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||||
|
{"0.222507385850720064199e-307", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||||
|
{"2.225073858507200642e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||||
|
{"-2.22507385850720064199176395546e-308", 0x800FFFFFFFFFFFFEu, 0x80000000u},
|
||||||
|
{"222507385850720064199176395547e-337", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||||
|
{"-2.2250738585072011e-308", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
||||||
|
{"-22250738585072012e-324", 0x8010000000000000u, 0x80000000u},
|
||||||
|
{"-2225073858507201136e-326", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
||||||
|
{"0.2225073858507201137e-307", 0x0010000000000000u, 0x00000000u},
|
||||||
|
{"0.2225073858507201136e-307", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||||
|
{"-2.2250738585072011361e-308", 0x8010000000000000u, 0x80000000u},
|
||||||
|
{"2.22507385850720113605e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||||
|
{"222507385850720113606e-328", 0x0010000000000000u, 0x00000000u},
|
||||||
|
{"22250738585072011360574097967e-336", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||||
|
{"0.222507385850720113605740979671e-307", 0x0010000000000000u, 0x00000000u},
|
||||||
|
{"22250738585072016e-324", 0x0010000000000000u, 0x00000000u},
|
||||||
|
{"0.22250738585072017e-307", 0x0010000000000001u, 0x00000000u},
|
||||||
|
{"0.222507385850720163e-307", 0x0010000000000000u, 0x00000000u},
|
||||||
|
{"2.225073858507201631e-308", 0x0010000000000001u, 0x00000000u},
|
||||||
|
{"-2.2250738585072016301e-308", 0x8010000000000000u, 0x80000000u},
|
||||||
|
{"22250738585072016302e-327", 0x0010000000000001u, 0x00000000u},
|
||||||
|
{"-222507385850720163012e-328", 0x8010000000000000u, 0x80000000u},
|
||||||
|
{"0.222507385850720163013e-307", 0x0010000000000001u, 0x00000000u},
|
||||||
|
{"0.222507385850720163012305563795e-307", 0x0010000000000000u, 0x00000000u},
|
||||||
|
{"-2.22507385850720163012305563796e-308", 0x8010000000000001u, 0x80000000u},
|
||||||
|
{"0.17976931348623156E+309", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||||
|
{"1.7976931348623157e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||||
|
{"1.797693134862315608e308", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||||
|
{"-1797693134862315609e290", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
||||||
|
{"-17976931348623156083e289", 0xFFEFFFFFFFFFFFFEu, 0xFF800000u},
|
||||||
|
{"-0.17976931348623156084E+309", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
||||||
|
{"0.179769313486231560835E+309", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||||
|
{"-1.79769313486231560836e308", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
||||||
|
{"1.79769313486231560835325876058e308", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||||
|
{"179769313486231560835325876059e279", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||||
|
{"1.7976931348623158e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||||
|
{"17976931348623159e292", 0x7FF0000000000000u, 0x7F800000u},
|
||||||
|
{"1797693134862315807e290", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||||
|
{"0.1797693134862315808E+309", 0x7FF0000000000000u, 0x7F800000u},
|
||||||
|
{"0.17976931348623158079E+309", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||||
|
{"-1.797693134862315808e308", 0xFFF0000000000000u, 0xFF800000u},
|
||||||
|
{"1.79769313486231580793e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||||
|
{"179769313486231580794e288", 0x7FF0000000000000u, 0x7F800000u},
|
||||||
|
{"179769313486231580793728971405e279", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||||
|
{"-0.179769313486231580793728971406E+309", 0xFFF0000000000000u, 0xFF800000u},
|
||||||
|
{"100000000000000011102230246251565404236316680908203125e-53", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"-1.00000000000000011102230246251565404236316680908203126", 0xBFF0000000000001u, 0xBF800000u},
|
||||||
|
{"1.00000000000000011102230246251565404236316680908203124e0", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"10000000000000001110223024625156540423631668090820312501e-55", 0x3FF0000000000001u, 0x3F800000u},
|
||||||
|
{"1.00000000000000011102230246251565404236316680908203125000000000000000000001", 0x3FF0000000000001u, 0x3F800000u},
|
||||||
|
{"10000000000000001e-16", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"1.0000000000000002", 0x3FF0000000000001u, 0x3F800000u},
|
||||||
|
{"1.000000000000000111", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"1.000000000000000112e0", 0x3FF0000000000001u, 0x3F800000u},
|
||||||
|
{"1.000000000000000111e0", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"-10000000000000001111e-19", 0xBFF0000000000001u, 0xBF800000u},
|
||||||
|
{"-100000000000000011102e-20", 0xBFF0000000000000u, 0xBF800000u},
|
||||||
|
{"-1.00000000000000011103", 0xBFF0000000000001u, 0xBF800000u},
|
||||||
|
{"1.00000000000000011102230246251", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"1.00000000000000011102230246252e0", 0x3FF0000000000001u, 0x3F800000u},
|
||||||
|
{"-0.999999999999999944488848768742172978818416595458984375", 0xBFF0000000000000u, 0xBF800000u},
|
||||||
|
{"-9.99999999999999944488848768742172978818416595458984376e-1", 0xBFF0000000000000u, 0xBF800000u},
|
||||||
|
{"999999999999999944488848768742172978818416595458984374e-54", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||||
|
{"0.99999999999999994448884876874217297881841659545898437501", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"9.99999999999999944488848768742172978818416595458984375000000000000000000001e-1", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"-0.99999999999999994", 0xBFEFFFFFFFFFFFFFu, 0xBF800000u},
|
||||||
|
{"9.9999999999999995e-1", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"9.999999999999999444e-1", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||||
|
{"9999999999999999445e-19", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"99999999999999994448e-20", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||||
|
{"-0.99999999999999994449", 0xBFF0000000000000u, 0xBF800000u},
|
||||||
|
{"0.999999999999999944488", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||||
|
{"-9.99999999999999944489e-1", 0xBFF0000000000000u, 0xBF800000u},
|
||||||
|
{"9.99999999999999944488848768742e-1", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||||
|
{"999999999999999944488848768743e-30", 0x3FF0000000000000u, 0x3F800000u},
|
||||||
|
{"-9.007199254740993e15", 0xC340000000000000u, 0xDA000000u},
|
||||||
|
{"9007199254740994e0", 0x4340000000000001u, 0x5A000000u},
|
||||||
|
{"9007199254740992", 0x4340000000000000u, 0x5A000000u},
|
||||||
|
{"9.00719925474099301e15", 0x4340000000000001u, 0x5A000000u},
|
||||||
|
{"9007199254740993000000000000000000001e-21", 0x4340000000000001u, 0x5A000000u},
|
||||||
|
{"-9007199254740993.000000000000000000000000000000", 0xC340000000000000u, 0xDA000000u},
|
||||||
|
{"90071992547409915e-1", 0x4340000000000000u, 0x5A000000u},
|
||||||
|
{"-9007199254740991.6", 0xC340000000000000u, 0xDA000000u},
|
||||||
|
{"9.0071992547409914e15", 0x433FFFFFFFFFFFFFu, 0x5A000000u},
|
||||||
|
{"-9007199254740991501e-3", 0xC340000000000000u, 0xDA000000u},
|
||||||
|
{"9007199254740991.5000000000000000000001", 0x4340000000000000u, 0x5A000000u},
|
||||||
|
{"9.0071992547409915000000000000000000000000000000e15", 0x4340000000000000u, 0x5A000000u},
|
||||||
|
{"0.100000000000000012490009027033011079765856266021728515625", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||||
|
{"1.00000000000000012490009027033011079765856266021728515626e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||||
|
{"100000000000000012490009027033011079765856266021728515624e-57", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||||
|
{"0.10000000000000001249000902703301107976585626602172851562501", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||||
|
{"0.10000000000000001", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||||
|
{"1.0000000000000002e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||||
|
{"-1.000000000000000124e-1", 0xBFB999999999999Au, 0xBDCCCCCDu},
|
||||||
|
{"1000000000000000125e-19", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||||
|
{"-10000000000000001249e-20", 0xBFB999999999999Au, 0xBDCCCCCDu},
|
||||||
|
{"0.1000000000000000125", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||||
|
{"0.10000000000000001249", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||||
|
{"1.00000000000000012491e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||||
|
{"1.00000000000000012490009027033e-1", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||||
|
{"100000000000000012490009027034e-30", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||||
|
{"2.45134755833537796875e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||||
|
{"-245134755833537796876e-6", 0xC2EBDE5C4164D83Au, 0xD75EF2E2u},
|
||||||
|
{"-245134755833537.796874", 0xC2EBDE5C4164D839u, 0xD75EF2E2u},
|
||||||
|
{"2.4513475583353779687501e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||||
|
{"245134755833537796875000000000000000000001e-27", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||||
|
{"245134755833537.796875000000000000000000000000000000", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||||
|
{"2.4513475583353779e14", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
||||||
|
{"2451347558335378e-1", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||||
|
{"2451347558335377968e-4", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
||||||
|
{"245134755833537.7969", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||||
|
{"245134755833537.79687", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
||||||
|
{"2.4513475583353779688e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||||
|
{"181510327827821147441864013671875e-23", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||||
|
{"-1815103278.27821147441864013671876", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||||
|
{"1.81510327827821147441864013671874e9", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||||
|
{"18151032782782114744186401367187501e-25", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||||
|
{"1815103278.27821147441864013671875000000000000000000001", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||||
|
{"-1.81510327827821147441864013671875000000000000000000000000000000e9", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||||
|
{"18151032782782114e-7", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||||
|
{"1815103278.2782115", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||||
|
{"1815103278.278211474", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||||
|
{"-1.815103278278211475e9", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||||
|
{"1.8151032782782114744e9", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||||
|
{"-18151032782782114745e-10", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||||
|
{"181510327827821147441e-11", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||||
|
{"1815103278.27821147442", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||||
|
{"1815103278.27821147441864013671", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||||
|
{"1.81510327827821147441864013672e9", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||||
|
{"3809325632181785344", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||||
|
{"3.809325632181785345e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||||
|
{"3809325632181785343e0", 0x43CA6EB8BD69FE29u, 0x5E5375C6u},
|
||||||
|
{"3809325632181785344.01", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||||
|
{"3.809325632181785344000000000000000000001e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||||
|
{"3809325632181785344000000000000000000000000000000e-30", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||||
|
{"3809325632181785300", 0x43CA6EB8BD69FE29u, 0x5E5375C6u},
|
||||||
|
{"3.8093256321817854e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||||
|
{"4.046966549916366943359375e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"4046966549916366943359376e-12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"4046966549916.366943359374", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||||
|
{"4.04696654991636694335937501e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"4046966549916366943359375000000000000000000001e-33", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"-4046966549916.366943359375000000000000000000000000000000", 0xC28D7210076CE2F0u, 0xD46B9080u},
|
||||||
|
{"4.0469665499163669e12", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||||
|
{"4046966549916367e-3", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"4046966549916366943e-6", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||||
|
{"4046966549916.366944", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"4046966549916.3669433", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||||
|
{"4.0469665499163669434e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"-4.04696654991636694335e12", 0xC28D7210076CE2EFu, 0xD46B9080u},
|
||||||
|
{"404696654991636694336e-8", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||||
|
{"28093802557000874e154", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||||
|
{"0.28093802557000875E+171", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||||
|
{"0.2809380255700087447E+171", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||||
|
{"2.809380255700087448e170", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||||
|
{"2.8093802557000874472e170", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||||
|
{"28093802557000874473e151", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||||
|
{"280938025570008744728e150", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||||
|
{"-0.280938025570008744729E+171", 0xE3529C3B77330BDCu, 0xFF800000u},
|
||||||
|
{"-0.280938025570008744728403667979E+171", 0xE3529C3B77330BDBu, 0xFF800000u},
|
||||||
|
{"2.8093802557000874472840366798e170", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||||
|
{"0.39523280297734525e-154", 0x1FE0F51BF17FD374u, 0x00000000u},
|
||||||
|
{"-3.9523280297734526e-155", 0x9FE0F51BF17FD375u, 0x80000000u},
|
||||||
|
{"3.952328029773452547e-155", 0x1FE0F51BF17FD374u, 0x00000000u},
|
||||||
|
{"3952328029773452548e-173", 0x1FE0F51BF17FD375u, 0x00000000u},
|
||||||
|
{"-39523280297734525478e-174", 0x9FE0F51BF17FD374u, 0x80000000u},
|
||||||
|
{"0.39523280297734525479e-154", 0x1FE0F51BF17FD375u, 0x00000000u},
|
||||||
|
{"-0.395232802977345254787e-154", 0x9FE0F51BF17FD374u, 0x80000000u},
|
||||||
|
{"-3.95232802977345254788e-155", 0x9FE0F51BF17FD375u, 0x80000000u},
|
||||||
|
{"-3.95232802977345254787245825501e-155", 0x9FE0F51BF17FD374u, 0x80000000u},
|
||||||
|
{"395232802977345254787245825502e-184", 0x1FE0F51BF17FD375u, 0x00000000u},
|
||||||
|
{"-1.0790205420931879e-276", 0x86A3209CA6233255u, 0x80000000u},
|
||||||
|
{"-1079020542093188e-291", 0x86A3209CA6233256u, 0x80000000u},
|
||||||
|
{"-1079020542093187947e-294", 0x86A3209CA6233255u, 0x80000000u},
|
||||||
|
{"0.1079020542093187948e-275", 0x06A3209CA6233256u, 0x00000000u},
|
||||||
|
{"0.1079020542093187947e-275", 0x06A3209CA6233255u, 0x00000000u},
|
||||||
|
{"1.0790205420931879471e-276", 0x06A3209CA6233256u, 0x00000000u},
|
||||||
|
{"1.07902054209318794701e-276", 0x06A3209CA6233255u, 0x00000000u},
|
||||||
|
{"-107902054209318794702e-296", 0x86A3209CA6233256u, 0x80000000u},
|
||||||
|
{"107902054209318794701153285302e-305", 0x06A3209CA6233255u, 0x00000000u},
|
||||||
|
{"-0.107902054209318794701153285303e-275", 0x86A3209CA6233256u, 0x80000000u},
|
||||||
|
{"58530471071351308e-228", 0x1413B446E6A16A3Bu, 0x00000000u},
|
||||||
|
{"-0.58530471071351309e-211", 0x9413B446E6A16A3Cu, 0x80000000u},
|
||||||
|
{"0.5853047107135130893e-211", 0x1413B446E6A16A3Bu, 0x00000000u},
|
||||||
|
{"-5.853047107135130894e-212", 0x9413B446E6A16A3Cu, 0x80000000u},
|
||||||
|
{"5.853047107135130893e-212", 0x1413B446E6A16A3Bu, 0x00000000u},
|
||||||
|
{"58530471071351308931e-231", 0x1413B446E6A16A3Cu, 0x00000000u},
|
||||||
|
{"-585304710713513089304e-232", 0x9413B446E6A16A3Bu, 0x80000000u},
|
||||||
|
{"0.585304710713513089305e-211", 0x1413B446E6A16A3Cu, 0x00000000u},
|
||||||
|
{"-0.585304710713513089304248824438e-211", 0x9413B446E6A16A3Bu, 0x80000000u},
|
||||||
|
{"5.85304710713513089304248824439e-212", 0x1413B446E6A16A3Cu, 0x00000000u},
|
||||||
|
{"0.19334214893983531e-78", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||||
|
{"1.9334214893983532e-79", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||||
|
{"1.933421489398353102e-79", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||||
|
{"1933421489398353103e-97", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||||
|
{"19334214893983531023e-98", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||||
|
{"0.19334214893983531024e-78", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||||
|
{"0.193342148939835310231e-78", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||||
|
{"1.93342148939835310232e-79", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||||
|
{"-1.93342148939835310231359014704e-79", 0xAF96ECBF1CFB10F6u, 0x80000000u},
|
||||||
|
{"193342148939835310231359014705e-108", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||||
|
{"2.9873358928024455e227", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||||
|
{"29873358928024456e211", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||||
|
{"298733589280244551e210", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||||
|
{"0.2987335892802445511E+228", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||||
|
{"0.29873358928024455109E+228", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||||
|
{"2.987335892802445511e227", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||||
|
{"-2.98733589280244551098e227", 0xEF2938807814E8A2u, 0xFF800000u},
|
||||||
|
{"-298733589280244551099e207", 0xEF2938807814E8A3u, 0xFF800000u},
|
||||||
|
{"298733589280244551098081559931e198", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||||
|
{"0.298733589280244551098081559932E+228", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||||
|
{"7.0064923216240853e-46", 0x3690000000000000u, 0x00000000u},
|
||||||
|
{"70064923216240854e-62", 0x3690000000000000u, 0x00000001u},
|
||||||
|
{"7006492321624085354e-64", 0x3690000000000000u, 0x00000000u},
|
||||||
|
{"-0.7006492321624085355e-45", 0xB690000000000000u, 0x80000001u},
|
||||||
|
{"0.70064923216240853546e-45", 0x3690000000000000u, 0x00000000u},
|
||||||
|
{"7.0064923216240853547e-46", 0x3690000000000000u, 0x00000001u},
|
||||||
|
{"7.00649232162408535461e-46", 0x3690000000000000u, 0x00000000u},
|
||||||
|
{"-700649232162408535462e-66", 0xB690000000000000u, 0x80000001u},
|
||||||
|
{"-700649232162408535461864791644e-75", 0xB690000000000000u, 0x80000000u},
|
||||||
|
{"0.700649232162408535461864791645e-45", 0x3690000000000000u, 0x00000001u},
|
||||||
|
{"21019476964872256e-61", 0x36A8000000000000u, 0x00000001u},
|
||||||
|
{"0.21019476964872257e-44", 0x36A8000000000000u, 0x00000002u},
|
||||||
|
{"-0.2101947696487225606e-44", 0xB6A8000000000000u, 0x80000001u},
|
||||||
|
{"-2.101947696487225607e-45", 0xB6A8000000000000u, 0x80000002u},
|
||||||
|
{"2.1019476964872256063e-45", 0x36A8000000000000u, 0x00000001u},
|
||||||
|
{"21019476964872256064e-64", 0x36A8000000000000u, 0x00000002u},
|
||||||
|
{"-210194769648722560638e-65", 0xB6A8000000000000u, 0x80000001u},
|
||||||
|
{"-0.210194769648722560639e-44", 0xB6A8000000000000u, 0x80000002u},
|
||||||
|
{"-0.210194769648722560638559437493e-44", 0xB6A8000000000000u, 0x80000001u},
|
||||||
|
{"2.10194769648722560638559437494e-45", 0x36A8000000000000u, 0x00000002u},
|
||||||
|
{"0.11754941406275178e-37", 0x380FFFFFA0000000u, 0x007FFFFEu},
|
||||||
|
{"1.1754941406275179e-38", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||||
|
{"-1.175494140627517859e-38", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
||||||
|
{"117549414062751786e-55", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||||
|
{"-11754941406275178592e-57", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
||||||
|
{"0.11754941406275178593e-37", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||||
|
{"0.117549414062751785924e-37", 0x380FFFFFA0000000u, 0x007FFFFEu},
|
||||||
|
{"1.17549414062751785925e-38", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||||
|
{"-1.17549414062751785924617589866e-38", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
||||||
|
{"117549414062751785924617589867e-67", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||||
|
{"1.1754942807573642e-38", 0x380FFFFFDFFFFFFFu, 0x007FFFFFu},
|
||||||
|
{"11754942807573643e-54", 0x380FFFFFE0000000u, 0x00800000u},
|
||||||
|
{"1175494280757364291e-56", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
||||||
|
{"0.1175494280757364292e-37", 0x380FFFFFE0000000u, 0x00800000u},
|
||||||
|
{"0.11754942807573642917e-37", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
||||||
|
{"-1.1754942807573642918e-38", 0xB80FFFFFE0000000u, 0x80800000u},
|
||||||
|
{"-1.17549428075736429172e-38", 0xB80FFFFFE0000000u, 0x807FFFFFu},
|
||||||
|
{"-117549428075736429173e-58", 0xB80FFFFFE0000000u, 0x80800000u},
|
||||||
|
{"117549428075736429172788299103e-67", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
||||||
|
{"0.117549428075736429172788299104e-37", 0x380FFFFFE0000000u, 0x00800000u},
|
||||||
|
{"11754944208872107e-54", 0x3810000010000000u, 0x00800000u},
|
||||||
|
{"-0.11754944208872108e-37", 0xB810000010000000u, 0x80800001u},
|
||||||
|
{"0.1175494420887210724e-37", 0x3810000010000000u, 0x00800000u},
|
||||||
|
{"1.175494420887210725e-38", 0x3810000010000000u, 0x00800001u},
|
||||||
|
{"1.1754944208872107242e-38", 0x3810000010000000u, 0x00800000u},
|
||||||
|
{"-11754944208872107243e-57", 0xB810000010000000u, 0x80800001u},
|
||||||
|
{"-11754944208872107242e-57", 0xB810000010000000u, 0x80800000u},
|
||||||
|
{"0.117549442088721072421e-37", 0x3810000010000000u, 0x00800001u},
|
||||||
|
{"0.11754944208872107242095900834e-37", 0x3810000010000000u, 0x00800000u},
|
||||||
|
{"-1.17549442088721072420959008341e-38", 0xB810000010000000u, 0x80800001u},
|
||||||
|
{"340282336497324057985868971510891282432", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"-3.40282336497324057985868971510891282433e38", 0xC7EFFFFFD0000000u, 0xFF7FFFFFu},
|
||||||
|
{"340282336497324057985868971510891282431e0", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"340282336497324057985868971510891282432.01", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||||
|
{"3.40282336497324057985868971510891282432000000000000000000001e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||||
|
{"340282336497324057985868971510891282432000000000000000000000000000000e-30", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"340282336497324050000000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"3.4028233649732406e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||||
|
{"3.402823364973240579e38", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"340282336497324058e21", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||||
|
{"34028233649732405798e19", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"340282336497324057990000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||||
|
{"340282336497324057985000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"3.40282336497324057986e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||||
|
{"3.4028233649732405798586897151e38", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||||
|
{"340282336497324057985868971511e9", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||||
|
{"3.40282356779733661637539395458142568448e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"-340282356779733661637539395458142568449e0", 0xC7EFFFFFF0000000u, 0xFF800000u},
|
||||||
|
{"340282356779733661637539395458142568447", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||||
|
{"3.4028235677973366163753939545814256844801e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"340282356779733661637539395458142568448000000000000000000001e-21", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"340282356779733661637539395458142568448.000000000000000000000000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"3.4028235677973366e38", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||||
|
{"-34028235677973367e22", 0xC7EFFFFFF0000000u, 0xFF800000u},
|
||||||
|
{"3402823567797336616e20", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||||
|
{"340282356779733661700000000000000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"340282356779733661630000000000000000000", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||||
|
{"3.4028235677973366164e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"3.40282356779733661637e38", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||||
|
{"340282356779733661638e18", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"-340282356779733661637539395458e9", 0xC7EFFFFFF0000000u, 0xFF7FFFFFu},
|
||||||
|
{"340282356779733661637539395459000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||||
|
{"-1000000059604644775390625e-24", 0xBFF0000010000000u, 0xBF800000u},
|
||||||
|
{"-1.000000059604644775390626", 0xBFF0000010000000u, 0xBF800001u},
|
||||||
|
{"-1.000000059604644775390624e0", 0xBFF0000010000000u, 0xBF800000u},
|
||||||
|
{"100000005960464477539062501e-26", 0x3FF0000010000000u, 0x3F800001u},
|
||||||
|
{"1.000000059604644775390625000000000000000000001", 0x3FF0000010000000u, 0x3F800001u},
|
||||||
|
{"1.000000059604644775390625000000000000000000000000000000e0", 0x3FF0000010000000u, 0x3F800000u},
|
||||||
|
{"-10000000596046447e-16", 0xBFF0000010000000u, 0xBF800000u},
|
||||||
|
{"1.0000000596046448", 0x3FF0000010000000u, 0x3F800001u},
|
||||||
|
{"1.000000059604644775", 0x3FF0000010000000u, 0x3F800000u},
|
||||||
|
{"-1.000000059604644776e0", 0xBFF0000010000000u, 0xBF800001u},
|
||||||
|
{"-1.0000000596046447753e0", 0xBFF0000010000000u, 0xBF800000u},
|
||||||
|
{"10000000596046447754e-19", 0x3FF0000010000000u, 0x3F800001u},
|
||||||
|
{"100000005960464477539e-20", 0x3FF0000010000000u, 0x3F800000u},
|
||||||
|
{"-1.0000000596046447754", 0xBFF0000010000000u, 0xBF800001u},
|
||||||
|
{"0.9999999701976776123046875", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||||
|
{"9.999999701976776123046876e-1", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||||
|
{"9999999701976776123046874e-25", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
||||||
|
{"0.999999970197677612304687501", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||||
|
{"-9.999999701976776123046875000000000000000000001e-1", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
||||||
|
{"-9999999701976776123046875000000000000000000000000000000e-55", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
||||||
|
{"0.99999997019767761", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
||||||
|
{"-9.9999997019767762e-1", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
||||||
|
{"-9.999999701976776123e-1", 0xBFEFFFFFF0000000u, 0xBF7FFFFFu},
|
||||||
|
{"9999999701976776124e-19", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||||
|
{"9999999701976776123e-19", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
||||||
|
{"0.99999997019767761231", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||||
|
{"-0.999999970197677612304", 0xBFEFFFFFF0000000u, 0xBF7FFFFFu},
|
||||||
|
{"9.99999970197677612305e-1", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||||
|
{"-1.6777217e7", 0xC170000010000000u, 0xCB800000u},
|
||||||
|
{"16777218e0", 0x4170000020000000u, 0x4B800001u},
|
||||||
|
{"16777216", 0x4170000000000000u, 0x4B800000u},
|
||||||
|
{"-1.677721701e7", 0xC17000001028F5C3u, 0xCB800001u},
|
||||||
|
{"-16777217000000000000000000001e-21", 0xC170000010000000u, 0xCB800001u},
|
||||||
|
{"16777217.000000000000000000000000000000", 0x4170000010000000u, 0x4B800000u},
|
||||||
|
{"167772155e-1", 0x416FFFFFF0000000u, 0x4B800000u},
|
||||||
|
{"16777215.6", 0x416FFFFFF3333333u, 0x4B800000u},
|
||||||
|
{"1.67772154e7", 0x416FFFFFECCCCCCDu, 0x4B7FFFFFu},
|
||||||
|
{"16777215501e-3", 0x416FFFFFF0083127u, 0x4B800000u},
|
||||||
|
{"-16777215.5000000000000000000001", 0xC16FFFFFF0000000u, 0xCB800000u},
|
||||||
|
{"-1.67772155000000000000000000000000000000e7", 0xC16FFFFFF0000000u, 0xCB800000u},
|
||||||
|
{"0.1000000052154064178466796875", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||||
|
{"1.000000052154064178466796876e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||||
|
{"-1000000052154064178466796874e-28", 0xBFB99999B0000000u, 0xBDCCCCCDu},
|
||||||
|
{"0.100000005215406417846679687501", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||||
|
{"1.000000052154064178466796875000000000000000000001e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||||
|
{"-1000000052154064178466796875000000000000000000000000000000e-58", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
||||||
|
{"-0.10000000521540641", 0xBFB99999AFFFFFFFu, 0xBDCCCCCDu},
|
||||||
|
{"-1.0000000521540642e-1", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
||||||
|
{"1.000000052154064178e-1", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
||||||
|
{"-1000000052154064179e-19", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
||||||
|
{"10000000521540641784e-20", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
||||||
|
{"0.10000000521540641785", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||||
|
{"0.100000005215406417846", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
||||||
|
{"1.00000005215406417847e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||||
|
{"5.429001220703125e3", 0x40B5350050000000u, 0x45A9A802u},
|
||||||
|
{"-5429001220703126e-12", 0xC0B5350050000001u, 0xC5A9A803u},
|
||||||
|
{"-5429.001220703124", 0xC0B535004FFFFFFFu, 0xC5A9A802u},
|
||||||
|
{"5.42900122070312501e3", 0x40B5350050000000u, 0x45A9A803u},
|
||||||
|
{"5429001220703125000000000000000000001e-33", 0x40B5350050000000u, 0x45A9A803u},
|
||||||
|
{"-5429.001220703125000000000000000000000000000000", 0xC0B5350050000000u, 0xC5A9A802u},
|
||||||
|
{"503719056e0", 0x41BE062490000000u, 0x4DF03124u},
|
||||||
|
{"503719057", 0x41BE062491000000u, 0x4DF03125u},
|
||||||
|
{"5.03719055e8", 0x41BE06248F000000u, 0x4DF03124u},
|
||||||
|
{"50371905601e-2", 0x41BE062490028F5Cu, 0x4DF03125u},
|
||||||
|
{"503719056.000000000000000000001", 0x41BE062490000000u, 0x4DF03125u},
|
||||||
|
{"5.03719056000000000000000000000000000000e8", 0x41BE062490000000u, 0x4DF03124u},
|
||||||
|
{"-92331620", 0xC196037990000000u, 0xCCB01BCCu},
|
||||||
|
{"9.233163e7", 0x41960379B8000000u, 0x4CB01BCEu},
|
||||||
|
{"9233161e1", 0x4196037968000000u, 0x4CB01BCBu},
|
||||||
|
{"92331620.1", 0x4196037990666666u, 0x4CB01BCDu},
|
||||||
|
{"9.233162000000000000000000001e7", 0x4196037990000000u, 0x4CB01BCDu},
|
||||||
|
{"9233162000000000000000000000000000000e-29", 0x4196037990000000u, 0x4CB01BCCu},
|
||||||
|
{"3.002458625e6", 0x4146E82D50000000u, 0x4A37416Au},
|
||||||
|
{"3002458626e-3", 0x4146E82D5020C49Cu, 0x4A37416Bu},
|
||||||
|
{"3002458.624", 0x4146E82D4FDF3B64u, 0x4A37416Au},
|
||||||
|
{"-3.00245862501e6", 0xC146E82D500053E3u, 0xCA37416Bu},
|
||||||
|
{"3002458625000000000000000000001e-24", 0x4146E82D50000000u, 0x4A37416Bu},
|
||||||
|
{"3002458.625000000000000000000000000000000", 0x4146E82D50000000u, 0x4A37416Au},
|
||||||
|
{"-1095485584696182596504479582065262592e1", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||||
|
{"10954855846961825965044795820652625930", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||||
|
{"1.095485584696182596504479582065262591e37", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||||
|
{"109548558469618259650447958206526259201e-1", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||||
|
{"-10954855846961825965044795820652625920.00000000000000000001", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||||
|
{"1.095485584696182596504479582065262592000000000000000000000000000000e37", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||||
|
{"10954855846961825e21", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||||
|
{"10954855846961826000000000000000000000", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||||
|
{"10954855846961825960000000000000000000", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||||
|
{"1.095485584696182597e37", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||||
|
{"1.0954855846961825965e37", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||||
|
{"-10954855846961825966e18", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||||
|
{"10954855846961825965e18", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||||
|
{"10954855846961825965100000000000000000", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||||
|
{"10954855846961825965044795820600000000", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||||
|
{"-1.09548558469618259650447958207e37", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||||
|
{"1.6449216019182103706535606608388384863861375606575165875256061553955078126e-21", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||||
|
{"-16449216019182103706535606608388384863861375606575165875256061553955078124e-94", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||||
|
{"-0.0000000000000000000016449216019182103", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||||
|
{"-1.6449216019182104e-21", 0xBB9F125A50000000u, 0x9CF892D3u},
|
||||||
|
{"-1.64492160191821037e-21", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||||
|
{"-1644921601918210371e-39", 0xBB9F125A50000000u, 0x9CF892D3u},
|
||||||
|
{"-16449216019182103706e-40", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||||
|
{"0.0000000000000000000016449216019182103707", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||||
|
{"0.00000000000000000000164492160191821037065", 0x3B9F125A50000000u, 0x1CF892D2u},
|
||||||
|
{"1.64492160191821037066e-21", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||||
|
{"1.64492160191821037065356066083e-21", 0x3B9F125A50000000u, 0x1CF892D2u},
|
||||||
|
{"164492160191821037065356066084e-50", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||||
|
{"6.565061509609222412109375e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||||
|
{"6565061509609222412109376e-25", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||||
|
{"0.6565061509609222412109374", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||||
|
{"6.56506150960922241210937501e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||||
|
{"6565061509609222412109375000000000000000000001e-46", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||||
|
{"-0.6565061509609222412109375000000000000000000000000000000", 0xBFE5021930000000u, 0xBF2810CAu},
|
||||||
|
{"6.5650615096092224e-1", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||||
|
{"-65650615096092225e-17", 0xBFE5021930000000u, 0xBF2810CAu},
|
||||||
|
{"6565061509609222412e-19", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||||
|
{"0.6565061509609222413", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||||
|
{"0.65650615096092224121", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||||
|
{"6.5650615096092224122e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||||
|
{"-6.5650615096092224121e-1", 0xBFE5021930000000u, 0xBF2810C9u},
|
||||||
|
{"656506150960922241211e-21", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||||
|
{"18014627239033005156980393746124491372029297053813934326171875e-77", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||||
|
{"0.00000000000000018014627239033005156980393746124491372029297053813934326171876", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||||
|
{"-1.8014627239033005156980393746124491372029297053813934326171874e-16", 0xBCA9F63970000000u, 0xA54FB1CBu},
|
||||||
|
{"1801462723903300515698039374612449137202929705381393432617187501e-79", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||||
|
{"18014627239033005e-32", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||||
|
{"0.00000000000000018014627239033006", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||||
|
{"0.0000000000000001801462723903300515", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||||
|
{"-1.801462723903300516e-16", 0xBCA9F63970000000u, 0xA54FB1CCu},
|
||||||
|
{"-1.8014627239033005156e-16", 0xBCA9F63970000000u, 0xA54FB1CBu},
|
||||||
|
{"18014627239033005157e-35", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||||
|
{"180146272390330051569e-36", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||||
|
{"-0.00000000000000018014627239033005157", 0xBCA9F63970000000u, 0xA54FB1CCu},
|
||||||
|
{"0.000000000000000180146272390330051569803937461", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||||
|
{"1.80146272390330051569803937462e-16", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||||
|
{"0.05534819327294826507568359375", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||||
|
{"-5.534819327294826507568359376e-2", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||||
|
{"5534819327294826507568359374e-29", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||||
|
{"-0.0553481932729482650756835937501", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||||
|
{"5.534819327294826507568359375000000000000000000001e-2", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||||
|
{"5534819327294826507568359375000000000000000000000000000000e-59", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||||
|
{"0.055348193272948265", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||||
|
{"5.5348193272948266e-2", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||||
|
{"5.534819327294826507e-2", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||||
|
{"5534819327294826508e-20", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||||
|
{"55348193272948265075e-21", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||||
|
{"-0.055348193272948265076", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||||
|
{"0.0553481932729482650756", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||||
|
{"-5.53481932729482650757e-2", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||||
|
{"5.179692133247783258005389047985340416e36", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||||
|
{"5179692133247783258005389047985340417e0", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||||
|
{"5179692133247783258005389047985340415", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||||
|
{"-5.17969213324778325800538904798534041601e36", 0xC78F2C9450000000u, 0xFC7964A3u},
|
||||||
|
{"-5179692133247783258005389047985340416000000000000000000001e-21", 0xC78F2C9450000000u, 0xFC7964A3u},
|
||||||
|
{"-5179692133247783258005389047985340416.000000000000000000000000000000", 0xC78F2C9450000000u, 0xFC7964A2u},
|
||||||
|
{"5.1796921332477832e36", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||||
|
{"51796921332477833e20", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||||
|
{"-5179692133247783258e18", 0xC78F2C9450000000u, 0xFC7964A2u},
|
||||||
|
{"5179692133247783259000000000000000000", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||||
|
{"5179692133247783258000000000000000000", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||||
|
{"5.1796921332477832581e36", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||||
|
{"-5.179692133247783258e36", 0xC78F2C9450000000u, 0xFC7964A2u},
|
||||||
|
{"-517969213324778325801e16", 0xC78F2C9450000000u, 0xFC7964A3u},
|
||||||
|
{"517969213324778325800538904798e7", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||||
|
{"5179692133247783258005389047990000000", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||||
|
{
|
||||||
|
"0.22250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350"
|
||||||
|
"8791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901"
|
||||||
|
"2981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630"
|
||||||
|
"6666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050"
|
||||||
|
"5108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462"
|
||||||
|
"1572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884"
|
||||||
|
"4239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704"
|
||||||
|
"2756718644338377048603786162277173854562306587467901408672332763671875e-307", 0x0010000000000000u, 0x00000000u
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508"
|
||||||
|
"7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012"
|
||||||
|
"9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306"
|
||||||
|
"6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505"
|
||||||
|
"1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621"
|
||||||
|
"5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844"
|
||||||
|
"2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042"
|
||||||
|
"756718644338377048603786162277173854562306587467901408672332763671875000000000000000000001e-308", 0x0010000000000000u, 0x00000000u
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"0.11754942807573642917278829910357665133228589927589904276829631184250030649651730385585324256680905"
|
||||||
|
"8189392089843750000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000000e-37", 0x380FFFFFE0000000u, 0x00800000u
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"1175494280757364291727882991035766513322858992758990427682963118425003064965173038558532425668090581"
|
||||||
|
"8939208984375000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
"00000000000001e-751", 0x380FFFFFE0000000u, 0x00800000u
|
||||||
|
},
|
||||||
|
{"0", 0x0000000000000000u, 0x00000000u},
|
||||||
|
{"-0", 0x8000000000000000u, 0x80000000u},
|
||||||
|
{"0.0", 0x0000000000000000u, 0x00000000u},
|
||||||
|
{"-0.0", 0x8000000000000000u, 0x80000000u},
|
||||||
|
{"0e999999999999999999999", 0x0000000000000000u, 0x00000000u},
|
||||||
|
{"-0.000e-99999", 0x8000000000000000u, 0x80000000u},
|
||||||
|
{"1e-400", 0x0000000000000000u, 0x00000000u},
|
||||||
|
{"-1e-400", 0x8000000000000000u, 0x80000000u},
|
||||||
|
{"1e400", 0x7FF0000000000000u, 0x7F800000u},
|
||||||
|
{"-1e400", 0xFFF0000000000000u, 0xFF800000u},
|
||||||
|
{"1e-50", 0x358DEE7A4AD4B81Fu, 0x00000000u},
|
||||||
|
{"-1e-50", 0xB58DEE7A4AD4B81Fu, 0x80000000u},
|
||||||
|
{"1e39", 0x48078287F49C4A1Du, 0x7F800000u},
|
||||||
|
{"-1e39", 0xC8078287F49C4A1Du, 0xFF800000u},
|
||||||
|
{"1e99999999999999999999999999", 0x7FF0000000000000u, 0x7F800000u},
|
||||||
|
{"1e-99999999999999999999999999", 0x0000000000000000u, 0x00000000u},
|
||||||
|
{"1e0000000000000000000000000000000000000000308", 0x7FE1CCF385EBC8A0u, 0x7F800000u},
|
||||||
|
{"123456789012345678901234567890e-30", 0x3FBF9ADD3746F65Fu, 0x3DFCD6EAu},
|
||||||
|
{"18446744073709551615", 0x43F0000000000000u, 0x5F800000u},
|
||||||
|
{"18446744073709551616", 0x43F0000000000000u, 0x5F800000u},
|
||||||
|
{"-9223372036854775808", 0xC3E0000000000000u, 0xDF000000u},
|
||||||
|
{"-9223372036854775809", 0xC3E0000000000000u, 0xDF000000u},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace float_hard_cases
|
||||||
@@ -51,7 +51,22 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fuzzer_common.hpp"
|
#include <cassert>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// the round-trip checks below are assertions; NDEBUG would compile them away
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#error "the fuzzer drivers must be built without NDEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
||||||
|
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
||||||
|
static bool same_value(const json& lhs, const json& rhs)
|
||||||
|
{
|
||||||
|
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
}
|
||||||
|
|
||||||
// value-stable comparison for the round-trip checks below; see the note
|
// value-stable comparison for the round-trip checks below; see the note
|
||||||
// above on why this compares dump()s rather than the json values directly
|
// above on why this compares dump()s rather than the json values directly
|
||||||
@@ -65,9 +80,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
|||||||
{
|
{
|
||||||
std::vector<uint8_t> const vec1(data, data + size);
|
std::vector<uint8_t> const vec1(data, data + size);
|
||||||
|
|
||||||
// step 0: parse input without exceptions
|
// step 0: parse input without exceptions; a parse error must then be
|
||||||
|
// reported as a discarded value, never thrown
|
||||||
|
json j_noexcept;
|
||||||
bool noexcept_threw = false;
|
bool noexcept_threw = false;
|
||||||
json const j_noexcept = parse_without_exceptions([&] { return json::from_bjdata(vec1, true, false); }, noexcept_threw);
|
try
|
||||||
|
{
|
||||||
|
j_noexcept = json::from_bjdata(vec1, true, false);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch (const json::exception&)
|
||||||
|
{
|
||||||
|
// type and out-of-range errors are not parse errors and still throw
|
||||||
|
noexcept_threw = true;
|
||||||
|
}
|
||||||
// whether step 1 succeeded; if not, the catch blocks below check that
|
// whether step 1 succeeded; if not, the catch blocks below check that
|
||||||
// step 0 failed, too
|
// step 0 failed, too
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|||||||
@@ -25,8 +25,23 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "fuzzer_common.hpp"
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// the round-trip checks below are assertions; NDEBUG would compile them away
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#error "the fuzzer drivers must be built without NDEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
||||||
|
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
||||||
|
static bool same_value(const json& lhs, const json& rhs)
|
||||||
|
{
|
||||||
|
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -57,9 +72,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
|||||||
|
|
||||||
std::vector<uint8_t> const vec1(data, data + size);
|
std::vector<uint8_t> const vec1(data, data + size);
|
||||||
|
|
||||||
// step 0: parse input without exceptions
|
// step 0: parse input without exceptions; a parse error must then be
|
||||||
|
// reported as a discarded value, never thrown
|
||||||
|
json j_noexcept;
|
||||||
bool noexcept_threw = false;
|
bool noexcept_threw = false;
|
||||||
json const j_noexcept = parse_without_exceptions([&] { return json::from_bon8(vec1, true, false); }, noexcept_threw);
|
try
|
||||||
|
{
|
||||||
|
j_noexcept = json::from_bon8(vec1, true, false);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch (const json::exception&)
|
||||||
|
{
|
||||||
|
// type and out-of-range errors are not parse errors and still throw
|
||||||
|
noexcept_threw = true;
|
||||||
|
}
|
||||||
// whether step 1 succeeded; if not, the catch blocks below check that
|
// whether step 1 succeeded; if not, the catch blocks below check that
|
||||||
// step 0 failed, too
|
// step 0 failed, too
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|||||||
@@ -21,16 +21,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fuzzer_common.hpp"
|
#include <cassert>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// the round-trip checks below are assertions; NDEBUG would compile them away
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#error "the fuzzer drivers must be built without NDEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
||||||
|
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
||||||
|
static bool same_value(const json& lhs, const json& rhs)
|
||||||
|
{
|
||||||
|
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
}
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const vec1(data, data + size);
|
std::vector<uint8_t> const vec1(data, data + size);
|
||||||
|
|
||||||
// step 0: parse input without exceptions
|
// step 0: parse input without exceptions; a parse error must then be
|
||||||
|
// reported as a discarded value, never thrown
|
||||||
|
json j_noexcept;
|
||||||
bool noexcept_threw = false;
|
bool noexcept_threw = false;
|
||||||
json const j_noexcept = parse_without_exceptions([&] { return json::from_bson(vec1, true, false); }, noexcept_threw);
|
try
|
||||||
|
{
|
||||||
|
j_noexcept = json::from_bson(vec1, true, false);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch (const json::exception&)
|
||||||
|
{
|
||||||
|
// type and out-of-range errors are not parse errors and still throw
|
||||||
|
noexcept_threw = true;
|
||||||
|
}
|
||||||
// whether step 1 succeeded; if not, the catch blocks below check that
|
// whether step 1 succeeded; if not, the catch blocks below check that
|
||||||
// step 0 failed, too
|
// step 0 failed, too
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|||||||
@@ -21,16 +21,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fuzzer_common.hpp"
|
#include <cassert>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// the round-trip checks below are assertions; NDEBUG would compile them away
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#error "the fuzzer drivers must be built without NDEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
||||||
|
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
||||||
|
static bool same_value(const json& lhs, const json& rhs)
|
||||||
|
{
|
||||||
|
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
}
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const vec1(data, data + size);
|
std::vector<uint8_t> const vec1(data, data + size);
|
||||||
|
|
||||||
// step 0: parse input without exceptions
|
// step 0: parse input without exceptions; a parse error must then be
|
||||||
|
// reported as a discarded value, never thrown
|
||||||
|
json j_noexcept;
|
||||||
bool noexcept_threw = false;
|
bool noexcept_threw = false;
|
||||||
json const j_noexcept = parse_without_exceptions([&] { return json::from_cbor(vec1, true, false); }, noexcept_threw);
|
try
|
||||||
|
{
|
||||||
|
j_noexcept = json::from_cbor(vec1, true, false);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch (const json::exception&)
|
||||||
|
{
|
||||||
|
// type and out-of-range errors are not parse errors and still throw
|
||||||
|
noexcept_threw = true;
|
||||||
|
}
|
||||||
// whether step 1 succeeded; if not, the catch blocks below check that
|
// whether step 1 succeeded; if not, the catch blocks below check that
|
||||||
// step 0 failed, too
|
// step 0 failed, too
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|||||||
@@ -22,14 +22,43 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fuzzer_common.hpp"
|
#include <cassert>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// the round-trip checks below are assertions; NDEBUG would compile them away
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#error "the fuzzer drivers must be built without NDEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
||||||
|
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
||||||
|
static bool same_value(const json& lhs, const json& rhs)
|
||||||
|
{
|
||||||
|
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
}
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
// step 0: parse input without exceptions
|
// step 0: parse input without exceptions; a parse error must then be
|
||||||
|
// reported as a discarded value, never thrown
|
||||||
|
json j_noexcept;
|
||||||
bool noexcept_threw = false;
|
bool noexcept_threw = false;
|
||||||
json const j_noexcept = parse_without_exceptions([&] { return json::parse(data, data + size, nullptr, false); }, noexcept_threw);
|
try
|
||||||
|
{
|
||||||
|
j_noexcept = json::parse(data, data + size, nullptr, false);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch (const json::exception&)
|
||||||
|
{
|
||||||
|
// type and out-of-range errors are not parse errors and still throw
|
||||||
|
noexcept_threw = true;
|
||||||
|
}
|
||||||
// whether step 1 succeeded; if not, the catch blocks below check that
|
// whether step 1 succeeded; if not, the catch blocks below check that
|
||||||
// step 0 failed, too
|
// step 0 failed, too
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|||||||
@@ -21,16 +21,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fuzzer_common.hpp"
|
#include <cassert>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// the round-trip checks below are assertions; NDEBUG would compile them away
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#error "the fuzzer drivers must be built without NDEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
||||||
|
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
||||||
|
static bool same_value(const json& lhs, const json& rhs)
|
||||||
|
{
|
||||||
|
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
}
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const vec1(data, data + size);
|
std::vector<uint8_t> const vec1(data, data + size);
|
||||||
|
|
||||||
// step 0: parse input without exceptions
|
// step 0: parse input without exceptions; a parse error must then be
|
||||||
|
// reported as a discarded value, never thrown
|
||||||
|
json j_noexcept;
|
||||||
bool noexcept_threw = false;
|
bool noexcept_threw = false;
|
||||||
json const j_noexcept = parse_without_exceptions([&] { return json::from_msgpack(vec1, true, false); }, noexcept_threw);
|
try
|
||||||
|
{
|
||||||
|
j_noexcept = json::from_msgpack(vec1, true, false);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch (const json::exception&)
|
||||||
|
{
|
||||||
|
// type and out-of-range errors are not parse errors and still throw
|
||||||
|
noexcept_threw = true;
|
||||||
|
}
|
||||||
// whether step 1 succeeded; if not, the catch blocks below check that
|
// whether step 1 succeeded; if not, the catch blocks below check that
|
||||||
// step 0 failed, too
|
// step 0 failed, too
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|||||||
@@ -30,16 +30,45 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fuzzer_common.hpp"
|
#include <cassert>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// the round-trip checks below are assertions; NDEBUG would compile them away
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#error "the fuzzer drivers must be built without NDEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
||||||
|
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
||||||
|
static bool same_value(const json& lhs, const json& rhs)
|
||||||
|
{
|
||||||
|
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
}
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const vec1(data, data + size);
|
std::vector<uint8_t> const vec1(data, data + size);
|
||||||
|
|
||||||
// step 0: parse input without exceptions
|
// step 0: parse input without exceptions; a parse error must then be
|
||||||
|
// reported as a discarded value, never thrown
|
||||||
|
json j_noexcept;
|
||||||
bool noexcept_threw = false;
|
bool noexcept_threw = false;
|
||||||
json const j_noexcept = parse_without_exceptions([&] { return json::from_ubjson(vec1, true, false); }, noexcept_threw);
|
try
|
||||||
|
{
|
||||||
|
j_noexcept = json::from_ubjson(vec1, true, false);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch (const json::exception&)
|
||||||
|
{
|
||||||
|
// type and out-of-range errors are not parse errors and still throw
|
||||||
|
noexcept_threw = true;
|
||||||
|
}
|
||||||
// whether step 1 succeeded; if not, the catch blocks below check that
|
// whether step 1 succeeded; if not, the catch blocks below check that
|
||||||
// step 0 failed, too
|
// step 0 failed, too
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
// code shared by the fuzzer drivers tests/src/fuzzer-parse_*.cpp
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert> // assert
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using nlohmann::json; // NOLINT(google-global-names-in-headers): shared by all fuzzer drivers
|
|
||||||
|
|
||||||
// the round-trip checks of the drivers are assertions; NDEBUG would compile them away
|
|
||||||
#ifdef NDEBUG
|
|
||||||
#error "the fuzzer drivers must be built without NDEBUG"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// compares dumps rather than values, because NaN != NaN; keep writes strings
|
|
||||||
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
|
|
||||||
inline bool same_value(const json& lhs, const json& rhs)
|
|
||||||
{
|
|
||||||
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
}
|
|
||||||
|
|
||||||
// step 0 of each driver: parse the input without exceptions; a parse error
|
|
||||||
// must then be reported as a discarded value, never thrown. Type and
|
|
||||||
// out-of-range errors are not parse errors and still throw; then @a threw is
|
|
||||||
// set and null is returned.
|
|
||||||
template<typename Parse>
|
|
||||||
json parse_without_exceptions(Parse parse, bool& threw)
|
|
||||||
{
|
|
||||||
threw = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return parse();
|
|
||||||
}
|
|
||||||
catch (const json::parse_error&)
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
catch (const json::exception&)
|
|
||||||
{
|
|
||||||
threw = true;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <cstdint> // uint8_t (via json::binary_t)
|
|
||||||
#include <limits> // numeric_limits
|
|
||||||
#include <string> // string, to_string
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
// the loggers are defined in this header only, so their vtables are emitted in
|
|
||||||
// every test that includes it
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wweak-vtables"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
/// a SAX event consumer that records every event it receives as a
|
|
||||||
/// human-readable string, used by the deserialization tests to check the
|
|
||||||
/// exact sequence of SAX events a parse run produces
|
|
||||||
struct SaxEventLogger : public nlohmann::json_sax<nlohmann::json>
|
|
||||||
{
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
bool null() override
|
|
||||||
{
|
|
||||||
events.emplace_back("null()");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool boolean(bool val) override
|
|
||||||
{
|
|
||||||
events.emplace_back(val ? "boolean(true)" : "boolean(false)");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_integer(json::number_integer_t val) override
|
|
||||||
{
|
|
||||||
events.push_back("number_integer(" + std::to_string(val) + ")");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_unsigned(json::number_unsigned_t val) override
|
|
||||||
{
|
|
||||||
events.push_back("number_unsigned(" + std::to_string(val) + ")");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_float(json::number_float_t /*val*/, const std::string& s) override
|
|
||||||
{
|
|
||||||
events.push_back("number_float(" + s + ")");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string(std::string& val) override
|
|
||||||
{
|
|
||||||
events.push_back("string(" + val + ")");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool binary(json::binary_t& val) override
|
|
||||||
{
|
|
||||||
std::string binary_contents = "binary(";
|
|
||||||
std::string comma_space;
|
|
||||||
for (const auto b : val)
|
|
||||||
{
|
|
||||||
binary_contents.append(comma_space);
|
|
||||||
binary_contents.append(std::to_string(static_cast<int>(b)));
|
|
||||||
comma_space = ", ";
|
|
||||||
}
|
|
||||||
binary_contents.append(")");
|
|
||||||
events.push_back(binary_contents);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_object(std::size_t elements) override
|
|
||||||
{
|
|
||||||
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
||||||
{
|
|
||||||
events.emplace_back("start_object()");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
events.push_back("start_object(" + std::to_string(elements) + ")");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool key(std::string& val) override
|
|
||||||
{
|
|
||||||
events.push_back("key(" + val + ")");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_object() override
|
|
||||||
{
|
|
||||||
events.emplace_back("end_object()");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_array(std::size_t elements) override
|
|
||||||
{
|
|
||||||
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
||||||
{
|
|
||||||
events.emplace_back("start_array()");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
events.push_back("start_array(" + std::to_string(elements) + ")");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_array() override
|
|
||||||
{
|
|
||||||
events.emplace_back("end_array()");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_error(std::size_t position, const std::string& /*last_token*/, const json::exception& /*ex*/) override
|
|
||||||
{
|
|
||||||
errored = true;
|
|
||||||
events.push_back("parse_error(" + std::to_string(position) + ")");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
|
|
||||||
bool errored = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger
|
|
||||||
{
|
|
||||||
bool start_object(std::size_t elements) override
|
|
||||||
{
|
|
||||||
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
||||||
{
|
|
||||||
events.emplace_back("start_object()");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
events.push_back("start_object(" + std::to_string(elements) + ")");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SaxEventLoggerExitAfterKey : public SaxEventLogger
|
|
||||||
{
|
|
||||||
bool key(std::string& val) override
|
|
||||||
{
|
|
||||||
events.push_back("key(" + val + ")");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger
|
|
||||||
{
|
|
||||||
bool start_array(std::size_t elements) override
|
|
||||||
{
|
|
||||||
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
||||||
{
|
|
||||||
events.emplace_back("start_array()");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
events.push_back("start_array(" + std::to_string(elements) + ")");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace utils
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
+554
-107
@@ -13,16 +13,20 @@
|
|||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cfloat> // FLT_EVAL_METHOD
|
|
||||||
#include <cstdint> // uint32_t, uint64_t
|
#include <cstdint> // uint32_t, uint64_t
|
||||||
|
#include <cstdio> // snprintf
|
||||||
#include <cstdlib> // strtod
|
#include <cstdlib> // strtod
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
|
#include <map> // map
|
||||||
|
#include <random> // mt19937
|
||||||
#include <sstream> // stringstream
|
#include <sstream> // stringstream
|
||||||
#include <string> // string
|
#include <string> // string
|
||||||
#include <utility> // pair
|
#include <utility> // pair
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
|
#include "float_hard_cases.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// shortcut to scan a string literal
|
// shortcut to scan a string literal
|
||||||
@@ -258,7 +262,7 @@ TEST_CASE("lexer number fast path")
|
|||||||
"123456789012345678901234567890", // huge -> float
|
"123456789012345678901234567890", // huge -> float
|
||||||
"0.30000000000000004", "2.2250738585072014e-308", "1e308",
|
"0.30000000000000004", "2.2250738585072014e-308", "1e308",
|
||||||
// high-precision / wide-exponent values that exercise the
|
// high-precision / wide-exponent values that exercise the
|
||||||
// std::from_chars (Eisel-Lemire) path beyond the Clinger subset
|
// Eisel-Lemire path beyond the Clinger subset
|
||||||
"1.7976931348623157e308", "1.2345678901234567e-250",
|
"1.7976931348623157e308", "1.2345678901234567e-250",
|
||||||
"9007199254740993", "5e-324", "1e-320"
|
"9007199254740993", "5e-324", "1e-320"
|
||||||
};
|
};
|
||||||
@@ -280,20 +284,18 @@ TEST_CASE("lexer number fast path")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("significant-digit gate for the Clinger fast path")
|
SECTION("significant digits around Clinger's fast path")
|
||||||
{
|
{
|
||||||
// Clinger's fast path needs a significand below 2^53, so it cannot
|
// Clinger's fast path needs a significand of at most 2^53, which
|
||||||
// succeed once the mantissa has 17 or more significant digits (the
|
// tokens with 17 or more significant digits exceed. The conversion
|
||||||
// significand would be at least 10^16). The lexer skips the attempt
|
// splits the token at the positions the scanners recorded, so leading
|
||||||
// there. That is only allowed to save work: every value must still come
|
// zeros must not count as digits - "0.1234567890123456" has 16
|
||||||
// out bit-exactly, and both scanners must agree. In particular the gate
|
// significant digits, not 17 - and both scanners must agree.
|
||||||
// must not fire for tokens whose leading zeros merely look like extra
|
|
||||||
// digits - "0.1234567890123456" has 16 significant digits, not 17.
|
|
||||||
const std::vector<std::string> numbers =
|
const std::vector<std::string> numbers =
|
||||||
{
|
{
|
||||||
"1234567890123456", // 16 significant digits
|
"1234567890123456", // 16 significant digits
|
||||||
"12345678901234567", // 17 -> attempt skipped
|
"12345678901234567", // 17
|
||||||
"123456789012345678", // 18 -> attempt skipped
|
"123456789012345678", // 18
|
||||||
"0.1234567890123456", // 16: the leading "0" is not significant
|
"0.1234567890123456", // 16: the leading "0" is not significant
|
||||||
"0.12345678901234567", // 17
|
"0.12345678901234567", // 17
|
||||||
"0.00000000000000001", // 1, in a long token
|
"0.00000000000000001", // 1, in a long token
|
||||||
@@ -664,46 +666,323 @@ TEST_CASE("lexer string fast path")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("parse_float_fast declines what it cannot convert exactly")
|
TEST_CASE("lexer escape fast path")
|
||||||
{
|
{
|
||||||
// The lexer only hands well-formed numbers to parse_float_fast, so the
|
// json::accept() never throws, so this section stays covered without
|
||||||
// malformed ones below can only be passed to it directly. Declining is
|
// exceptions; it pins which of the cases below are valid/invalid and
|
||||||
// always safe: the caller then falls back to a slower, exact conversion.
|
// checks the contiguous and streaming paths agree on that classification.
|
||||||
const auto fast = [](const std::string & s, double & out)
|
SECTION("accept() parity")
|
||||||
{
|
{
|
||||||
return nlohmann::detail::parse_float_fast(s.data(), s.data() + s.size(), out);
|
const std::vector<std::pair<std::string, bool>> cases =
|
||||||
|
{
|
||||||
|
{"\\u0041", true}, {"\\u00e4", true}, {"\\u00E4", true},
|
||||||
|
{"\\uD83D\\uDE00", true},
|
||||||
|
{"\\u12", false}, {"\\u12G4", false}, {"\\uXYZW", false},
|
||||||
|
{"\\uD800", false}, {"\\uD800A", false}, {"\\uD800\\u0041", false},
|
||||||
|
{"\\uDC00", false}, {"\\u", false}
|
||||||
};
|
};
|
||||||
double out = 0;
|
|
||||||
|
|
||||||
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
|
for (const auto& c : cases)
|
||||||
// without true double precision, the fast path declines everything
|
{
|
||||||
CHECK_FALSE(fast("1.5", out));
|
for (const std::size_t offset :
|
||||||
#else
|
{
|
||||||
CHECK(fast("1.5", out));
|
std::size_t{0}, std::size_t{9}
|
||||||
CHECK(out == 1.5);
|
})
|
||||||
CHECK(fast("+2.5e1", out));
|
{
|
||||||
CHECK(out == 25.0);
|
const std::string doc = "[\"" + std::string(offset, 'a') + c.first + "\"]";
|
||||||
CHECK(fast("-25E-1", out));
|
CAPTURE(doc)
|
||||||
CHECK(out == -2.5);
|
CHECK(json::accept(doc) == c.second);
|
||||||
CHECK(fast("1e", out));
|
std::stringstream ss(doc);
|
||||||
CHECK(out == 1.0);
|
CHECK(json::accept(ss) == c.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
// the full outcome of parsing @a doc: the parsed value, or the exact
|
||||||
|
// error message, so a mismatch in either is caught
|
||||||
|
const auto outcome = [](const std::string & doc, bool streaming) -> std::string
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (streaming)
|
||||||
|
{
|
||||||
|
std::stringstream ss(doc);
|
||||||
|
const json j = json::parse(ss);
|
||||||
|
return j.dump();
|
||||||
|
}
|
||||||
|
const json j = json::parse(doc);
|
||||||
|
return j.dump();
|
||||||
|
}
|
||||||
|
catch (const json::exception& e)
|
||||||
|
{
|
||||||
|
return {e.what()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SECTION("contiguous vs streaming parity")
|
||||||
|
{
|
||||||
|
const std::vector<std::string> escapes =
|
||||||
|
{
|
||||||
|
"\\u0041", // "A"
|
||||||
|
"\\u00e4", // "ä" (lowercase hex)
|
||||||
|
"\\u00E4", // "ä" (uppercase hex)
|
||||||
|
"\\uD83D\\uDE00", // valid surrogate pair (an emoji)
|
||||||
|
"\\u12", // truncated: only 2 hex digits before the closing quote
|
||||||
|
"\\u12G4", // invalid hex digit at the 3rd position
|
||||||
|
"\\uXYZW", // all 4 bytes invalid
|
||||||
|
"\\uD800", // lone high surrogate, string ends right after
|
||||||
|
"\\uD800A", // high surrogate not followed by another \u escape
|
||||||
|
"\\uD800\\u0041", // high surrogate followed by \u, but not a low surrogate
|
||||||
|
"\\uDC00", // lone low surrogate
|
||||||
|
"\\u", // '\u' with nothing after (closing quote right away)
|
||||||
|
};
|
||||||
|
|
||||||
|
// once at the start of the string and once past the first 8-byte SWAR
|
||||||
|
// word of the outer string_bulk_run, so the escape is reached both
|
||||||
|
// right after the opening quote and mid-run
|
||||||
|
for (const auto& escape : escapes)
|
||||||
|
{
|
||||||
|
for (const std::size_t offset :
|
||||||
|
{
|
||||||
|
std::size_t{0}, std::size_t{9}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
const std::string doc = "[\"" + std::string(offset, 'a') + escape + "\"]";
|
||||||
|
CAPTURE(doc)
|
||||||
|
CHECK(outcome(doc, false) == outcome(doc, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// the escape is the last thing before end of input: no closing
|
||||||
|
// quote at all
|
||||||
|
const std::string truncated_doc = "[\"" + escape;
|
||||||
|
CAPTURE(truncated_doc)
|
||||||
|
CHECK(outcome(truncated_doc, false) == outcome(truncated_doc, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("truncated \\u escape at every distance from the end of input")
|
||||||
|
{
|
||||||
|
// ia.bulk_remaining() must correctly report fewer than 4 bytes for
|
||||||
|
// every possible count of trailing hex-looking bytes (0, 1, 2, or 3)
|
||||||
|
// before end of input, so the fast path declines and the byte path
|
||||||
|
// alone reports the "must be followed by 4 hex digits" error, at the
|
||||||
|
// same position, in every case
|
||||||
|
for (const std::string& tail :
|
||||||
|
{
|
||||||
|
std::string{}, std::string("1"), std::string("12"), std::string("123")
|
||||||
|
})
|
||||||
|
{
|
||||||
|
const std::string doc = "[\"\\u" + tail;
|
||||||
|
CAPTURE(doc)
|
||||||
|
CHECK(outcome(doc, false) == outcome(doc, true));
|
||||||
|
CHECK(outcome(doc, false).find("must be followed by 4 hex digits") != std::string::npos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("invalid hex digit at every position of the 4")
|
||||||
|
{
|
||||||
|
// the fast path must decline for *any* invalid byte among the 4, not
|
||||||
|
// just the first, and the byte path must then stop at exactly that
|
||||||
|
// position - same as it always has
|
||||||
|
for (std::size_t bad_pos = 0; bad_pos < 4; ++bad_pos)
|
||||||
|
{
|
||||||
|
std::string digits = "1234";
|
||||||
|
digits[bad_pos] = 'g'; // not a hex digit
|
||||||
|
const std::string doc = "[\"\\u" + digits + "\"]";
|
||||||
|
CAPTURE(doc)
|
||||||
|
CHECK(outcome(doc, false) == outcome(doc, true));
|
||||||
|
CHECK(outcome(doc, false).find("must be followed by 4 hex digits") != std::string::npos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("random escapes")
|
||||||
|
{
|
||||||
|
// A seeded PRNG builds the 4 bytes following `\u` from a mix of hex
|
||||||
|
// digits and non-hex bytes, at varying distances from the start of
|
||||||
|
// the string, to compare the two scanners on many more shapes than
|
||||||
|
// are practical to enumerate by hand.
|
||||||
|
std::mt19937 gen(7654321); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
|
||||||
|
const std::string hex_alphabet = "0123456789AaBbCcDdEeFf";
|
||||||
|
std::uniform_int_distribution<std::size_t> pick_hex(0, hex_alphabet.size() - 1);
|
||||||
|
std::uniform_int_distribution<int> pick_byte(1, 255); // never NUL
|
||||||
|
std::uniform_int_distribution<int> pick_is_hex(0, 4); // 4-in-5 chance of a hex digit
|
||||||
|
std::uniform_int_distribution<std::size_t> pick_offset(0, 12);
|
||||||
|
|
||||||
|
std::vector<std::string> mismatches;
|
||||||
|
for (int iter = 0; iter < 3000; ++iter)
|
||||||
|
{
|
||||||
|
std::string digits;
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (pick_is_hex(gen) != 0)
|
||||||
|
{
|
||||||
|
digits += hex_alphabet[pick_hex(gen)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char c = static_cast<char>(pick_byte(gen));
|
||||||
|
if (c == '"' || c == '\\')
|
||||||
|
{
|
||||||
|
// keep the string well-formed apart from the escape
|
||||||
|
// itself, so any mismatch is attributable to the \u
|
||||||
|
// handling and not to an unrelated quote/escape
|
||||||
|
c = 'z';
|
||||||
|
}
|
||||||
|
digits += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const std::string doc = "[\"" + std::string(pick_offset(gen), 'a') + "\\u" + digits + "\"]";
|
||||||
|
if (outcome(doc, false) != outcome(doc, true))
|
||||||
|
{
|
||||||
|
mismatches.push_back(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CAPTURE(mismatches)
|
||||||
|
CHECK(mismatches.empty());
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// not a number
|
namespace
|
||||||
CHECK_FALSE(fast("", out));
|
{
|
||||||
CHECK_FALSE(fast("-", out));
|
// the index of the decimal point (or npos) and of the end of the mantissa of a
|
||||||
CHECK_FALSE(fast(".", out));
|
// number token, which the lexer records while scanning it
|
||||||
CHECK_FALSE(fast("1.2.3", out));
|
std::pair<std::size_t, std::size_t> float_token_layout(const std::string& s)
|
||||||
CHECK_FALSE(fast("1x", out));
|
{
|
||||||
CHECK_FALSE(fast("1e+", out));
|
std::size_t dot = std::string::npos;
|
||||||
CHECK_FALSE(fast("1e1x", out));
|
std::size_t mantissa_end = s.size();
|
||||||
|
for (std::size_t i = 0; i < s.size(); ++i)
|
||||||
|
{
|
||||||
|
if (s[i] == '.')
|
||||||
|
{
|
||||||
|
dot = i;
|
||||||
|
}
|
||||||
|
else if (s[i] == 'e' || s[i] == 'E')
|
||||||
|
{
|
||||||
|
mantissa_end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {dot, mantissa_end};
|
||||||
|
}
|
||||||
|
|
||||||
// numbers that are not represented exactly on the fast path
|
template<typename FloatType>
|
||||||
CHECK_FALSE(fast("12345678901234567890", out));
|
FloatType parse_native(const std::string& s)
|
||||||
CHECK_FALSE(fast("1e10000", out));
|
{
|
||||||
CHECK_FALSE(fast("9007199254740993", out));
|
const auto layout = float_token_layout(s);
|
||||||
CHECK_FALSE(fast("1e23", out));
|
return nlohmann::detail::parse_float_native<FloatType>(s.data(), s.data() + s.size(), layout.first, layout.second);
|
||||||
CHECK_FALSE(fast("1e-23", out));
|
}
|
||||||
|
|
||||||
|
std::uint64_t bits_of(double d)
|
||||||
|
{
|
||||||
|
std::uint64_t b = 0;
|
||||||
|
std::memcpy(&b, &d, sizeof(b));
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t bits_of(float f)
|
||||||
|
{
|
||||||
|
std::uint32_t b = 0;
|
||||||
|
std::memcpy(&b, &f, sizeof(b));
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t native_bits64(const std::string& s)
|
||||||
|
{
|
||||||
|
return bits_of(parse_native<double>(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t native_bits32(const std::string& s)
|
||||||
|
{
|
||||||
|
return bits_of(parse_native<float>(s));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("parse_float_native rounds correctly")
|
||||||
|
{
|
||||||
|
SECTION("double")
|
||||||
|
{
|
||||||
|
CHECK(native_bits64("1.5") == 0x3FF8000000000000u);
|
||||||
|
CHECK(native_bits64("0.1") == 0x3FB999999999999Au);
|
||||||
|
CHECK(native_bits64("-0.0") == 0x8000000000000000u);
|
||||||
|
CHECK(native_bits64("0e999999999999999999999") == 0u);
|
||||||
|
// 2^53 + 1 is exactly between two doubles: ties to even, unless more digits follow
|
||||||
|
CHECK(native_bits64("9007199254740993") == 0x4340000000000000u);
|
||||||
|
CHECK(native_bits64("9007199254740993.0000000000000000001") == 0x4340000000000001u);
|
||||||
|
CHECK(native_bits64("9007199254740992.9999999999999999999") == 0x4340000000000000u);
|
||||||
|
// 1 + 2^-53 exactly (a tie), and one unit in the 55th digit around it
|
||||||
|
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203125") == 0x3FF0000000000000u);
|
||||||
|
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203126") == 0x3FF0000000000001u);
|
||||||
|
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203124") == 0x3FF0000000000000u);
|
||||||
|
// subnormal and overflow boundaries
|
||||||
|
CHECK(native_bits64("2.4703282292062327e-324") == 0u);
|
||||||
|
CHECK(native_bits64("2.4703282292062328e-324") == 1u);
|
||||||
|
CHECK(native_bits64("2.2250738585072011e-308") == 0x000FFFFFFFFFFFFFu);
|
||||||
|
CHECK(native_bits64("2.2250738585072012e-308") == 0x0010000000000000u);
|
||||||
|
CHECK(native_bits64("1.7976931348623157e308") == 0x7FEFFFFFFFFFFFFFu);
|
||||||
|
CHECK(native_bits64("1.7976931348623159e308") == 0x7FF0000000000000u);
|
||||||
|
CHECK(native_bits64("-1e400") == 0xFFF0000000000000u);
|
||||||
|
CHECK(native_bits64("-1e-400") == 0x8000000000000000u);
|
||||||
|
// exponents and zeros far beyond the range cancel out
|
||||||
|
CHECK(native_bits64("0." + std::string(1000, '0') + "1e1001") == 0x3FF0000000000000u);
|
||||||
|
CHECK(native_bits64("1" + std::string(1000, '0') + "e-1000") == 0x3FF0000000000000u);
|
||||||
|
CHECK(native_bits64("1e-99999999999999999999999") == 0u);
|
||||||
|
CHECK(native_bits64("1E+99999999999999999999999") == 0x7FF0000000000000u);
|
||||||
|
// more digits than any midpoint has (769): only whether a nonzero digit follows matters
|
||||||
|
const std::string tie = "1.00000000000000011102230246251565404236316680908203125";
|
||||||
|
CHECK(native_bits64(tie + std::string(800, '0')) == 0x3FF0000000000000u);
|
||||||
|
CHECK(native_bits64(tie + std::string(800, '0') + "1") == 0x3FF0000000000001u);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("float")
|
||||||
|
{
|
||||||
|
CHECK(native_bits32("1.5") == 0x3FC00000u);
|
||||||
|
CHECK(native_bits32("0.1") == 0x3DCCCCCDu);
|
||||||
|
CHECK(native_bits32("-0.0") == 0x80000000u);
|
||||||
|
// 2^24 + 1 is exactly between two floats
|
||||||
|
CHECK(native_bits32("16777217") == 0x4B800000u);
|
||||||
|
CHECK(native_bits32("16777217.000000000000000000001") == 0x4B800001u);
|
||||||
|
CHECK(native_bits32("16777218.999999999999999999999") == 0x4B800001u);
|
||||||
|
CHECK(native_bits32("16777219") == 0x4B800002u);
|
||||||
|
// subnormal and overflow boundaries
|
||||||
|
CHECK(native_bits32("3.4028235677973366e38") == 0x7F7FFFFFu);
|
||||||
|
CHECK(native_bits32("3.4028235677973367e38") == 0x7F800000u);
|
||||||
|
CHECK(native_bits32("7.006492321624085e-46") == 0u);
|
||||||
|
CHECK(native_bits32("7.006492321624086e-46") == 1u);
|
||||||
|
CHECK(native_bits32("1.1754942e-38") == 0x007FFFFFu);
|
||||||
|
CHECK(native_bits32("-1.17549435e-38") == 0x80800000u);
|
||||||
|
CHECK(native_bits32("1e39") == 0x7F800000u);
|
||||||
|
CHECK(native_bits32("-1e-50") == 0x80000000u);
|
||||||
|
// not rounded through double: its double would round to another float
|
||||||
|
CHECK(native_bits32("1.00000005960464477539062500000000001") == 0x3F800001u);
|
||||||
|
CHECK(native_bits32("9007199254740993") == 0x5A000000u);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("the conversion shared with other parsers")
|
||||||
|
{
|
||||||
|
// convert_float() gives the lexer's results, for every type
|
||||||
|
const std::vector<std::string> tokens =
|
||||||
|
{
|
||||||
|
"0", "-0.0", "1.5", "0.1", "1e-400", "-2.5E+3", "123456789012345678901234567890",
|
||||||
|
"9007199254740993.0000000000000000001", "4.9406564584124654e-324"
|
||||||
|
};
|
||||||
|
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||||
|
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||||
|
for (const auto& t : tokens)
|
||||||
|
{
|
||||||
|
CAPTURE(t)
|
||||||
|
const auto layout = float_token_layout(t);
|
||||||
|
const char* const first = t.data();
|
||||||
|
const char* const last = first + t.size();
|
||||||
|
const auto d = nlohmann::detail::convert_float<double>(first, last, layout.first, layout.second);
|
||||||
|
const auto f = nlohmann::detail::convert_float<float>(first, last, layout.first, layout.second);
|
||||||
|
const auto ld = nlohmann::detail::convert_float<long double>(first, last, layout.first, layout.second);
|
||||||
|
CHECK(bits_of(d) == bits_of(json::parse(t).get<double>()));
|
||||||
|
CHECK(bits_of(f) == bits_of(float_json::parse(t).get<float>()));
|
||||||
|
CHECK(ld == long_double_json::parse(t).get<long double>());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -807,40 +1086,6 @@ std::size_t big_bit_length(const big_uint& a)
|
|||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t bits_of(double d)
|
|
||||||
{
|
|
||||||
std::uint64_t b = 0;
|
|
||||||
std::memcpy(&b, &d, sizeof(b));
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool eisel_lemire(const std::string& s, double& out)
|
|
||||||
{
|
|
||||||
return nlohmann::detail::parse_float_eisel_lemire(s.data(), s.data() + s.size(), out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// significant digits of a token, without trailing zeros
|
|
||||||
std::size_t significant_digits(const std::string& s)
|
|
||||||
{
|
|
||||||
std::string digits;
|
|
||||||
for (const char c : s)
|
|
||||||
{
|
|
||||||
if (c == 'e' || c == 'E')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (c >= '0' && c <= '9' && !(digits.empty() && c == '0'))
|
|
||||||
{
|
|
||||||
digits += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (!digits.empty() && digits.back() == '0')
|
|
||||||
{
|
|
||||||
digits.pop_back();
|
|
||||||
}
|
|
||||||
return digits.size();
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("Eisel-Lemire float conversion")
|
TEST_CASE("Eisel-Lemire float conversion")
|
||||||
@@ -1268,26 +1513,33 @@ TEST_CASE("Eisel-Lemire float conversion")
|
|||||||
for (const auto& c : known)
|
for (const auto& c : known)
|
||||||
{
|
{
|
||||||
CAPTURE(c.first)
|
CAPTURE(c.first)
|
||||||
double out = 0;
|
CHECK(native_bits64(c.first) == c.second);
|
||||||
if (eisel_lemire(c.first, out))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("binary32")
|
||||||
{
|
{
|
||||||
CHECK(bits_of(out) == c.second);
|
using binary32 = nlohmann::detail::ieee_binary_format<24>;
|
||||||
}
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 1) == 0x3F800000u);
|
||||||
else
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 1) == 0x3DCCCCCDu);
|
||||||
{
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 15) == 0x3FC00000u);
|
||||||
// only tokens with more than 19 significant digits are left to
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777217) == 0x4B800000u); // tie, to even
|
||||||
// strtod: those whose value lies too close to a tie
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777219) == 0x4B800002u); // tie, to even
|
||||||
CHECK(significant_digits(c.first) > 19);
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(-45, 1) == 0x00000001u);
|
||||||
}
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(-46, 7) == 0x00000000u);
|
||||||
}
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(-46, 8) == 0x00000001u);
|
||||||
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(-65, 9999999999999999999u) == 0x00000000u);
|
||||||
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(20, 3402823466385288598u) == 0x7F7FFFFFu);
|
||||||
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(20, 3402823669209384635u) == 0x7F800000u);
|
||||||
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(39, 1) == 0x7F800000u);
|
||||||
|
CHECK(nlohmann::detail::eisel_lemire<binary32>(-5, 0) == 0x00000000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("round trip")
|
SECTION("round trip")
|
||||||
{
|
{
|
||||||
// every double written by to_chars and read back, also with trailing
|
// every double written by to_chars and read back, and its 17-digit
|
||||||
// digits that make the token longer than 19 digits
|
// form with trailing digits that make the token longer than 19 digits
|
||||||
std::uint64_t state = 5295;
|
std::uint64_t state = 5295;
|
||||||
std::size_t declined = 0;
|
|
||||||
for (int i = 0; i < 200000; ++i)
|
for (int i = 0; i < 200000; ++i)
|
||||||
{
|
{
|
||||||
state ^= state << 13u;
|
state ^= state << 13u;
|
||||||
@@ -1309,30 +1561,51 @@ TEST_CASE("Eisel-Lemire float conversion")
|
|||||||
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), d);
|
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), d);
|
||||||
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
||||||
CAPTURE(token)
|
CAPTURE(token)
|
||||||
double out = 0;
|
CHECK(native_bits64(token) == b);
|
||||||
REQUIRE(eisel_lemire(token, out));
|
|
||||||
CHECK(bits_of(out) == b);
|
|
||||||
|
|
||||||
// insert digits before the exponent: the value moves by far less
|
// insert digits before the exponent of the 17-digit form: that
|
||||||
// than the distance to the rounding boundary, so it must not change
|
// form lies strictly inside the rounding interval of the double
|
||||||
std::string longer = token;
|
// (the shortest one may lie on its boundary), and the digits move
|
||||||
|
// it by far less than the distance to the boundary, so the value
|
||||||
|
// must not change
|
||||||
|
std::array<char, 64> digits17{};
|
||||||
|
static_cast<void>(std::snprintf(digits17.data(), digits17.size(), "%.17g", d)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
std::string longer = digits17.data();
|
||||||
const std::size_t e = longer.find('e');
|
const std::size_t e = longer.find('e');
|
||||||
const std::size_t dot = longer.find('.');
|
const std::size_t dot = longer.find('.');
|
||||||
const std::string extra = dot == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
|
const std::string extra = dot == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
|
||||||
longer.insert(e == std::string::npos ? longer.size() : e, extra);
|
longer.insert(e == std::string::npos ? longer.size() : e, extra);
|
||||||
CAPTURE(longer)
|
CAPTURE(longer)
|
||||||
if (eisel_lemire(longer, out))
|
CHECK(native_bits64(longer) == b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("round trip, binary32")
|
||||||
{
|
{
|
||||||
CHECK(bits_of(out) == b);
|
std::uint32_t state = 5295;
|
||||||
}
|
for (int i = 0; i < 100000; ++i)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// w and w + 1 round differently: only when the value is very
|
state ^= state << 13u;
|
||||||
// close to a rounding boundary
|
state ^= state >> 17u;
|
||||||
++declined;
|
state ^= state << 5u;
|
||||||
|
std::uint32_t b = state;
|
||||||
|
if ((b & 0x7F800000u) == 0x7F800000u)
|
||||||
|
{
|
||||||
|
continue; // infinity or NaN
|
||||||
}
|
}
|
||||||
|
if (i % 4 == 0)
|
||||||
|
{
|
||||||
|
b &= 0x807FFFFFu; // subnormals
|
||||||
|
}
|
||||||
|
float f = 0;
|
||||||
|
std::memcpy(&f, &b, sizeof(f));
|
||||||
|
|
||||||
|
std::array<char, 64> buffer{};
|
||||||
|
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), f);
|
||||||
|
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
||||||
|
CAPTURE(token)
|
||||||
|
CHECK(native_bits32(token) == b);
|
||||||
}
|
}
|
||||||
CHECK(declined < 1000); // 107 of the 200,000
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("used by the lexer")
|
SECTION("used by the lexer")
|
||||||
@@ -1346,3 +1619,177 @@ TEST_CASE("Eisel-Lemire float conversion")
|
|||||||
"[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&);
|
"[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||||
|
|
||||||
|
// the bits of the float that parse() gives for a token, via both scanners;
|
||||||
|
// the value must be the same for both
|
||||||
|
template<typename Json, typename Bits>
|
||||||
|
void check_parse(const std::string& token, Bits expected, Bits infinity)
|
||||||
|
{
|
||||||
|
std::stringstream stream(token);
|
||||||
|
if ((expected & ~(Bits{1} << ((8 * sizeof(Bits)) - 1))) == infinity)
|
||||||
|
{
|
||||||
|
Json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = Json::parse(token), ("[json.exception.out_of_range.406] number overflow parsing '" + token + "'").c_str(), typename Json::out_of_range&);
|
||||||
|
CHECK_THROWS_WITH_AS(_ = Json::parse(stream), ("[json.exception.out_of_range.406] number overflow parsing '" + token + "'").c_str(), typename Json::out_of_range&);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const Json contiguous = Json::parse(token);
|
||||||
|
const Json streamed = Json::parse(stream);
|
||||||
|
if (contiguous.is_number_float()) // not an integer that fits
|
||||||
|
{
|
||||||
|
CHECK(bits_of(contiguous.template get<typename Json::number_float_t>()) == expected);
|
||||||
|
CHECK(bits_of(streamed.template get<typename Json::number_float_t>()) == expected);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK(streamed.is_number_integer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("float conversion of hard cases")
|
||||||
|
{
|
||||||
|
// see float_hard_cases.hpp
|
||||||
|
for (const auto& c : float_hard_cases::cases())
|
||||||
|
{
|
||||||
|
const std::string token = c.token;
|
||||||
|
CAPTURE(token)
|
||||||
|
CHECK(native_bits64(token) == c.bits64);
|
||||||
|
CHECK(native_bits32(token) == c.bits32);
|
||||||
|
check_parse<json>(token, c.bits64, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<float_json>(token, c.bits32, std::uint32_t{0x7F800000u});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("float overflow and underflow in the parser")
|
||||||
|
{
|
||||||
|
SECTION("double")
|
||||||
|
{
|
||||||
|
check_parse<json>("1.7976931348623157e308", std::uint64_t{0x7FEFFFFFFFFFFFFFu}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<json>("1.7976931348623159e308", std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<json>("-1e309", std::uint64_t{0xFFF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<json>("1" + std::string(400, '0'), std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<json>("1e99999999999999999999", std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
// an underflow gives a zero with the sign of the token
|
||||||
|
check_parse<json>("1e-400", std::uint64_t{0}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<json>("-1e-400", std::uint64_t{0x8000000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<json>("-2.4703282292062327e-324", std::uint64_t{0x8000000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
check_parse<json>("0." + std::string(400, '0') + "1", std::uint64_t{0}, std::uint64_t{0x7FF0000000000000u});
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("float")
|
||||||
|
{
|
||||||
|
check_parse<float_json>("3.4028234e38", std::uint32_t{0x7F7FFFFFu}, std::uint32_t{0x7F800000u});
|
||||||
|
check_parse<float_json>("3.4028236e38", std::uint32_t{0x7F800000u}, std::uint32_t{0x7F800000u});
|
||||||
|
check_parse<float_json>("-1e39", std::uint32_t{0xFF800000u}, std::uint32_t{0x7F800000u});
|
||||||
|
check_parse<float_json>("1e-46", std::uint32_t{0}, std::uint32_t{0x7F800000u});
|
||||||
|
check_parse<float_json>("-1e-46", std::uint32_t{0x80000000u}, std::uint32_t{0x7F800000u});
|
||||||
|
check_parse<float_json>("-7.006492321624085e-46", std::uint32_t{0x80000000u}, std::uint32_t{0x7F800000u});
|
||||||
|
check_parse<float_json>("-7.006492321624086e-46", std::uint32_t{0x80000001u}, std::uint32_t{0x7F800000u});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("string scanning kernels")
|
||||||
|
{
|
||||||
|
// the word-at-a-time kernels must stop exactly where a byte-by-byte scan
|
||||||
|
// stops, for any content, length, and alignment
|
||||||
|
const auto reference_special = [](const unsigned char* data, std::size_t n)
|
||||||
|
{
|
||||||
|
std::size_t i = 0;
|
||||||
|
while (i < n && !nlohmann::detail::is_string_special(data[i]))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
const auto reference_copyable = [](const unsigned char* data, std::size_t n)
|
||||||
|
{
|
||||||
|
std::size_t i = 0;
|
||||||
|
while (i < n && nlohmann::detail::is_ascii_copyable(data[i]))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
const auto reference_bulk_run = [](const unsigned char* data, std::size_t n)
|
||||||
|
{
|
||||||
|
std::size_t i = 0;
|
||||||
|
while (i < n)
|
||||||
|
{
|
||||||
|
if (data[i] < 0x80u)
|
||||||
|
{
|
||||||
|
if (nlohmann::detail::is_string_special(data[i]))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::size_t seq = nlohmann::detail::validate_one_utf8(data + i, n - i);
|
||||||
|
if (seq == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += seq;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
// pieces: ordinary ASCII, stops, DEL, well-formed sequences of every
|
||||||
|
// length, and ill-formed or truncated ones
|
||||||
|
const std::vector<std::string> pieces =
|
||||||
|
{
|
||||||
|
"a", "Z", " ", "~", "0123456789", "\"", "\\", std::string(1, '\0'), "\n", "\x1F", "\x7F",
|
||||||
|
"\xC3\xA4", "\xE2\x82\xAC", "\xE6\x97\xA5\xE6\x9C\xAC", "\xF0\x9F\x98\x80", "\xED\x9F\xBF",
|
||||||
|
"\x80", "\xC0\x80", "\xC3", "\xE2\x82", "\xED\xA0\x80", "\xF4\x90\x80\x80", "\xFF",
|
||||||
|
};
|
||||||
|
std::uint64_t state = 5295;
|
||||||
|
const auto next = [&state]()
|
||||||
|
{
|
||||||
|
state ^= state << 13u;
|
||||||
|
state ^= state >> 7u;
|
||||||
|
state ^= state << 17u;
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
// the upper half as a 32-bit value: converts to std::size_t implicitly on
|
||||||
|
// every platform (a cast of std::uint64_t is useless where both are the
|
||||||
|
// same type, and required where std::size_t is 32 bits wide)
|
||||||
|
const auto next_small = [&next]()
|
||||||
|
{
|
||||||
|
return static_cast<std::uint32_t>(next() >> 32u);
|
||||||
|
};
|
||||||
|
for (int round = 0; round < 100000; ++round)
|
||||||
|
{
|
||||||
|
// mostly ordinary text, so that runs span several words
|
||||||
|
std::string text(next_small() % 8u, '.');
|
||||||
|
const std::size_t count = next_small() % 12u;
|
||||||
|
for (std::size_t k = 0; k < count; ++k)
|
||||||
|
{
|
||||||
|
const std::size_t p = (next() % 4 == 0) ? next_small() % pieces.size() : 0;
|
||||||
|
text += pieces[p];
|
||||||
|
text += std::string(next_small() % 10u, 'x');
|
||||||
|
}
|
||||||
|
const auto* data = reinterpret_cast<const unsigned char*>(text.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
|
for (std::size_t offset = 0; offset < 3 && offset <= text.size(); ++offset)
|
||||||
|
{
|
||||||
|
const std::size_t n = text.size() - offset;
|
||||||
|
CAPTURE(text)
|
||||||
|
CAPTURE(offset)
|
||||||
|
CHECK(nlohmann::detail::find_string_special(data + offset, n) == reference_special(data + offset, n));
|
||||||
|
CHECK(nlohmann::detail::find_ascii_copyable_run(data + offset, n) == reference_copyable(data + offset, n));
|
||||||
|
CHECK(nlohmann::detail::scalar_string_bulk_run(data + offset, n) == reference_bulk_run(data + offset, n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the trailing-zero count, whichever implementation the compiler gets
|
||||||
|
for (int k = 0; k < 64; ++k)
|
||||||
|
{
|
||||||
|
const std::uint64_t bit = std::uint64_t{1} << k;
|
||||||
|
CHECK(nlohmann::detail::count_trailing_zeros(bit) == k);
|
||||||
|
CHECK(nlohmann::detail::count_trailing_zeros(bit | (bit << 1u) | 0x8000000000000000u) == k);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,15 +37,194 @@ using nlohmann::json;
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "sax_countdown.hpp"
|
|
||||||
#include "sax_event_loggers.hpp"
|
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
|
|
||||||
using utils::SaxCountdown;
|
|
||||||
using utils::SaxEventLogger;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
class SaxEventLogger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool null()
|
||||||
|
{
|
||||||
|
events.emplace_back("null()");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool boolean(bool val)
|
||||||
|
{
|
||||||
|
events.emplace_back(val ? "boolean(true)" : "boolean(false)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_integer(json::number_integer_t val)
|
||||||
|
{
|
||||||
|
events.push_back("number_integer(" + std::to_string(val) + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_unsigned(json::number_unsigned_t val)
|
||||||
|
{
|
||||||
|
events.push_back("number_unsigned(" + std::to_string(val) + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_float(json::number_float_t /*unused*/, const std::string& s)
|
||||||
|
{
|
||||||
|
events.push_back("number_float(" + s + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string(std::string& val)
|
||||||
|
{
|
||||||
|
events.push_back("string(" + val + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool binary(json::binary_t& val)
|
||||||
|
{
|
||||||
|
std::string binary_contents = "binary(";
|
||||||
|
std::string comma_space;
|
||||||
|
for (auto b : val)
|
||||||
|
{
|
||||||
|
binary_contents.append(comma_space);
|
||||||
|
binary_contents.append(std::to_string(static_cast<int>(b)));
|
||||||
|
comma_space = ", ";
|
||||||
|
}
|
||||||
|
binary_contents.append(")");
|
||||||
|
events.push_back(binary_contents);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_object(std::size_t elements)
|
||||||
|
{
|
||||||
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
||||||
|
{
|
||||||
|
events.emplace_back("start_object()");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
events.push_back("start_object(" + std::to_string(elements) + ")");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool key(std::string& val)
|
||||||
|
{
|
||||||
|
events.push_back("key(" + val + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool end_object()
|
||||||
|
{
|
||||||
|
events.emplace_back("end_object()");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_array(std::size_t elements)
|
||||||
|
{
|
||||||
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
||||||
|
{
|
||||||
|
events.emplace_back("start_array()");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
events.push_back("start_array(" + std::to_string(elements) + ")");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool end_array()
|
||||||
|
{
|
||||||
|
events.emplace_back("end_array()");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_error(std::size_t position, const std::string& /*unused*/, const json::exception& /*unused*/)
|
||||||
|
{
|
||||||
|
errored = true;
|
||||||
|
events.push_back("parse_error(" + std::to_string(position) + ")");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
|
||||||
|
bool errored = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SaxCountdown : public nlohmann::json::json_sax_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SaxCountdown(const int count) : events_left(count)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool null() override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool boolean(bool /*val*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_integer(json::number_integer_t /*val*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_unsigned(json::number_unsigned_t /*val*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_float(json::number_float_t /*val*/, const std::string& /*s*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string(std::string& /*val*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool binary(json::binary_t& /*val*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_object(std::size_t /*elements*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool key(std::string& /*val*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool end_object() override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_array(std::size_t /*elements*/) override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool end_array() override
|
||||||
|
{
|
||||||
|
return events_left-- > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& /*ex*/) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int events_left = 0;
|
||||||
|
};
|
||||||
|
|
||||||
json parser_helper(const std::string& s);
|
json parser_helper(const std::string& s);
|
||||||
bool accept_helper(const std::string& s);
|
bool accept_helper(const std::string& s);
|
||||||
void comments_helper(const std::string& s);
|
void comments_helper(const std::string& s);
|
||||||
|
|||||||
@@ -36,15 +36,155 @@ using nlohmann::json;
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <valarray>
|
#include <valarray>
|
||||||
|
|
||||||
#include "sax_event_loggers.hpp"
|
|
||||||
|
|
||||||
using utils::SaxEventLogger;
|
|
||||||
using utils::SaxEventLoggerExitAfterKey;
|
|
||||||
using utils::SaxEventLoggerExitAfterStartArray;
|
|
||||||
using utils::SaxEventLoggerExitAfterStartObject;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
struct SaxEventLogger : public nlohmann::json_sax<json>
|
||||||
|
{
|
||||||
|
bool null() override
|
||||||
|
{
|
||||||
|
events.emplace_back("null()");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool boolean(bool val) override
|
||||||
|
{
|
||||||
|
events.emplace_back(val ? "boolean(true)" : "boolean(false)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_integer(json::number_integer_t val) override
|
||||||
|
{
|
||||||
|
events.push_back("number_integer(" + std::to_string(val) + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_unsigned(json::number_unsigned_t val) override
|
||||||
|
{
|
||||||
|
events.push_back("number_unsigned(" + std::to_string(val) + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool number_float(json::number_float_t /*val*/, const std::string& s) override
|
||||||
|
{
|
||||||
|
events.push_back("number_float(" + s + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string(std::string& val) override
|
||||||
|
{
|
||||||
|
events.push_back("string(" + val + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool binary(json::binary_t& val) override
|
||||||
|
{
|
||||||
|
std::string binary_contents = "binary(";
|
||||||
|
std::string comma_space;
|
||||||
|
for (auto b : val)
|
||||||
|
{
|
||||||
|
binary_contents.append(comma_space);
|
||||||
|
binary_contents.append(std::to_string(static_cast<int>(b)));
|
||||||
|
comma_space = ", ";
|
||||||
|
}
|
||||||
|
binary_contents.append(")");
|
||||||
|
events.push_back(binary_contents);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_object(std::size_t elements) override
|
||||||
|
{
|
||||||
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
||||||
|
{
|
||||||
|
events.emplace_back("start_object()");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
events.push_back("start_object(" + std::to_string(elements) + ")");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool key(std::string& val) override
|
||||||
|
{
|
||||||
|
events.push_back("key(" + val + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool end_object() override
|
||||||
|
{
|
||||||
|
events.emplace_back("end_object()");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_array(std::size_t elements) override
|
||||||
|
{
|
||||||
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
||||||
|
{
|
||||||
|
events.emplace_back("start_array()");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
events.push_back("start_array(" + std::to_string(elements) + ")");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool end_array() override
|
||||||
|
{
|
||||||
|
events.emplace_back("end_array()");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_error(std::size_t position, const std::string& /*last_token*/, const json::exception& /*ex*/) override
|
||||||
|
{
|
||||||
|
events.push_back("parse_error(" + std::to_string(position) + ")");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger
|
||||||
|
{
|
||||||
|
bool start_object(std::size_t elements) override
|
||||||
|
{
|
||||||
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
||||||
|
{
|
||||||
|
events.emplace_back("start_object()");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
events.push_back("start_object(" + std::to_string(elements) + ")");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaxEventLoggerExitAfterKey : public SaxEventLogger
|
||||||
|
{
|
||||||
|
bool key(std::string& val) override
|
||||||
|
{
|
||||||
|
events.push_back("key(" + val + ")");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger
|
||||||
|
{
|
||||||
|
bool start_array(std::size_t elements) override
|
||||||
|
{
|
||||||
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
||||||
|
{
|
||||||
|
events.emplace_back("start_array()");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
events.push_back("start_array(" + std::to_string(elements) + ")");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class proxy_iterator
|
class proxy_iterator
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -260,10 +260,11 @@ struct LocaleSwitchingSax final: public nlohmann::json_sax<json>
|
|||||||
|
|
||||||
TEST_CASE("locale changes between lexer construction and number conversion (#5198)")
|
TEST_CASE("locale changes between lexer construction and number conversion (#5198)")
|
||||||
{
|
{
|
||||||
// The numbers are chosen so that the conversion also takes the strtod
|
// float and double are converted without the locale. A long double that
|
||||||
// fallback, which honors the locale that is current at conversion time:
|
// is not binary64 can take the strtold fallback, which honors the locale
|
||||||
// too many significant digits for Clinger's fast path, an underflow that
|
// that is current at conversion time. The numbers are chosen so that it
|
||||||
// std::from_chars rejects, and a plain value.
|
// does: too many significant digits for Clinger's fast path, an underflow
|
||||||
|
// that std::from_chars rejects, and a plain value.
|
||||||
const std::vector<std::string> numbers = {"3.14159265358979323846", "1.5e-400", "12.34", "-0.000123456789012345678"};
|
const std::vector<std::string> numbers = {"3.14159265358979323846", "1.5e-400", "12.34", "-0.000123456789012345678"};
|
||||||
std::string text = "[";
|
std::string text = "[";
|
||||||
for (const auto& n : numbers)
|
for (const auto& n : numbers)
|
||||||
@@ -327,7 +328,8 @@ TEST_CASE("locale changes between lexer construction and number conversion (#519
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a long double goes through std::strtold unless std::from_chars supports it
|
// a long double goes through std::strtold unless it is binary64 or
|
||||||
|
// std::from_chars supports it
|
||||||
{
|
{
|
||||||
bool switched = false;
|
bool switched = false;
|
||||||
const auto cb = [&](int /*depth*/, long_double_json::parse_event_t event, long_double_json& /*parsed*/) noexcept
|
const auto cb = [&](int /*depth*/, long_double_json::parse_event_t event, long_double_json& /*parsed*/) noexcept
|
||||||
@@ -353,8 +355,15 @@ TEST_CASE("locale with a multi-byte decimal point")
|
|||||||
{
|
{
|
||||||
// Some locales use a decimal point that is not a single character, e.g.
|
// Some locales use a decimal point that is not a single character, e.g.
|
||||||
// U+066B ARABIC DECIMAL SEPARATOR (two bytes in UTF-8). It cannot be
|
// U+066B ARABIC DECIMAL SEPARATOR (two bytes in UTF-8). It cannot be
|
||||||
// substituted in place for '.', so the strtod fallback stops early. The
|
// substituted in place for '.', so the strtold fallback (only for long
|
||||||
// conversion must still terminate rather than retry forever.
|
// double formats other than binary64) converts a copy of the token with
|
||||||
|
// the whole decimal point instead (#5660). The values must be those of the
|
||||||
|
// "C" locale.
|
||||||
|
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||||
|
const char* const long_double_numbers = "[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]";
|
||||||
|
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||||
|
const long_double_json expected_long_double = long_double_json::parse(long_double_numbers);
|
||||||
|
|
||||||
const std::array<const char*, 6> names = {{"ar_EG.UTF-8", "ar_SA.UTF-8", "fa_IR.UTF-8", "ps_AF.UTF-8", "ar_EG", "fa_IR"}};
|
const std::array<const char*, 6> names = {{"ar_EG.UTF-8", "ar_SA.UTF-8", "fa_IR.UTF-8", "ps_AF.UTF-8", "ar_EG", "fa_IR"}};
|
||||||
bool tested = false;
|
bool tested = false;
|
||||||
for (const char* name : names)
|
for (const char* name : names)
|
||||||
@@ -372,12 +381,20 @@ TEST_CASE("locale with a multi-byte decimal point")
|
|||||||
tested = true;
|
tested = true;
|
||||||
|
|
||||||
// too many significant digits for Clinger's fast path, and an underflow
|
// too many significant digits for Clinger's fast path, and an underflow
|
||||||
// that std::from_chars rejects: both reach the strtod fallback
|
// that std::from_chars rejects: double does not depend on the locale
|
||||||
json j;
|
json j;
|
||||||
CHECK_NOTHROW(j = json::parse("[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]"));
|
CHECK_NOTHROW(j = json::parse("[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]"));
|
||||||
CHECK(j.is_array());
|
CHECK(j.is_array());
|
||||||
|
CHECK(j[0] == 3.14159265358979323846);
|
||||||
|
CHECK(j[1] == 0.0);
|
||||||
|
CHECK(j[2] == -0.000123456789012345678);
|
||||||
CHECK(json::accept("3.14159265358979323846"));
|
CHECK(json::accept("3.14159265358979323846"));
|
||||||
|
|
||||||
|
// a long double that reaches the strtold fallback is not truncated
|
||||||
|
long_double_json ld;
|
||||||
|
CHECK_NOTHROW(ld = long_double_json::parse(long_double_numbers));
|
||||||
|
CHECK(ld == expected_long_double);
|
||||||
|
|
||||||
// a value the locale-independent paths convert is not affected
|
// a value the locale-independent paths convert is not affected
|
||||||
CHECK(json::parse("12.5") == 12.5);
|
CHECK(json::parse("12.5") == 12.5);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user