Compare commits

..
Author SHA1 Message Date
Niels Lohmann c9a6d0635f Add docset entry for basic_json::with_t
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 11:48:19 +02:00
Niels Lohmann a4c4400b21 Fix with_t nav entry and document chaining of the with_*_t aliases
Indent the with_t entry in mkdocs.yml so it is listed under basic_json,
explain that the aliases can be chained and work on ordered_json, and
test both, including json::with_object_t<ordered_map> == ordered_json.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 07:25:15 +02:00
Niels LohmannandRaphael Grimm 2bb3988854 Add tests for the with_*_t member alias templates
Check with std::is_same that each with_*_t alias produces the expected
basic_json type, and that with_string_t keeps nlohmann::ordered_map as
the object type when used on ordered_json.

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 07:25:14 +02:00
Niels LohmannandRaphael Grimm 8452edbc06 Add documentation for the with_*_t member alias templates
Add docs/mkdocs/docs/api/basic_json/with_t.md documenting with_object_t,
with_array_t, with_string_t, with_boolean_t, with_integers_t, with_float_t,
with_allocator_t, with_json_serializer_t, with_binary_t and with_base_class_t,
with an accompanying example, and link the page from the basic_json member
types list and the mkdocs navigation.

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 07:25:13 +02:00
Niels LohmannandRaphael Grimm e48deffea5 Rename with_changed_*_t aliases to with_*_t and merge integer/unsigned aliases
Per review discussion on #3898 between gregmarr and nlohmann:
- rename with_changed_X_t to with_X_t for brevity
- replace the separate with_changed_integer_t/with_changed_unsigned_t
  aliases with a single with_integers_t<NumberIntegerType2, NumberUnsignedType2>
- add @sa doc comment links for the upcoming documentation page

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 07:25:12 +02:00
barcode 99601407d3 Add helper types to make it easier to create a basic_json type with modified template parameters
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 07:25:11 +02:00
43 changed files with 1570 additions and 4243 deletions

No files matched your search

+6 -6
View File
@@ -17,11 +17,11 @@ permissions:
contents: read contents: read
jobs: jobs:
macos-15: macos-14:
runs-on: macos-15 # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md runs-on: macos-14 # https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md
strategy: strategy:
matrix: matrix:
xcode: ['16.0', '16.1', '16.2', '16.3', '16.4', '26.0.1', '26.1.1', '26.2', '26.3'] xcode: ['15.0.1', '15.1', '15.2', '15.3', '15.4']
env: env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
@@ -36,11 +36,11 @@ jobs:
- name: Test - name: Test
run: cd build ; ctest -j 10 --output-on-failure run: cd build ; ctest -j 10 --output-on-failure
macos-26: macos-15:
runs-on: macos-26 # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md runs-on: macos-15 # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
strategy: strategy:
matrix: matrix:
xcode: ['26.4.1', '26.5', '26.6'] xcode: ['16.0', '16.1', '16.2', '16.3', '16.4', '26.0.1']
env: env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
+1 -1
View File
@@ -209,7 +209,7 @@ jobs:
strategy: strategy:
matrix: matrix:
# older GCC docker images (4, 5, 6) fail to check out code # older GCC docker images (4, 5, 6) fail to check out code
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', '16', 'latest'] compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', 'latest']
container: gcc:${{ matrix.compiler }} container: gcc:${{ matrix.compiler }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+1 -1
View File
@@ -1393,7 +1393,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 &copy; 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 &copy; 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, 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 &copy; 2021 The fast_float authors - 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 &copy; 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">
+1
View File
@@ -131,6 +131,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Meth
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value_t', 'Enum', 'api/basic_json/value_t/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value_t', 'Enum', 'api/basic_json/value_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::with_t', 'Type', 'api/basic_json/with_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Method', 'api/basic_json/~basic_json/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Method', 'api/basic_json/~basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html');
+3
View File
@@ -109,6 +109,9 @@ The class satisfies the following concept requirements:
- **initializer_list_t** - type for initializer lists of `basic_json` values - **initializer_list_t** - type for initializer lists of `basic_json` values
- [**input_format_t**](input_format_t.md) - type to choose the format to parse - [**input_format_t**](input_format_t.md) - type to choose the format to parse
- [**json_sax_t**](../json_sax/index.md) - type for SAX events - [**json_sax_t**](../json_sax/index.md) - type for SAX events
- [**with_object_t, with_array_t, with_string_t, with_boolean_t, with_integers_t, with_float_t, with_allocator_t,
with_json_serializer_t, with_binary_t, with_base_class_t**](with_t.md) - types to create a `basic_json` type with
one (or two) replaced template parameters
### Exceptions ### Exceptions
@@ -23,10 +23,9 @@ type to use.
## Template parameters ## Template parameters
`NumberFloatType` `NumberFloatType`
: the type to store floating-point numbers. The parser converts `#!cpp float`, `#!cpp double`, and a : the type to store floating-point numbers. Parsing and serialization are implemented in terms of
`#!cpp long double` that is IEEE 754 binary64 itself and other `#!cpp long double` formats with `#!cpp std::strtof`/`#!cpp std::strtod`/`#!cpp std::strtold` and `#!cpp std::snprintf`, so the type must be
`#!cpp std::from_chars` or `#!cpp std::strtold`, and serialization falls back to `#!cpp std::snprintf`, so the `#!cpp float`, `#!cpp double`, or `#!cpp long double`. 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).
+1 -5
View File
@@ -68,8 +68,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict` (the default only if not valid UTF-8 and `error_handler` is `strict` (the default only if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
discarded; example: `"cannot serialize discarded value to BJData"`
## Complexity ## Complexity
@@ -121,6 +119,4 @@ Linear in the size of the JSON value `j`.
- BJData version parameter (for draft3 binary encoding) added in version 3.12.0. - BJData version parameter (for draft3 binary encoding) added in version 3.12.0.
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key - Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
that is not valid UTF-8 unchanged, as before; `strict` (the default if that is not valid UTF-8 unchanged, as before; `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`. [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
array or object was silently skipped, producing invalid BJData.
@@ -58,9 +58,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key is - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key is
not valid UTF-8 and `error_handler` is `strict` (the default only if not valid UTF-8 and `error_handler` is `strict` (the default only if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if a value nested in `j` is discarded
(the top-level value itself is covered by `type_error.317` above, since it must be an object); example:
`"cannot serialize discarded value to BSON"`
## Complexity ## Complexity
@@ -113,8 +110,6 @@ pass before anything is written.
- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0. - Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0.
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0. - Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
- `out_of_range.415` is now detected before anything is written, like the other exceptions above, since version 3.13.0. - `out_of_range.415` is now detected before anything is written, like the other exceptions above, since version 3.13.0.
- Throws `type_error.321` for a discarded value nested in `j` since version 3.13.0; previously, it was silently
skipped, producing a document whose declared size did not match what was actually written.
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key - Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
that is not valid UTF-8 unchanged, as before; `strict` (the default if that is not valid UTF-8 unchanged, as before; `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316` before anything [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316` before anything
@@ -49,8 +49,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict` (the default only if not valid UTF-8 and `error_handler` is `strict` (the default only if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
discarded; example: `"cannot serialize discarded value to CBOR"`
## Complexity ## Complexity
@@ -88,5 +86,3 @@ Linear in the size of the JSON value `j`.
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key - Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
that is not valid UTF-8 unchanged, as before; `strict` (the default if that is not valid UTF-8 unchanged, as before; `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`. [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
array or object was silently skipped, producing invalid CBOR.
@@ -54,8 +54,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
`"subtype 70000 is too large for the MessagePack ext type (max 255)"` `"subtype 70000 is too large for the MessagePack ext type (max 255)"`
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict` not valid UTF-8 and `error_handler` is `strict`
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
discarded; example: `"cannot serialize discarded value to MessagePack"`
## Complexity ## Complexity
@@ -110,5 +108,3 @@ Linear in the size of the JSON value `j`.
- Fixed in version 3.13.0 to serialize `number_integer_t`/`number_unsigned_t` pairs of different width correctly; - Fixed in version 3.13.0 to serialize `number_integer_t`/`number_unsigned_t` pairs of different width correctly;
before, integers could be serialized with the wrong value if `number_integer_t` was narrower than before, integers could be serialized with the wrong value if `number_integer_t` was narrower than
`number_unsigned_t`. `number_unsigned_t`.
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
array or object was silently skipped, producing invalid MessagePack.
@@ -61,8 +61,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict` (the default only if not valid UTF-8 and `error_handler` is `strict` (the default only if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
discarded; example: `"cannot serialize discarded value to UBJSON"`
## Complexity ## Complexity
@@ -114,5 +112,3 @@ Linear in the size of the JSON value `j`.
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key - Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
that is not valid UTF-8 unchanged, as before; `strict` (the default if that is not valid UTF-8 unchanged, as before; `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`. [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
array or object was silently skipped, producing invalid UBJSON.
+136
View File
@@ -0,0 +1,136 @@
# <small>nlohmann::basic_json::</small>with_t
Member alias templates `with_object_t`, `with_array_t`, `with_string_t`, `with_boolean_t`, `with_integers_t`,
`with_float_t`, `with_allocator_t`, `with_json_serializer_t`, `with_binary_t`, and `with_base_class_t`.
```cpp
template<template<typename, typename, typename...> class ObjectType2>
using with_object_t = basic_json<ObjectType2, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
template<template<typename, typename...> class ArrayType2>
using with_array_t = basic_json<ObjectType, ArrayType2, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
template<class StringType2>
using with_string_t = basic_json<ObjectType, ArrayType, StringType2, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
template<class BooleanType2>
using with_boolean_t = basic_json<ObjectType, ArrayType, StringType, BooleanType2,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
template<class NumberIntegerType2, class NumberUnsignedType2>
using with_integers_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType2, NumberUnsignedType2, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
template<class NumberFloatType2>
using with_float_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType2,
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
template<template<typename> class AllocatorType2>
using with_allocator_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType2, JSONSerializer, BinaryType, CustomBaseClass>;
template<template<typename, typename = void> class JSONSerializer2>
using with_json_serializer_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer2, BinaryType, CustomBaseClass>;
template<class BinaryType2>
using with_binary_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType2, CustomBaseClass>;
template<class CustomBaseClass2>
using with_base_class_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass2>;
```
These member alias templates make it easier to create a `basic_json` type that is identical to the current type except
for one (or, in the case of `with_integers_t`, two) of its [template parameters](index.md#template-parameters).
Spelling out all 11 template parameters of `basic_json` just to change a single one is verbose and error-prone; these
aliases only require the replacement type(s).
with_object_t&lt;ObjectType2&gt;
: replaces `ObjectType`
with_array_t&lt;ArrayType2&gt;
: replaces `ArrayType`
with_string_t&lt;StringType2&gt;
: replaces `StringType`
with_boolean_t&lt;BooleanType2&gt;
: replaces `BooleanType`
with_integers_t&lt;NumberIntegerType2, NumberUnsignedType2&gt;
: replaces both `NumberIntegerType` and `NumberUnsignedType`; the two are combined into a single alias because they
are usually changed together (for instance, when switching to fixed-width integer types)
with_float_t&lt;NumberFloatType2&gt;
: replaces `NumberFloatType`
with_allocator_t&lt;AllocatorType2&gt;
: replaces `AllocatorType`
with_json_serializer_t&lt;JSONSerializer2&gt;
: replaces `JSONSerializer`
with_binary_t&lt;BinaryType2&gt;
: replaces `BinaryType`
with_base_class_t&lt;CustomBaseClass2&gt;
: replaces `CustomBaseClass`; see also [`json_base_class_t`](json_base_class_t.md)
## Notes
All other template parameters are kept unchanged, so the resulting type still uses, for instance, the same
`ObjectType` unless `with_object_t` itself is used.
The aliases are members of every `basic_json` specialization, including [`ordered_json`](../ordered_json.md), and the
type they produce is again a `basic_json` specialization. They can therefore be chained to replace several template
parameters at once:
```cpp
using my_json = nlohmann::json::with_integers_t<int, unsigned int>::with_float_t<float>;
using my_ordered_json = nlohmann::ordered_json::with_string_t<std::wstring>;
```
The result is the same type as spelling out all template parameters, so the order of the chained aliases does not
matter. For instance, `nlohmann::json::with_object_t<nlohmann::ordered_map>` is `nlohmann::ordered_json`.
## Examples
??? example
The following code shows how `with_object_t` can be used to create a JSON type that stores object elements in a
`std::map` and therefore keeps them sorted by key, unlike the default type which preserves insertion order
only when `nlohmann::ordered_json` is used.
```cpp
--8<-- "examples/with_t.cpp"
```
Output:
```json
--8<-- "examples/with_t.output"
```
## See also
- [basic_json](index.md#template-parameters) - the template parameters that can be replaced
- [json_base_class_t](json_base_class_t.md) - the type used for `CustomBaseClass`
## Version history
- Added in version 3.13.0.
@@ -21,18 +21,17 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
| Compiler | Architecture | Operating System | CI | | Compiler | Architecture | Operating System | CI |
|----------------------------------------------|--------------|-----------------------------------|-----------| |----------------------------------------------|--------------|-----------------------------------|-----------|
| AppleClang 15.0.0.15000040; Xcode 15.0.1 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
| AppleClang 15.0.0.15000100; Xcode 15.1 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
| AppleClang 15.0.0.15000100; Xcode 15.2 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
| AppleClang 15.0.0.15000309; Xcode 15.3 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
| AppleClang 15.0.0.15000309; Xcode 15.4 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
| AppleClang 16.0.0.16000026; Xcode 16 | arm64 | macOS 15.2 (Sequoia) | GitHub | | AppleClang 16.0.0.16000026; Xcode 16 | arm64 | macOS 15.2 (Sequoia) | GitHub |
| AppleClang 16.0.0.16000026; Xcode 16.1 | arm64 | macOS 15.2 (Sequoia) | GitHub | | AppleClang 16.0.0.16000026; Xcode 16.1 | arm64 | macOS 15.2 (Sequoia) | GitHub |
| AppleClang 16.0.0.16000026; Xcode 16.2 | arm64 | macOS 15.2 (Sequoia) | GitHub | | AppleClang 16.0.0.16000026; Xcode 16.2 | arm64 | macOS 15.2 (Sequoia) | GitHub |
| AppleClang 17.0.0.17000013; Xcode 16.3 | arm64 | macOS 15.5 (Sequoia) | GitHub | | AppleClang 17.0.0.17000013; Xcode 16.3 | arm64 | macOS 15.5 (Sequoia) | GitHub |
| AppleClang 17.0.0.17000013; Xcode 16.4 | arm64 | macOS 15.5 (Sequoia) | GitHub | | AppleClang 17.0.0.17000013; Xcode 16.4 | arm64 | macOS 15.5 (Sequoia) | GitHub |
| AppleClang 17.0.0.17000319; Xcode 26.0.1 | arm64 | macOS 15.5 (Sequoia) | GitHub | | AppleClang 17.0.0.17000319; Xcode 26.0.1 | arm64 | macOS 15.5 (Sequoia) | GitHub |
| AppleClang 17.0.0.17000404; Xcode 26.1.1 | arm64 | macOS 15.7.9 (Sequoia) | GitHub |
| AppleClang 17.0.0.17000603; Xcode 26.2 | arm64 | macOS 15.7.9 (Sequoia) | GitHub |
| AppleClang 17.0.0.17000604; Xcode 26.3 | arm64 | macOS 15.7.9 (Sequoia) | GitHub |
| AppleClang 21.0.0.21000099; Xcode 26.4.1 | arm64 | macOS 26.6.2 (Tahoe) | GitHub |
| AppleClang 21.0.0.21000101; Xcode 26.5 | arm64 | macOS 26.6.2 (Tahoe) | GitHub |
| AppleClang 21.0.0.21000101; Xcode 26.6 | arm64 | macOS 26.6.2 (Tahoe) | GitHub |
| Clang 3.4.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | | Clang 3.4.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| Clang 3.5.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | | Clang 3.5.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| Clang 3.6.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | | Clang 3.6.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
@@ -90,7 +89,7 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
| GNU 13.3.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | | GNU 13.3.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | | GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | | GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 16.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | | GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 16.1.0 | arm64 | Ubuntu 24.04 | GitHub | | GNU 16.1.0 | arm64 | Ubuntu 24.04 | GitHub |
| icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub | | icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub |
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub | | icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
@@ -13,19 +13,7 @@ class visitor_adaptor_with_metadata
void do_visit(const Ptr& ptr, const Fnc& fnc) const; void do_visit(const Ptr& ptr, const Fnc& fnc) const;
}; };
using json = nlohmann::basic_json < using json = nlohmann::json::with_base_class_t<visitor_adaptor_with_metadata>;
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
visitor_adaptor_with_metadata
>;
template <class Fnc> template <class Fnc>
void visitor_adaptor_with_metadata::visit(const Fnc& fnc) const void visitor_adaptor_with_metadata::visit(const Fnc& fnc) const
+18
View File
@@ -0,0 +1,18 @@
#include <iostream>
#include <map>
#include <nlohmann/json.hpp>
// a JSON type that stores objects in a std::map (which keeps keys sorted)
// instead of the default ordered associative container
using sorted_json = nlohmann::json::with_object_t<std::map>;
int main()
{
sorted_json j;
j["c"] = 1;
j["a"] = 2;
j["b"] = 3;
// keys are sorted, because std::map is used to store the object
std::cout << j.dump() << std::endl;
}
+1
View File
@@ -0,0 +1 @@
{"a":2,"b":3,"c":1}
@@ -82,13 +82,12 @@ 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).
- The library converts integers and floating-point numbers itself, independent of the locale. Floating-point - Integers are converted by the library's own digit parser. Floating-point numbers are converted with
numbers are correctly rounded (to nearest, ties to even). Only a `#!c long double` that is not IEEE 754 binary64 [`std::from_chars`](https://en.cppreference.com/w/cpp/utility/from_chars) if the library is compiled with C++17
(e.g., the 80-bit x87 format) is converted with `#!cpp std::from_chars` where available, or else with and the standard library supports it, then with an exact fast path for `#!c double` values with few significant
[`std::strtold`](https://en.cppreference.com/w/cpp/string/byte/strtof). For that call, the library temporarily digits, and otherwise with the locale-aware
replaces the `.` with the decimal point of the current locale (which may be longer than one byte, e.g., in [`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) (`std::strtof`/`std::strtold` for the
`fa_IR.UTF-8`), so the result does not depend on the locale either. Changing the locale in another thread during other floating-point types). Before version 3.13.0, the conversion was realized by
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.
@@ -101,10 +100,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 whose rounded value is not satisfying - Numbers exceeding the limits of `#!c double` (i.e., numbers that after conversion via
[`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. Numbers too [`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing.
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;`.
@@ -26,9 +26,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 silently misparses numbers - A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and can silently misparse
stored as a `#!cpp long double` that is not IEEE 754 binary64 (e.g., the 80-bit x87 format), because the lexer floating-point numbers, because the lexer may hand the buffer to `#!cpp std::strtod`, which reads up to the
hands the buffer to `#!cpp std::strtold`. terminating null character.
- 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
@@ -537,10 +537,9 @@ 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 to `#!cpp float`, `#!cpp double`, and a - The [parser](../parsing/index.md) converts number literals with `#!cpp std::from_chars` or, as a fallback, with
`#!cpp long double` that is IEEE 754 binary64 itself; other `#!cpp long double` formats are converted with `#!cpp std::strtof`, `#!cpp std::strtod`, or `#!cpp std::strtold`; the library provides overloads for exactly these
`#!cpp std::from_chars` where available, or with `#!cpp std::strtold`. The library provides overloads for exactly three types.
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`).
-14
View File
@@ -804,20 +804,6 @@ does not list an enumerator and it is therefore converted like the first listed
[json.exception.type_error.318] duplicate object key 'red' [json.exception.type_error.318] duplicate object key 'red'
``` ```
### json.exception.type_error.321
A discarded value (one created by [`parse()`](../api/basic_json/parse.md) with a callback that returns `false` for the
value, or by default-constructing a [`basic_json`](../api/basic_json/index.md) with
[`value_t::discarded`](../api/basic_json/value_t.md)) was passed to a binary serialization function, either directly or
nested in an array or object. There is no way to represent a discarded value in CBOR, MessagePack, UBJSON, BJData, or BSON.
!!! failure "Example message"
Serializing `#!json [1, 2]` to CBOR, where the second element was discarded by a parser callback:
```
[json.exception.type_error.321] cannot serialize discarded value to CBOR
```
## Out of range ## Out of range
This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance, in the case of array indices or nonexisting object keys. This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance, in the case of array indices or nonexisting object keys.
+1 -1
View File
@@ -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, 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 &copy; 2021 The fast_float authors 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 &copy; 2021 The fast_float authors
+1
View File
@@ -232,6 +232,7 @@ nav:
- 'update': api/basic_json/update.md - 'update': api/basic_json/update.md
- 'value': api/basic_json/value.md - 'value': api/basic_json/value.md
- 'value_t': api/basic_json/value_t.md - 'value_t': api/basic_json/value_t.md
- 'with_t': api/basic_json/with_t.md
- byte_container_with_subtype: - byte_container_with_subtype:
- 'Overview': api/byte_container_with_subtype/index.md - 'Overview': api/byte_container_with_subtype/index.md
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md - '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
+2 -26
View File
@@ -39,25 +39,6 @@ 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
{ {
@@ -87,19 +68,14 @@ 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 unsigned char* b) noexcept inline std::uint64_t read_eight_bytes(const char* p) 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
+11 -92
View File
@@ -221,44 +221,6 @@ 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`
@@ -278,14 +240,6 @@ 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 };
@@ -1090,11 +1044,9 @@ 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 `.`. The conversion of float and double does not use always holds `.`. Only the std::strtod fallback of convert_number()
the locale either. Only the std::strtold fallback of depends on the locale, and it looks up the decimal point right
convert_number() for long double formats other than binary64 before converting (see detail::convert_float_locale_aware()).
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.
{ {
@@ -1107,7 +1059,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 split the token; npos means // convert_number() uses it to count significant digits; 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;
@@ -1437,8 +1389,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);
with decimal_point_position, it locates the parts used to skip Clinger's fast path when it cannot
of a float token without scanning it again possibly succeed - see detail::mantissa_fits_clinger()
*/ */
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)
{ {
@@ -1492,11 +1444,10 @@ 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. float and double (and long // integer conversion above overflowed. Prefer std::from_chars
// double where it is binary64) are converted by the library itself, // (Eisel-Lemire, locale-independent, correctly rounded) when available;
// correctly rounded and independent of the locale; other long double // otherwise the exact Clinger fast path (double only); otherwise the
// formats use std::from_chars when available, otherwise the // locale-aware strtof/strtod/strtold.
// 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;
@@ -2070,39 +2021,6 @@ scan_number_done:
// read the next character and ignore whitespace // read the next character and ignore whitespace
skip_whitespace(); skip_whitespace();
return scan_after_whitespace();
}
/*!
@brief scan the next token when the caller expects a separator (':' or
',') most of the time
After an object key the next token is almost always ':', after a value
inside an object or array almost always ','. Testing for that character
first is a compare and a well-predicted branch, where the switch in
scan_after_whitespace() is an indirect jump through a table. Anything else
goes through the switch, so the result is the same as scan()'s.
May only be called after scan() has run once (the BOM check is skipped).
*/
token_type scan_expecting(token_type expected_type)
{
JSON_ASSERT(expected_type == token_type::name_separator || expected_type == token_type::value_separator);
JSON_ASSERT(position.chars_read_total > 0);
const char_int_type expected_char = static_cast<unsigned char>((expected_type == token_type::name_separator) ? ':' : ',');
skip_whitespace();
if (JSON_HEDLEY_LIKELY(current == expected_char))
{
return expected_type;
}
return scan_after_whitespace();
}
private:
/// the part of scan() after the leading whitespace: skip comments and
/// scan the token that starts with current
token_type scan_after_whitespace()
{
// ignore comments // ignore comments
while (ignore_comments && current == '/') while (ignore_comments && current == '/')
{ {
@@ -2182,6 +2100,7 @@ scan_number_done:
} }
} }
private:
/// input adapter /// input adapter
InputAdapterType ia; InputAdapterType ia;
File diff suppressed because it is too large. Load diff
+4 -11
View File
@@ -260,7 +260,7 @@ class parser
} }
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{ {
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(),
@@ -423,7 +423,7 @@ class parser
{ {
// comma -> next value // comma -> next value
// or end of array (ignore_trailing_commas = true) // or end of array (ignore_trailing_commas = true)
if (get_token_expecting(token_type::value_separator)) if (get_token() == token_type::value_separator)
{ {
// parse a new value // parse a new value
get_token(); get_token();
@@ -463,7 +463,7 @@ class parser
// comma -> next value // comma -> next value
// or end of object (ignore_trailing_commas = true) // or end of object (ignore_trailing_commas = true)
if (get_token_expecting(token_type::value_separator)) if (get_token() == token_type::value_separator)
{ {
get_token(); get_token();
@@ -484,7 +484,7 @@ class parser
} }
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{ {
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(),
@@ -528,13 +528,6 @@ class parser
return last_token = m_lexer.scan(); return last_token = m_lexer.scan();
} }
/// get next token from lexer; true if it is the separator @a expected_type
/// (name_separator or value_separator), which it usually is
bool get_token_expecting(token_type expected_type)
{
return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type;
}
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 ";
+29 -70
View File
@@ -8,12 +8,10 @@
#pragma once #pragma once
#include <array> // array
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint64_t, uint8_t #include <cstdint> // uint64_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
@@ -71,12 +69,18 @@ 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)
{ {
const std::uint64_t special = swar_string_special(read_eight_bytes(data + i)); std::uint64_t word = 0;
if (special != 0) std::memcpy(&word, data + i, sizeof(word));
if (swar_string_special(word) != 0)
{ {
// the lowest flagged byte is the first special one: the borrows of // a special byte is in this word; locate it (endian-agnostic)
// the subtractions can only flag bytes above a true hit for (std::size_t j = 0; j < 8; ++j)
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)
@@ -110,7 +114,8 @@ 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)
{ {
const std::uint64_t v = read_eight_bytes(data + i); std::uint64_t v = 0;
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)
@@ -121,9 +126,7 @@ 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)
{ {
// the lowest flagged byte is the first one to stop at (see break;
// find_string_special())
return i + (static_cast<std::size_t>(count_trailing_zeros(stop)) / 8);
} }
} }
for (; i < n; ++i) for (; i < n; ++i)
@@ -250,18 +253,12 @@ 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 const std::size_t seq = validate_one_utf8(data + pos, n - pos);
// by sequence without searching for the next special byte in between if (seq == 0)
do
{ {
const std::size_t seq = validate_one_utf8(data + pos, n - pos); break; // ill-formed or truncated: let the byte path diagnose it
if (seq == 0)
{
return pos; // ill-formed or truncated: let the byte path diagnose it
}
pos += seq;
} }
while (pos < n && data[pos] >= 0x80u); pos += seq;
} }
return pos; return pos;
} }
@@ -276,7 +273,8 @@ 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)
{ {
const std::uint64_t v = read_eight_bytes(data + i); std::uint64_t v = 0;
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)
@@ -284,8 +282,14 @@ 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)
{ {
// the lowest flagged byte is the first delimiter (see find_string_special()) for (std::size_t j = 0; j < 8; ++j)
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)
@@ -316,50 +320,5 @@ 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
@@ -127,7 +127,6 @@ class binary_writer
@throw type_error.316 if a string value or an object key is not valid @throw type_error.316 if a string value or an object key is not valid
UTF-8 UTF-8
@throw type_error.317 if @a j is not an object @throw type_error.317 if @a j is not an object
@throw type_error.321 if a value nested in @a j is discarded
*/ */
void write_bson(const BasicJsonType& j) void write_bson(const BasicJsonType& j)
{ {
@@ -159,7 +158,6 @@ class binary_writer
@param[in] j JSON value to serialize @param[in] j JSON value to serialize
@throw type_error.316 if a string value or an object key is not valid @throw type_error.316 if a string value or an object key is not valid
UTF-8 UTF-8
@throw type_error.321 if @a j or a value nested in it is discarded
*/ */
void write_cbor(const BasicJsonType& j) void write_cbor(const BasicJsonType& j)
{ {
@@ -324,7 +322,7 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
default: default:
throw_on_discarded(j, "CBOR"); break;
} }
} }
@@ -384,7 +382,6 @@ class binary_writer
/*! /*!
@param[in] j JSON value to serialize @param[in] j JSON value to serialize
@throw type_error.321 if @a j or a value nested in it is discarded
*/ */
void write_msgpack(const BasicJsonType& j) void write_msgpack(const BasicJsonType& j)
{ {
@@ -658,7 +655,7 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
default: default:
throw_on_discarded(j, "MessagePack"); break;
} }
} }
@@ -671,7 +668,6 @@ class binary_writer
@param[in] bjdata_version which BJData version to use, default is draft2 @param[in] bjdata_version which BJData version to use, default is draft2
@throw type_error.316 if a string value or an object key is not valid @throw type_error.316 if a string value or an object key is not valid
UTF-8 UTF-8
@throw type_error.321 if @a j or a value nested in it is discarded
*/ */
void write_ubjson(const BasicJsonType& j, const bool use_count, void write_ubjson(const BasicJsonType& j, const bool use_count,
const bool use_type, const bool add_prefix = true, const bool use_type, const bool add_prefix = true,
@@ -905,7 +901,7 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
default: default:
throw_on_discarded(j, use_bjdata ? "BJData" : "UBJSON"); break;
} }
} }
@@ -925,15 +921,6 @@ class binary_writer
} }
private: private:
/*!
@brief throws because @a j is discarded and cannot be serialized
@throw type_error.321 always
*/
JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const char* format_name)
{
JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j));
}
////////// //////////
// BSON // // BSON //
////////// //////////
@@ -1185,7 +1172,6 @@ class binary_writer
into a byte, before anything is written into a byte, before anything is written
@throw type_error.316 if @a j is a string that is not valid UTF-8, before @throw type_error.316 if @a j is a string that is not valid UTF-8, before
anything is written anything is written
@throw type_error.321 if @a j is discarded
*/ */
std::size_t calc_bson_value_size(const BasicJsonType& j) std::size_t calc_bson_value_size(const BasicJsonType& j)
{ {
@@ -1212,12 +1198,10 @@ class binary_writer
case value_t::null: case value_t::null:
return 0ul; return 0ul;
case value_t::discarded:
throw_on_discarded(j, "BSON");
// LCOV_EXCL_START // LCOV_EXCL_START
case value_t::object: case value_t::object:
case value_t::array: case value_t::array:
case value_t::discarded:
default: default:
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
return 0ul; return 0ul;
@@ -1254,12 +1238,10 @@ class binary_writer
case value_t::null: case value_t::null:
return write_bson_null(name); return write_bson_null(name);
case value_t::discarded:
throw_on_discarded(j, "BSON");
// LCOV_EXCL_START // LCOV_EXCL_START
case value_t::object: case value_t::object:
case value_t::array: case value_t::array:
case value_t::discarded:
default: default:
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
return; return;
@@ -1326,8 +1308,6 @@ class binary_writer
byte, before anything is written byte, before anything is written
@throw type_error.316 if a string value or a key is not valid UTF-8, @throw type_error.316 if a string value or a key is not valid UTF-8,
before anything is written before anything is written
@throw type_error.321 if a value nested in @a document is discarded,
before anything is written
*/ */
std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes) std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
{ {
+138 -182
View File
@@ -226,6 +226,70 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// SAX interface type, see @ref nlohmann::json_sax /// SAX interface type, see @ref nlohmann::json_sax
using json_sax_t = json_sax<basic_json>; using json_sax_t = json_sax<basic_json>;
////////////////////////////////////////////////////////////////////////////////
// utility templates to create a json type with different template parameters //
////////////////////////////////////////////////////////////////////////////////
/// Json type using a different type for storing objects
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<template<typename, typename, typename...> class ObjectType2>
using with_object_t = basic_json<ObjectType2, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
/// Json type using a different type for storing arrays
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<template<typename, typename...> class ArrayType2>
using with_array_t = basic_json<ObjectType, ArrayType2, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
/// Json type using a different type for storing strings
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<class StringType2>
using with_string_t = basic_json<ObjectType, ArrayType, StringType2, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
/// Json type using a different type for storing booleans
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<class BooleanType2>
using with_boolean_t = basic_json<ObjectType, ArrayType, StringType, BooleanType2,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
/// Json type using different types for storing signed and unsigned integers
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<class NumberIntegerType2, class NumberUnsignedType2>
using with_integers_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType2, NumberUnsignedType2, NumberFloatType, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
/// Json type using a different type for storing floating point numbers
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<class NumberFloatType2>
using with_float_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType2, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>;
/// Json type using a different type as base allocator
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<template<typename> class AllocatorType2>
using with_allocator_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType2, JSONSerializer, BinaryType, CustomBaseClass>;
/// Json type using a different type as json serializer
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<template<typename, typename = void> class JSONSerializer2>
using with_json_serializer_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer2, BinaryType, CustomBaseClass>;
/// Json type using a different type for storing binary data
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<class BinaryType2>
using with_binary_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType2, CustomBaseClass>;
/// Json type using a different type as base class
/// @sa https://json.nlohmann.me/api/basic_json/with_t/
template<class CustomBaseClass2>
using with_base_class_t = basic_json<ObjectType, ArrayType, StringType, BooleanType,
NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass2>;
//////////////// ////////////////
// exceptions // // exceptions //
//////////////// ////////////////
@@ -612,210 +676,100 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// constructor for rvalue binary arrays (internal type) /// constructor for rvalue binary arrays (internal type)
json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {} json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
private: void destroy(value_t t)
// raw, allocation-free transfer of m_data from src to dst: no
// set_parents()/assert_invariant() (the former is O(#children) per
// call under JSON_DIAGNOSTICS, which would make the walk below
// quadratic); dst takes ownership, src is left as value_t::null.
static void take(basic_json& dst, basic_json& src) noexcept
{
dst.m_data.m_type = src.m_data.m_type;
dst.m_data.m_value = src.m_data.m_value;
src.m_data.m_type = value_t::null;
}
// true if v is not an array/object, or is an already-empty one
static bool has_no_children(const basic_json& v) noexcept
{
switch (v.m_data.m_type)
{
case value_t::array:
return v.m_data.m_value.array->empty();
case value_t::object:
return v.m_data.m_value.object->empty();
default:
return true;
}
}
static basic_json& last_child(basic_json& v)
{
if (v.m_data.m_type == value_t::array)
{
return v.m_data.m_value.array->back();
}
JSON_ASSERT(v.m_data.m_type == value_t::object);
return v.m_data.m_value.object->rbegin()->second;
}
// removes the last child of a non-empty array/object v; this never
// allocates, and since it is only ever called when that child is a
// scalar or an already-empty array/object, destroying it never
// recurses more than one level deep (see destroy() below)
static void pop_last_child(basic_json& v)
{
if (v.m_data.m_type == value_t::array)
{
v.m_data.m_value.array->pop_back();
}
else
{
JSON_ASSERT(v.m_data.m_type == value_t::object);
// erase() needs a forward iterator, so std::prev(end()) is
// used here rather than rbegin() (see last_child() above)
v.m_data.m_value.object->erase(std::prev(v.m_data.m_value.object->end()));
}
}
// deallocates the (already empty) array/object held by v; this is
// the same allocator-based free the old recursive implementation
// used, just factored out so every level of the walk in destroy()
// can share it
static void free_container(basic_json& v) noexcept
{
if (v.m_data.m_type == value_t::array)
{
JSON_ASSERT(v.m_data.m_value.array->empty());
AllocatorType<array_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, v.m_data.m_value.array);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, v.m_data.m_value.array, 1);
}
else
{
JSON_ASSERT(v.m_data.m_type == value_t::object);
JSON_ASSERT(v.m_data.m_value.object->empty());
AllocatorType<object_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, v.m_data.m_value.object);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, v.m_data.m_value.object, 1);
}
v.m_data.m_type = value_t::null; // avoid a double free if v is later destructed
}
public:
void destroy_string() noexcept
{
if (string == nullptr)
{
// not initialized (e.g., due to exception in the ctor)
return;
}
AllocatorType<string_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
}
void destroy_binary() noexcept
{
if (binary == nullptr)
{
// not initialized (e.g., due to exception in the ctor)
return;
}
AllocatorType<binary_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
}
// t must be value_t::array or value_t::object
void destroy_container(value_t t) noexcept
{ {
if ( if (
(t == value_t::object && object == nullptr) || (t == value_t::object && object == nullptr) ||
(t == value_t::array && array == nullptr) (t == value_t::array && array == nullptr) ||
(t == value_t::string && string == nullptr) ||
(t == value_t::binary && binary == nullptr)
) )
{ {
// not initialized (e.g., due to exception in the ctor) // not initialized (e.g., due to exception in the ctor)
return; return;
} }
if (t == value_t::array || t == value_t::object)
// Destroy the tree without recursing per nesting level and
// without any heap allocation: a heap-allocated flattening
// stack (the previous implementation) can itself throw
// bad_alloc, which would escape this noexcept destructor and
// terminate the program (#5135).
//
// Instead, walk down the "last child" chain, reversing links
// as we go: cur is the container currently being emptied,
// and prev is its parent (value_t::null when there is none).
// Each parent's last child slot doubles as storage for that
// parent's own parent link while we are below it, so no
// extra memory is needed. We only ever remove a child once
// it is a scalar or an empty array/object, which neither
// allocates nor recurses more than one level deep.
//
// This json_value is not itself a basic_json, so the
// top-level container is first moved into a local stand-in
// ("cur"); a default-constructed basic_json has a null
// pointer in its m_value (see data::m_value's initializer),
// so swapping it with *this leaves this union's own pointer
// null, and it is never looked at or freed a second time.
basic_json cur;
cur.m_data.m_type = t;
using std::swap;
swap(cur.m_data.m_value, *this);
basic_json prev; // value_t::null: no parent
while (true)
{ {
if (has_no_children(cur)) // flatten the current json_value to a heap-allocated stack
std::vector<basic_json> stack;
// move the top-level items to stack
if (t == value_t::array)
{ {
if (prev.m_data.m_type == value_t::null) stack.reserve(array->size());
std::move(array->begin(), array->end(), std::back_inserter(stack));
}
else
{
stack.reserve(object->size());
for (auto&& it : *object)
{ {
free_container(cur); stack.push_back(std::move(it.second));
return; // back at the top with nothing left to do }
}
while (!stack.empty())
{
// move the last item to a local variable to be processed
basic_json current_item(std::move(stack.back()));
stack.pop_back();
// if current_item is array/object, move
// its children to the stack to be processed later
if (current_item.is_array())
{
std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));
current_item.m_data.m_value.array->clear();
}
else if (current_item.is_object())
{
for (auto&& it : *current_item.m_data.m_value.object)
{
stack.push_back(std::move(it.second));
}
current_item.m_data.m_value.object->clear();
} }
// ascend: detach the grandparent link from prev's // it's now safe that current_item gets destructed
// last slot, drop that (now null) slot, free cur // since it doesn't have any children
// (it is empty), then move up one level
basic_json gp;
take(gp, last_child(prev));
pop_last_child(prev);
free_container(cur);
take(cur, prev);
take(prev, gp);
continue;
} }
basic_json& cur_last_ref = last_child(cur);
if (has_no_children(cur_last_ref))
{
// scalar, or already-empty array/object
pop_last_child(cur);
continue;
}
// descend into the non-empty last child, reversing the
// link: its slot takes over prev, and the child becomes
// the new cur
basic_json tmp;
take(tmp, cur_last_ref);
take(cur_last_ref, prev);
take(prev, cur);
take(cur, tmp);
} }
}
void destroy(value_t t)
{
switch (t) switch (t)
{ {
case value_t::string: case value_t::object:
destroy_string(); {
AllocatorType<object_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
break; break;
}
case value_t::array:
{
AllocatorType<array_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
break;
}
case value_t::string:
{
AllocatorType<string_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
break;
}
case value_t::binary: case value_t::binary:
destroy_binary(); {
break; AllocatorType<binary_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
case value_t::object: std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
case value_t::array:
destroy_container(t);
break; break;
}
case value_t::null: case value_t::null:
case value_t::boolean: case value_t::boolean:
@@ -824,7 +778,9 @@ public:
case value_t::number_float: case value_t::number_float:
case value_t::discarded: case value_t::discarded:
default: default:
{
break; break;
}
} }
} }
}; };
File diff suppressed because it is too large. Load diff
-599
View File
@@ -1,599 +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 <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
+3 -109
View File
@@ -48,14 +48,7 @@ TEST_CASE("bad_alloc")
SECTION("bad_alloc") SECTION("bad_alloc")
{ {
// create JSON type using the throwing allocator // create JSON type using the throwing allocator
using bad_json = nlohmann::basic_json<std::map, using bad_json = nlohmann::json::with_allocator_t<bad_allocator>;
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
bad_allocator>;
// creating an object should throw // creating an object should throw
CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&); CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
@@ -129,14 +122,7 @@ void my_allocator_clean_up(T* p)
TEST_CASE("controlled bad_alloc") TEST_CASE("controlled bad_alloc")
{ {
// create JSON type using the throwing allocator // create JSON type using the throwing allocator
using my_json = nlohmann::basic_json<std::map, using my_json = nlohmann::json::with_allocator_t<my_allocator>;
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
my_allocator>;
SECTION("class json_value") SECTION("class json_value")
{ {
@@ -593,102 +579,10 @@ TEST_CASE("bad my_allocator::construct")
{ {
SECTION("my_allocator::construct doesn't forward") SECTION("my_allocator::construct doesn't forward")
{ {
using bad_alloc_json = nlohmann::basic_json<std::map, using bad_alloc_json = nlohmann::json::with_allocator_t<allocator_no_forward>;
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
allocator_no_forward>;
bad_alloc_json j; bad_alloc_json j;
j["test"] = bad_alloc_json::array_t(); j["test"] = bad_alloc_json::array_t();
j["test"].push_back("should not leak"); j["test"].push_back("should not leak");
} }
} }
namespace
{
std::size_t counting_allocator_allocations = 0;
std::size_t counting_allocator_deallocations = 0;
template<class T>
struct counting_allocator : std::allocator<T>
{
using std::allocator<T>::allocator;
T* allocate(std::size_t n)
{
++counting_allocator_allocations;
return std::allocator<T>::allocate(n);
}
void deallocate(T* p, std::size_t n)
{
++counting_allocator_deallocations;
std::allocator<T>::deallocate(p, n);
}
template <class U>
struct rebind
{
using other = counting_allocator<U>;
};
};
} // namespace
TEST_CASE("destructor performs no allocation, only deallocation")
{
// see https://github.com/nlohmann/json/issues/4842 and
// https://github.com/nlohmann/json/issues/5135: destroying nested
// arrays/objects used to allocate a temporary stack (first with
// std::allocator, later - after #4842 - with the provided allocator).
// Since that stack could itself throw bad_alloc from inside the
// noexcept destructor (#5135), destroy() no longer allocates anything:
// it only ever frees what is already there.
using counting_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
counting_allocator>;
SECTION("array")
{
auto* j = new counting_json({1, {2, {3, 4}}, 5}); // NOLINT(cppcoreguidelines-owning-memory)
const auto allocations_before = counting_allocator_allocations;
const auto deallocations_before = counting_allocator_deallocations;
delete j; // NOLINT(cppcoreguidelines-owning-memory)
CHECK(counting_allocator_allocations == allocations_before);
CHECK(counting_allocator_deallocations > deallocations_before);
}
SECTION("object")
{
auto* j = new counting_json({{"a", {{"b", {1, 2}}}}, {"c", 3}}); // NOLINT(cppcoreguidelines-owning-memory)
const auto allocations_before = counting_allocator_allocations;
const auto deallocations_before = counting_allocator_deallocations;
delete j; // NOLINT(cppcoreguidelines-owning-memory)
CHECK(counting_allocator_allocations == allocations_before);
CHECK(counting_allocator_deallocations > deallocations_before);
}
SECTION("mixed tree of empty/non-empty arrays and objects")
{
auto* j = new counting_json( // NOLINT(cppcoreguidelines-owning-memory)
{
{"empty_obj", counting_json::object()},
{"empty_arr", counting_json::array()},
{"nested", {{"a", counting_json::array({1, 2, counting_json::object()})}, {"b", 3}}},
{"tail", counting_json::array({counting_json::array({1}), 2, counting_json::array({3})})}
});
const auto allocations_before = counting_allocator_allocations;
const auto deallocations_before = counting_allocator_deallocations;
delete j; // NOLINT(cppcoreguidelines-owning-memory)
CHECK(counting_allocator_allocations == allocations_before);
CHECK(counting_allocator_deallocations > deallocations_before);
}
}
+1 -10
View File
@@ -163,16 +163,7 @@ void int_to_string(alt_string& target, std::size_t value)
target = std::to_string(value).c_str(); target = std::to_string(value).c_str();
} }
using alt_json = nlohmann::basic_json < using alt_json = nlohmann::json::with_string_t<alt_string>;
std::map,
std::vector,
alt_string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer >;
bool operator<(const char* op1, const alt_string& op2) noexcept bool operator<(const char* op1, const alt_string& op2) noexcept
{ {
+3 -52
View File
@@ -114,59 +114,10 @@ TEST_CASE("BJData")
{ {
SECTION("discarded") SECTION("discarded")
{ {
// a discarded value cannot be serialized to BJData // discarded values are not serialized
json const j = json::value_t::discarded; json const j = json::value_t::discarded;
CHECK_THROWS_WITH_AS(json::to_bjdata(j), "[json.exception.type_error.321] cannot serialize discarded value to BJData", json::type_error&); const auto result = json::to_bjdata(j);
} CHECK(result.empty());
SECTION("discarded values nested in a container")
{
json const discarded = json::value_t::discarded;
SECTION("in an array")
{
json const j = {1, discarded, 2};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_bjdata(j), "[json.exception.type_error.321] (/1) cannot serialize discarded value to BJData", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_bjdata(j), "[json.exception.type_error.321] cannot serialize discarded value to BJData", json::type_error&);
#endif
}
SECTION("as an object value")
{
json j;
j["a"] = 1;
j["b"] = discarded;
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_bjdata(j), "[json.exception.type_error.321] (/b) cannot serialize discarded value to BJData", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_bjdata(j), "[json.exception.type_error.321] cannot serialize discarded value to BJData", json::type_error&);
#endif
}
SECTION("nested deeper (array in object in array)")
{
json inner_array = {1, discarded};
json middle_object;
middle_object["x"] = inner_array;
json const j = {middle_object};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_bjdata(j), "[json.exception.type_error.321] (/0/x/1) cannot serialize discarded value to BJData", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_bjdata(j), "[json.exception.type_error.321] cannot serialize discarded value to BJData", json::type_error&);
#endif
}
SECTION("optimized array of all-discarded elements")
{
json const j = {discarded, discarded};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_bjdata(j, true, true), "[json.exception.type_error.321] (/0) cannot serialize discarded value to BJData", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_bjdata(j, true, true), "[json.exception.type_error.321] cannot serialize discarded value to BJData", json::type_error&);
#endif
}
} }
SECTION("null") SECTION("null")
-48
View File
@@ -149,54 +149,6 @@ TEST_CASE("BSON")
json const j = std::vector<int> {1, 2, 3, 4, 5, 6, 7}; json const j = std::vector<int> {1, 2, 3, 4, 5, 6, 7};
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is array", json::type_error&); CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is array", json::type_error&);
} }
SECTION("discarded")
{
json const j = json::value_t::discarded;
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is discarded", json::type_error&);
}
}
SECTION("discarded values nested in a container cannot be serialized to BSON")
{
json const discarded = json::value_t::discarded;
SECTION("as an object value")
{
json j;
j["a"] = 1;
j["b"] = discarded;
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.321] (/b) cannot serialize discarded value to BSON", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.321] cannot serialize discarded value to BSON", json::type_error&);
#endif
}
SECTION("in an array that is an object value")
{
json j;
j["a"] = json::array({1, discarded, 2});
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.321] (/a/1) cannot serialize discarded value to BSON", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.321] cannot serialize discarded value to BSON", json::type_error&);
#endif
}
SECTION("nested deeper (array in object in object)")
{
json inner_array = {1, discarded};
json middle_object;
middle_object["x"] = inner_array;
json j;
j["outer"] = middle_object;
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.321] (/outer/x/1) cannot serialize discarded value to BSON", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.321] cannot serialize discarded value to BSON", json::type_error&);
#endif
}
} }
SECTION("keys containing code-point U+0000 cannot be serialized to BSON") SECTION("keys containing code-point U+0000 cannot be serialized to BSON")
+3 -42
View File
@@ -38,49 +38,10 @@ TEST_CASE("CBOR")
{ {
SECTION("discarded") SECTION("discarded")
{ {
// a discarded value cannot be serialized to CBOR // discarded values are not serialized
json const j = json::value_t::discarded; json const j = json::value_t::discarded;
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.321] cannot serialize discarded value to CBOR", json::type_error&); const auto result = json::to_cbor(j);
} CHECK(result.empty());
SECTION("discarded values nested in a container")
{
json const discarded = json::value_t::discarded;
SECTION("in an array")
{
json const j = {1, discarded, 2};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.321] (/1) cannot serialize discarded value to CBOR", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.321] cannot serialize discarded value to CBOR", json::type_error&);
#endif
}
SECTION("as an object value")
{
json j;
j["a"] = 1;
j["b"] = discarded;
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.321] (/b) cannot serialize discarded value to CBOR", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.321] cannot serialize discarded value to CBOR", json::type_error&);
#endif
}
SECTION("nested deeper (array in object in array)")
{
json inner_array = {1, discarded};
json middle_object;
middle_object["x"] = inner_array;
json const j = {middle_object};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.321] (/0/x/1) cannot serialize discarded value to CBOR", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.321] cannot serialize discarded value to CBOR", json::type_error&);
#endif
}
} }
SECTION("NaN") SECTION("NaN")
+106 -553
View File
@@ -13,19 +13,15 @@
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 <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
@@ -261,7 +257,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
// Eisel-Lemire path beyond the Clinger subset // std::from_chars (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"
}; };
@@ -283,18 +279,20 @@ TEST_CASE("lexer number fast path")
} }
} }
SECTION("significant digits around Clinger's fast path") SECTION("significant-digit gate for the Clinger fast path")
{ {
// Clinger's fast path needs a significand of at most 2^53, which // Clinger's fast path needs a significand below 2^53, so it cannot
// tokens with 17 or more significant digits exceed. The conversion // succeed once the mantissa has 17 or more significant digits (the
// splits the token at the positions the scanners recorded, so leading // significand would be at least 10^16). The lexer skips the attempt
// zeros must not count as digits - "0.1234567890123456" has 16 // there. That is only allowed to save work: every value must still come
// significant digits, not 17 - and both scanners must agree. // out bit-exactly, and both scanners must agree. In particular the gate
// 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 "12345678901234567", // 17 -> attempt skipped
"123456789012345678", // 18 "123456789012345678", // 18 -> attempt skipped
"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
@@ -665,323 +663,46 @@ TEST_CASE("lexer string fast path")
} }
} }
TEST_CASE("lexer escape fast path") TEST_CASE("parse_float_fast declines what it cannot convert exactly")
{ {
// json::accept() never throws, so this section stays covered without // The lexer only hands well-formed numbers to parse_float_fast, so the
// exceptions; it pins which of the cases below are valid/invalid and // malformed ones below can only be passed to it directly. Declining is
// checks the contiguous and streaming paths agree on that classification. // always safe: the caller then falls back to a slower, exact conversion.
SECTION("accept() parity") const auto fast = [](const std::string & s, double & out)
{ {
const std::vector<std::pair<std::string, bool>> cases = return nlohmann::detail::parse_float_fast(s.data(), s.data() + s.size(), out);
{
{"\\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}
};
for (const auto& c : cases)
{
for (const std::size_t offset :
{
std::size_t{0}, std::size_t{9}
})
{
const std::string doc = "[\"" + std::string(offset, 'a') + c.first + "\"]";
CAPTURE(doc)
CHECK(json::accept(doc) == c.second);
std::stringstream ss(doc);
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()};
}
}; };
double out = 0;
SECTION("contiguous vs streaming parity") #if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
{ // without true double precision, the fast path declines everything
const std::vector<std::string> escapes = CHECK_FALSE(fast("1.5", out));
{ #else
"\\u0041", // "A" CHECK(fast("1.5", out));
"\\u00e4", // "ä" (lowercase hex) CHECK(out == 1.5);
"\\u00E4", // "ä" (uppercase hex) CHECK(fast("+2.5e1", out));
"\\uD83D\\uDE00", // valid surrogate pair (an emoji) CHECK(out == 25.0);
"\\u12", // truncated: only 2 hex digits before the closing quote CHECK(fast("-25E-1", out));
"\\u12G4", // invalid hex digit at the 3rd position CHECK(out == -2.5);
"\\uXYZW", // all 4 bytes invalid CHECK(fast("1e", out));
"\\uD800", // lone high surrogate, string ends right after CHECK(out == 1.0);
"\\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)
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
}
namespace // not a number
{ 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(".", out));
std::pair<std::size_t, std::size_t> float_token_layout(const std::string& s) CHECK_FALSE(fast("1.2.3", out));
{ CHECK_FALSE(fast("1x", out));
std::size_t dot = std::string::npos; CHECK_FALSE(fast("1e+", out));
std::size_t mantissa_end = s.size(); CHECK_FALSE(fast("1e1x", out));
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};
}
template<typename FloatType> // numbers that are not represented exactly on the fast path
FloatType parse_native(const std::string& s) CHECK_FALSE(fast("12345678901234567890", out));
{ CHECK_FALSE(fast("1e10000", out));
const auto layout = float_token_layout(s); CHECK_FALSE(fast("9007199254740993", out));
return nlohmann::detail::parse_float_native<FloatType>(s.data(), s.data() + s.size(), layout.first, layout.second); CHECK_FALSE(fast("1e23", out));
} 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
@@ -1085,6 +806,40 @@ 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")
@@ -1482,33 +1237,26 @@ TEST_CASE("Eisel-Lemire float conversion")
for (const auto& c : known) for (const auto& c : known)
{ {
CAPTURE(c.first) CAPTURE(c.first)
CHECK(native_bits64(c.first) == c.second); double out = 0;
if (eisel_lemire(c.first, out))
{
CHECK(bits_of(out) == c.second);
}
else
{
// only tokens with more than 19 significant digits are left to
// strtod: those whose value lies too close to a tie
CHECK(significant_digits(c.first) > 19);
}
} }
} }
SECTION("binary32")
{
using binary32 = nlohmann::detail::ieee_binary_format<24>;
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 1) == 0x3F800000u);
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 1) == 0x3DCCCCCDu);
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 15) == 0x3FC00000u);
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777217) == 0x4B800000u); // tie, to even
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777219) == 0x4B800002u); // tie, to even
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, and its 17-digit // every double written by to_chars and read back, also with trailing
// form with trailing digits that make the token longer than 19 digits // 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;
@@ -1530,51 +1278,30 @@ 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)
CHECK(native_bits64(token) == b); double out = 0;
REQUIRE(eisel_lemire(token, out));
CHECK(bits_of(out) == b);
// insert digits before the exponent of the 17-digit form: that // insert digits before the exponent: the value moves by far less
// form lies strictly inside the rounding interval of the double // than the distance to the rounding boundary, so it must not change
// (the shortest one may lie on its boundary), and the digits move std::string longer = token;
// 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)
CHECK(native_bits64(longer) == b); if (eisel_lemire(longer, out))
}
}
SECTION("round trip, binary32")
{
std::uint32_t state = 5295;
for (int i = 0; i < 100000; ++i)
{
state ^= state << 13u;
state ^= state >> 17u;
state ^= state << 5u;
std::uint32_t b = state;
if ((b & 0x7F800000u) == 0x7F800000u)
{ {
continue; // infinity or NaN CHECK(bits_of(out) == b);
} }
if (i % 4 == 0) else
{ {
b &= 0x807FFFFFu; // subnormals // w and w + 1 round differently: only when the value is very
// close to a rounding boundary
++declined;
} }
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")
@@ -1588,177 +1315,3 @@ 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);
}
}
-52
View File
@@ -2317,58 +2317,6 @@ TEST_CASE("parser class")
#endif #endif
} }
SECTION("comments before separators")
{
// The parser first checks for the expected ':' or ',' and only then
// falls back to the full token switch, which skips comments. A comment
// directly before a separator takes that fallback.
json _;
SECTION("ignored")
{
const std::vector<std::pair<std::string, json>> inputs =
{
{"{\"a\" /* c */ : 1}", {{"a", 1}}},
{"{\"a\" // c\n: 1}", {{"a", 1}}},
{R"({"a": 1, "b" /* c */ : 2})", {{"a", 1}, {"b", 2}}},
{R"({"a": 1 /* c */ , "b": 2})", {{"a", 1}, {"b", 2}}},
{"{\"a\": 1 // c\n, \"b\": 2}", {{"a", 1}, {"b", 2}}},
{"[1 /* c */ , 2]", {1, 2}},
{"[1 // c\n, 2]", {1, 2}},
{"{\"a\" /* c */ /* d */ : [1 // c\n , 2 /**/ ] /**/ , \"b\" : 3}", {{"a", {1, 2}}, {"b", 3}}}
};
for (const auto& input : inputs)
{
CAPTURE(input.first)
CHECK(json::parse(input.first, nullptr, true, true) == input.second);
CHECK(json::accept(input.first, true));
}
}
SECTION("ignored, with trailing commas")
{
CHECK(json::parse(std::string("[1 /* c */ , ]"), nullptr, true, true, true) == json({1}));
CHECK(json::parse(std::string("{\"a\": 1 /* c */ , }"), nullptr, true, true, true) == json({{"a", 1}}));
CHECK_THROWS_WITH_AS(_ = json::parse(std::string("[1 /* c */ , ]"), nullptr, true, true),
"[json.exception.parse_error.101] parse error at line 1, column 14: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal", json::parse_error);
CHECK_THROWS_WITH_AS(_ = json::parse(std::string("{\"a\": 1 /* c */ , }"), nullptr, true, true),
"[json.exception.parse_error.101] parse error at line 1, column 19: syntax error while parsing object key - unexpected '}'; expected string literal", json::parse_error);
}
SECTION("not ignored")
{
CHECK_THROWS_WITH_AS(_ = json::parse(std::string("{\"a\" /* c */ : 1}")),
"[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing object separator - invalid literal; last read: '\"a\" /'; expected ':'", json::parse_error);
CHECK_THROWS_WITH_AS(_ = json::parse(std::string("{\"a\": 1, \"b\" /* c */ : 2}")),
"[json.exception.parse_error.101] parse error at line 1, column 14: syntax error while parsing object separator - invalid literal; last read: '\"b\" /'; expected ':'", json::parse_error);
CHECK_THROWS_WITH_AS(_ = json::parse(std::string("{\"a\": 1 /* c */ , \"b\": 2}")),
"[json.exception.parse_error.101] parse error at line 1, column 9: syntax error while parsing object - invalid literal; last read: '1 /'; expected '}'", json::parse_error);
CHECK_THROWS_WITH_AS(_ = json::parse(std::string("[1 /* c */ , 2]")),
"[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing array - invalid literal; last read: '1 /'; expected ']'", json::parse_error);
CHECK(!json::accept(std::string("[1 /* c */ , 2]")));
}
}
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Macro for all test cases for start_pos and end_pos // Macro for all test cases for start_pos and end_pos
#define SETUP_TESTCASES() \ #define SETUP_TESTCASES() \
+2 -27
View File
@@ -38,20 +38,7 @@ class json_metadata
}; };
template<class T> template<class T>
using json_with_metadata = using json_with_metadata = nlohmann::json::with_base_class_t<json_metadata<T>>;
nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
json_metadata<T>
>;
TEST_CASE("JSON Node Metadata") TEST_CASE("JSON Node Metadata")
{ {
@@ -268,19 +255,7 @@ class visitor_adaptor
void do_visit(const Ptr& ptr, const Fnc& fnc) const; void do_visit(const Ptr& ptr, const Fnc& fnc) const;
}; };
using json_with_visitor_t = nlohmann::basic_json < using json_with_visitor_t = nlohmann::json::with_base_class_t<visitor_adaptor>;
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
visitor_adaptor
>;
template <class Fnc> template <class Fnc>
void visitor_adaptor::visit(const Fnc& fnc) const void visitor_adaptor::visit(const Fnc& fnc) const
+8 -25
View File
@@ -260,11 +260,10 @@ 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)")
{ {
// float and double are converted without the locale. A long double that // The numbers are chosen so that the conversion also takes the strtod
// is not binary64 can take the strtold fallback, which honors the locale // fallback, which honors the locale that is current at conversion time:
// that is current at conversion time. The numbers are chosen so that it // too many significant digits for Clinger's fast path, an underflow that
// does: too many significant digits for Clinger's fast path, an underflow // std::from_chars rejects, and a plain value.
// 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)
@@ -328,8 +327,7 @@ TEST_CASE("locale changes between lexer construction and number conversion (#519
} }
} }
// a long double goes through std::strtold unless it is binary64 or // a long double goes through std::strtold unless std::from_chars supports it
// 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
@@ -355,15 +353,8 @@ 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 strtold fallback (only for long // substituted in place for '.', so the strtod fallback stops early. The
// double formats other than binary64) converts a copy of the token with // conversion must still terminate rather than retry forever.
// 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)
@@ -381,20 +372,12 @@ 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: double does not depend on the locale // that std::from_chars rejects: both reach the strtod fallback
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);
} }
+3 -42
View File
@@ -41,49 +41,10 @@ TEST_CASE("MessagePack")
{ {
SECTION("discarded") SECTION("discarded")
{ {
// a discarded value cannot be serialized to MessagePack // discarded values are not serialized
json const j = json::value_t::discarded; json const j = json::value_t::discarded;
CHECK_THROWS_WITH_AS(json::to_msgpack(j), "[json.exception.type_error.321] cannot serialize discarded value to MessagePack", json::type_error&); const auto result = json::to_msgpack(j);
} CHECK(result.empty());
SECTION("discarded values nested in a container")
{
json const discarded = json::value_t::discarded;
SECTION("in an array")
{
json const j = {1, discarded, 2};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_msgpack(j), "[json.exception.type_error.321] (/1) cannot serialize discarded value to MessagePack", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_msgpack(j), "[json.exception.type_error.321] cannot serialize discarded value to MessagePack", json::type_error&);
#endif
}
SECTION("as an object value")
{
json j;
j["a"] = 1;
j["b"] = discarded;
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_msgpack(j), "[json.exception.type_error.321] (/b) cannot serialize discarded value to MessagePack", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_msgpack(j), "[json.exception.type_error.321] cannot serialize discarded value to MessagePack", json::type_error&);
#endif
}
SECTION("nested deeper (array in object in array)")
{
json inner_array = {1, discarded};
json middle_object;
middle_object["x"] = inner_array;
json const j = {middle_object};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_msgpack(j), "[json.exception.type_error.321] (/0/x/1) cannot serialize discarded value to MessagePack", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_msgpack(j), "[json.exception.type_error.321] cannot serialize discarded value to MessagePack", json::type_error&);
#endif
}
} }
SECTION("null") SECTION("null")
-287
View File
@@ -40,9 +40,7 @@ using ordered_json = nlohmann::ordered_json;
#endif #endif
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <list> #include <list>
#include <new>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@@ -109,84 +107,6 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>; using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
namespace
{
// An allocator whose allocate() can be told to fail on demand, so tests can
// check that ~basic_json() tolerates - in fact, after #5135, never even
// triggers - an allocation failure. This replaces an earlier version of
// this test that overrode the process-wide ::operator new/::operator
// delete, which affected every allocation in the whole unit-regression2
// binary rather than just the values under test.
std::size_t failing_allocator_allocations = 0;
std::size_t failing_allocator_deallocations = 0;
bool fail_next_allocation = false;
template<class T>
struct failing_allocator : std::allocator<T>
{
using std::allocator<T>::allocator;
failing_allocator() noexcept = default;
template<class U>
failing_allocator(const failing_allocator<U>& /*unused*/) noexcept {} // NOLINT(google-explicit-constructor)
T* allocate(std::size_t n)
{
if (fail_next_allocation)
{
fail_next_allocation = false;
throw std::bad_alloc();
}
++failing_allocator_allocations;
return std::allocator<T>::allocate(n);
}
void deallocate(T* p, std::size_t n)
{
++failing_allocator_deallocations;
std::allocator<T>::deallocate(p, n);
}
template<class U>
struct rebind
{
using other = failing_allocator<U>;
};
};
using failing_json = nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, failing_allocator>;
using failing_ordered_json = nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, failing_allocator>;
// builds `depth` levels of nesting around a scalar, iteratively (never
// recursing: each wrap only moves the previous, already-built value, which
// is O(1)), each level an array or an object depending on `nest_objects`
template<class BasicJsonType>
BasicJsonType make_deep_nest(std::size_t depth, bool nest_objects)
{
BasicJsonType v = 0;
for (std::size_t i = 0; i < depth; ++i)
{
if (nest_objects)
{
BasicJsonType wrapper = BasicJsonType::object();
wrapper["x"] = std::move(v);
v = std::move(wrapper);
}
else
{
BasicJsonType wrapper = BasicJsonType::array();
wrapper.push_back(std::move(v));
v = std::move(wrapper);
}
}
return v;
}
} // namespace
#endif
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// for #1647 // for #1647
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
@@ -1020,211 +940,4 @@ TEST_CASE("regression test - excessive binary container size honors allow_except
CHECK(json::from_cbor(std::vector<std::uint8_t> {0x9b, 0, 0, 0, 0, 0, 0, 0, 0x02}, true, false).is_discarded()); CHECK(json::from_cbor(std::vector<std::uint8_t> {0x9b, 0, 0, 0, 0, 0, 0, 0, 0x02}, true, false).is_discarded());
} }
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
TEST_CASE("regression test #5135 - destructor never allocates, even under memory pressure")
{
// Before the fix, ~basic_json() flattened a nested array/object into a
// heap-allocated std::vector to avoid recursing; that allocation could
// itself throw bad_alloc, which escapes a noexcept destructor and
// terminates the program. destroy() no longer allocates anything, so
// none of the sections below ever observe fail_next_allocation being
// consumed: CHECK(fail_next_allocation) confirms it was never touched.
SECTION("the original report: a small, mixed array/object nest")
{
failing_allocator_allocations = 0;
failing_allocator_deallocations = 0;
{
failing_json j = failing_json::array(
{
failing_json::array({1, 2}),
failing_json::object({{"key", failing_json::array({3})}})
});
fail_next_allocation = true;
} // j is destroyed here, with every further allocation set to fail
CHECK(fail_next_allocation);
fail_next_allocation = false;
CHECK(failing_allocator_deallocations > 0);
}
SECTION("100000-deep nested array")
{
std::size_t allocations_before = 0;
{
failing_json j = make_deep_nest<failing_json>(100000, false);
allocations_before = failing_allocator_allocations;
fail_next_allocation = true;
}
CHECK(fail_next_allocation);
fail_next_allocation = false;
CHECK(failing_allocator_allocations == allocations_before);
}
SECTION("100000-deep nested object")
{
std::size_t allocations_before = 0;
{
failing_json j = make_deep_nest<failing_json>(100000, true);
allocations_before = failing_allocator_allocations;
fail_next_allocation = true;
}
CHECK(fail_next_allocation);
fail_next_allocation = false;
CHECK(failing_allocator_allocations == allocations_before);
}
SECTION("100000-deep nested ordered_json")
{
std::size_t allocations_before = 0;
{
failing_ordered_json j = make_deep_nest<failing_ordered_json>(100000, true);
allocations_before = failing_allocator_allocations;
fail_next_allocation = true;
}
CHECK(fail_next_allocation);
fail_next_allocation = false;
CHECK(failing_allocator_allocations == allocations_before);
}
SECTION("wide and deep: 1000 arrays of 1000 elements, each a small nested object")
{
std::size_t allocations_before = 0;
{
failing_json wide = failing_json::array();
for (std::size_t i = 0; i < 1000; ++i)
{
failing_json inner = failing_json::array();
for (std::size_t k = 0; k < 1000; ++k)
{
inner.push_back(failing_json::object({{"a", 1}, {"b", failing_json::array({1, 2, 3})}}));
}
wide.push_back(std::move(inner));
}
allocations_before = failing_allocator_allocations;
fail_next_allocation = true;
}
CHECK(fail_next_allocation);
fail_next_allocation = false;
CHECK(failing_allocator_allocations == allocations_before);
}
}
#endif
namespace
{
// a single-element chain of `depth` arrays, built iteratively (never
// recursing: each wrap only moves the previous, already-built value)
template<class BasicJsonType>
BasicJsonType make_single_chain(std::size_t depth)
{
BasicJsonType v = 1;
for (std::size_t i = 0; i < depth; ++i)
{
BasicJsonType wrapper = BasicJsonType::array();
wrapper.push_back(std::move(v));
v = std::move(wrapper);
}
return v;
}
// copies value first, to make sure nothing was corrupted by building it,
// then lets both the copy and the original destruct via normal scope exit
template<class BasicJsonType>
void check_destroy_edge_case(const BasicJsonType& value)
{
const BasicJsonType copy = value;
CHECK(copy == value);
}
} // namespace
TEST_CASE_TEMPLATE("regression test #5135 - destroy() edge cases", BasicJsonType, json, ordered_json)
{
using binary_t = typename BasicJsonType::binary_t;
SECTION("mix of empty objects, empty arrays, non-empty containers, and scalars")
{
BasicJsonType root = BasicJsonType::array();
root.push_back(BasicJsonType::object());
root.push_back(BasicJsonType::array());
root.push_back(BasicJsonType::object({{"k", 1}}));
root.push_back(BasicJsonType::array({1, 2, 3}));
root.push_back(nullptr);
root.push_back(true);
root.push_back(42);
root.push_back(3.14);
root.push_back("a string");
root.push_back(BasicJsonType(binary_t({1, 2, 3})));
check_destroy_edge_case(root);
}
SECTION("container child in first position only")
{
BasicJsonType root = BasicJsonType::array({BasicJsonType::array({1, 2}), 3, 4, 5});
check_destroy_edge_case(root);
}
SECTION("container child in last position only")
{
BasicJsonType root = BasicJsonType::array({1, 2, 3, BasicJsonType::array({4, 5})});
check_destroy_edge_case(root);
}
SECTION("container children in first and last position")
{
BasicJsonType root = BasicJsonType::array({BasicJsonType::array({1}), 2, 3, BasicJsonType::array({4})});
check_destroy_edge_case(root);
}
SECTION("single-element chain, 1000 levels deep")
{
BasicJsonType root = make_single_chain<BasicJsonType>(1000);
check_destroy_edge_case(root);
}
SECTION("top-level empty array")
{
BasicJsonType root = BasicJsonType::array();
check_destroy_edge_case(root);
}
SECTION("top-level empty object")
{
BasicJsonType root = BasicJsonType::object();
check_destroy_edge_case(root);
}
SECTION("object whose last child is a non-empty array whose last child is an empty object")
{
BasicJsonType inner_array = BasicJsonType::array({1, 2, BasicJsonType::object()});
BasicJsonType root = BasicJsonType::object({{"a", 1}, {"b", inner_array}});
check_destroy_edge_case(root);
}
SECTION("destruction via erase() on a deeply nested child")
{
BasicJsonType root = BasicJsonType::array();
root.push_back(make_single_chain<BasicJsonType>(500));
root.push_back(BasicJsonType::object({{"k", BasicJsonType::array({1, 2, 3})}}));
// erase() must destroy the removed subtree without recursing or
// allocating beyond what erase() itself needs
root.erase(0);
CAPTURE(root.size())
CHECK(root.size() == 1);
}
SECTION("destruction via assignment on a deep tree")
{
BasicJsonType root = make_single_chain<BasicJsonType>(2000);
// assigning a new value destroys the old one in place
root = nullptr;
CHECK(root.is_null());
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_CLANG_SUPPRESS_WARNING_POP
+5 -59
View File
@@ -35,59 +35,10 @@ TEST_CASE("UBJSON")
{ {
SECTION("discarded") SECTION("discarded")
{ {
// a discarded value cannot be serialized to UBJSON // discarded values are not serialized
json const j = json::value_t::discarded; json const j = json::value_t::discarded;
CHECK_THROWS_WITH_AS(json::to_ubjson(j), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error&); const auto result = json::to_ubjson(j);
} CHECK(result.empty());
SECTION("discarded values nested in a container")
{
json const discarded = json::value_t::discarded;
SECTION("in an array")
{
json const j = {1, discarded, 2};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_ubjson(j), "[json.exception.type_error.321] (/1) cannot serialize discarded value to UBJSON", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_ubjson(j), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error&);
#endif
}
SECTION("as an object value")
{
json j;
j["a"] = 1;
j["b"] = discarded;
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_ubjson(j), "[json.exception.type_error.321] (/b) cannot serialize discarded value to UBJSON", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_ubjson(j), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error&);
#endif
}
SECTION("nested deeper (array in object in array)")
{
json inner_array = {1, discarded};
json middle_object;
middle_object["x"] = inner_array;
json const j = {middle_object};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_ubjson(j), "[json.exception.type_error.321] (/0/x/1) cannot serialize discarded value to UBJSON", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_ubjson(j), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error&);
#endif
}
SECTION("optimized array of all-discarded elements")
{
json const j = {discarded, discarded};
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json::to_ubjson(j, true, true), "[json.exception.type_error.321] (/0) cannot serialize discarded value to UBJSON", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json::to_ubjson(j, true, true), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error&);
#endif
}
} }
SECTION("null") SECTION("null")
@@ -2148,14 +2099,9 @@ TEST_CASE("UBJSON")
SECTION("discarded") SECTION("discarded")
{ {
// a discarded value cannot be serialized to UBJSON, even as part
// of an optimized array of a single (here: valueless) type
json const j = {json::value_t::discarded, json::value_t::discarded}; json const j = {json::value_t::discarded, json::value_t::discarded};
#if JSON_DIAGNOSTICS std::vector<uint8_t> expected = {'[', '$', 'N', '#', 'i', 2};
CHECK_THROWS_WITH_AS(json::to_ubjson(j, true, true), "[json.exception.type_error.321] (/0) cannot serialize discarded value to UBJSON", json::type_error&); CHECK(json::to_ubjson(j, true, true) == expected);
#else
CHECK_THROWS_WITH_AS(json::to_ubjson(j, true, true), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error&);
#endif
} }
} }
} }
+77 -3
View File
@@ -23,6 +23,7 @@ using nlohmann::json;
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif #endif
#include <deque>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -684,8 +685,7 @@ static std::ostream& operator<<(std::ostream& os, small_pod l)
TEST_CASE("custom serializer for pods" * doctest::test_suite("udt")) TEST_CASE("custom serializer for pods" * doctest::test_suite("udt"))
{ {
using custom_json = using custom_json =
nlohmann::basic_json<std::map, std::vector, std::string, bool, nlohmann::json::with_json_serializer_t<pod_serializer>;
std::int64_t, std::uint64_t, double, std::allocator, pod_serializer>;
auto p = udt::small_pod{42, '/', 42}; auto p = udt::small_pod{42, '/', 42};
custom_json const j = p; custom_json const j = p;
@@ -703,7 +703,7 @@ TEST_CASE("custom serializer for pods" * doctest::test_suite("udt"))
template <typename T, typename> template <typename T, typename>
struct another_adl_serializer; struct another_adl_serializer;
using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>; using custom_json = nlohmann::json::with_json_serializer_t<another_adl_serializer>;
template <typename T, typename> template <typename T, typename>
struct another_adl_serializer struct another_adl_serializer
@@ -734,6 +734,80 @@ TEST_CASE("custom serializer that does adl by default" * doctest::test_suite("ud
CHECK(me == cj.get<udt::person>()); CHECK(me == cj.get<udt::person>());
} }
TEST_CASE("with_*_t aliases" * doctest::test_suite("udt"))
{
// a custom base class used to check with_base_class_t
struct custom_base_class {};
CHECK(std::is_same<json::with_object_t<std::deque>,
nlohmann::basic_json<std::deque, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_array_t<std::deque>,
nlohmann::basic_json<std::map, std::deque, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_string_t<std::wstring>,
nlohmann::basic_json<std::map, std::vector, std::wstring, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_boolean_t<int>,
nlohmann::basic_json<std::map, std::vector, std::string, int,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_integers_t<std::int32_t, std::uint32_t>,
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int32_t, std::uint32_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_float_t<float>,
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, float, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_allocator_t<std::allocator>,
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_json_serializer_t<nlohmann::adl_serializer>,
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_binary_t<std::vector<char>>,
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<char>>>::value);
CHECK(std::is_same<json::with_base_class_t<custom_base_class>,
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>, custom_base_class>>::value);
// with_string_t on ordered_json must keep ordered_map as the object type
CHECK(std::is_same<nlohmann::ordered_json::with_string_t<std::wstring>,
nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::wstring, bool,
std::int64_t, std::uint64_t, double, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
// the aliases are members of the resulting type, so they can be chained
CHECK(std::is_same<json::with_integers_t<int, unsigned int>::with_float_t<float>,
nlohmann::basic_json<std::map, std::vector, std::string, bool,
int, unsigned int, float, std::allocator,
nlohmann::adl_serializer, std::vector<std::uint8_t>>>::value);
CHECK(std::is_same<json::with_float_t<float>::with_integers_t<int, unsigned int>,
json::with_integers_t<int, unsigned int>::with_float_t<float>>::value);
// replacing the object type of json with ordered_map yields ordered_json
CHECK(std::is_same<json::with_object_t<nlohmann::ordered_map>, nlohmann::ordered_json>::value);
CHECK(std::is_same<nlohmann::ordered_json::with_object_t<std::map>, json>::value);
}
TEST_CASE("different basic_json types conversions") TEST_CASE("different basic_json types conversions")
{ {
SECTION("null") SECTION("null")