mirror of
https://github.com/nlohmann/json.git
synced 2026-10-02 12:40:32 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
809ace26b3 | ||
|
|
92c729f657 | ||
|
|
f3e30fa5b7 | ||
|
|
73d116547a |
@@ -61,6 +61,7 @@ option(JSON_Install "Install CMake targets during install
|
|||||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
||||||
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
||||||
option(JSON_StrictNulHandling "Build with strict NUL-byte handling enabled." OFF)
|
option(JSON_StrictNulHandling "Build with strict NUL-byte handling enabled." OFF)
|
||||||
|
option(JSON_StrictBinaryUTF8 "Build with UTF-8 checks in the CBOR, UBJSON, BJData, and BSON writers enabled." OFF)
|
||||||
|
|
||||||
if (JSON_CI)
|
if (JSON_CI)
|
||||||
include(ci)
|
include(ci)
|
||||||
@@ -118,6 +119,10 @@ if (JSON_StrictNulHandling)
|
|||||||
message(STATUS "Strict NUL-byte handling enabled (JSON_STRICT_NUL_HANDLING=1)")
|
message(STATUS "Strict NUL-byte handling enabled (JSON_STRICT_NUL_HANDLING=1)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (JSON_StrictBinaryUTF8)
|
||||||
|
message(STATUS "Strict UTF-8 checks in binary writers enabled (JSON_STRICT_BINARY_UTF8=1)")
|
||||||
|
endif()
|
||||||
|
|
||||||
if (JSON_Diagnostic_Positions)
|
if (JSON_Diagnostic_Positions)
|
||||||
message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)")
|
message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)")
|
||||||
endif()
|
endif()
|
||||||
@@ -153,6 +158,7 @@ target_compile_definitions(
|
|||||||
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
||||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||||
$<$<BOOL:${JSON_StrictNulHandling}>:JSON_STRICT_NUL_HANDLING=1>
|
$<$<BOOL:${JSON_StrictNulHandling}>:JSON_STRICT_NUL_HANDLING=1>
|
||||||
|
$<$<BOOL:${JSON_StrictBinaryUTF8}>:JSON_STRICT_BINARY_UTF8=1>
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
|
|||||||
+1
-1
@@ -691,7 +691,7 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
|
|||||||
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
||||||
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
||||||
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
||||||
JSON_StrictNulHandling)
|
JSON_StrictNulHandling JSON_StrictBinaryUTF8)
|
||||||
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||||
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
||||||
is false.
|
is false.
|
||||||
|
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is not
|
||||||
|
valid UTF-8 and [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled; otherwise, the bytes are
|
||||||
|
written unchanged
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -89,4 +92,6 @@ Linear in the size of the JSON value `j`.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.11.0.
|
- Added in version 3.11.0.
|
||||||
- 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.
|
||||||
|
- Throwing `type_error.316` for a string or object key that is not valid UTF-8 if
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled added in version 3.13.0.
|
||||||
@@ -46,6 +46,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
||||||
exceeds 255, the maximum of the BSON binary subtype; example:
|
exceeds 255, the maximum of the BSON binary subtype; example:
|
||||||
`"subtype 70000 is too large for the BSON binary subtype (max 255)"`
|
`"subtype 70000 is too large for the BSON binary subtype (max 255)"`
|
||||||
|
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key is not valid
|
||||||
|
UTF-8 and [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled; otherwise, the bytes are
|
||||||
|
written unchanged
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -82,3 +85,6 @@ pass before anything is written.
|
|||||||
- Added in version 3.4.0.
|
- Added in version 3.4.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.
|
||||||
|
- Throwing `type_error.316` for a string value or object key that is not valid UTF-8 if
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled, detected before anything is written,
|
||||||
|
added in version 3.13.0.
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is not
|
||||||
|
valid UTF-8 and [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled; otherwise, the bytes are
|
||||||
|
written unchanged
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the size of the JSON value `j`.
|
Linear in the size of the JSON value `j`.
|
||||||
@@ -68,3 +74,5 @@ Linear in the size of the JSON value `j`.
|
|||||||
|
|
||||||
- Added in version 2.0.9.
|
- Added in version 2.0.9.
|
||||||
- Compact representation of floating-point numbers added in version 3.8.0.
|
- Compact representation of floating-point numbers added in version 3.8.0.
|
||||||
|
- Throwing `type_error.316` for a string or object key that is not valid UTF-8 if
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled added in version 3.13.0.
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
||||||
is false.
|
is false.
|
||||||
|
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is not
|
||||||
|
valid UTF-8 and [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled; otherwise, the bytes are
|
||||||
|
written unchanged
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -82,3 +85,5 @@ Linear in the size of the JSON value `j`.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.1.0.
|
- Added in version 3.1.0.
|
||||||
|
- Throwing `type_error.316` for a string or object key that is not valid UTF-8 if
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled added in version 3.13.0.
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
|
|
||||||
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - opt in to leaving an input stream positioned
|
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - opt in to leaving an input stream positioned
|
||||||
right after a parsed number
|
right after a parsed number
|
||||||
|
- [**JSON_STRICT_BINARY_UTF8**](json_strict_binary_utf8.md) - opt in to checking strings for valid UTF-8 in the CBOR,
|
||||||
|
UBJSON, BJData, and BSON writers
|
||||||
- [**JSON_STRICT_NUL_HANDLING**](json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input instead of
|
- [**JSON_STRICT_NUL_HANDLING**](json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input instead of
|
||||||
treating it as end of input
|
treating it as end of input
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# JSON_STRICT_BINARY_UTF8
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_STRICT_BINARY_UTF8 /* value */
|
||||||
|
```
|
||||||
|
|
||||||
|
When defined to `1`, the binary writers [`to_cbor`](../basic_json/to_cbor.md), [`to_ubjson`](../basic_json/to_ubjson.md),
|
||||||
|
[`to_bjdata`](../basic_json/to_bjdata.md), and [`to_bson`](../basic_json/to_bson.md) check every string value and
|
||||||
|
object key for valid UTF-8 and throw [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for
|
||||||
|
ill-formed UTF-8, like [`dump`](../basic_json/dump.md) does. Without it, they write the bytes unchanged.
|
||||||
|
|
||||||
|
The macro does not affect:
|
||||||
|
|
||||||
|
- [`to_msgpack`](../basic_json/to_msgpack.md): the MessagePack specification allows a `str` value to contain bytes that
|
||||||
|
are not valid UTF-8, so it always writes them unchanged.
|
||||||
|
- [`to_bon8`](../basic_json/to_bon8.md): BON8 always checks, because the UTF-8 lead bytes mark where a string ends.
|
||||||
|
- The binary readers ([`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md),
|
||||||
|
[`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md),
|
||||||
|
[`from_bson`](../basic_json/from_bson.md)): none of these formats requires a decoder to reject ill-formed UTF-8, so
|
||||||
|
they always return the bytes unchanged.
|
||||||
|
|
||||||
|
## Default definition
|
||||||
|
|
||||||
|
The default value is `0` (disabled, the behavior of version 3.12.0 and earlier is preserved).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_STRICT_BINARY_UTF8 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
!!! note "Background"
|
||||||
|
|
||||||
|
CBOR, UBJSON, BJData, and BSON all require strings to be UTF-8. Up to version 3.12.0, the writers did not check
|
||||||
|
this, so they could produce output that other decoders reject. Checking by default would break code that stores
|
||||||
|
other encodings (for instance ISO 8859-1) in a string and only ever writes it to a binary format, so this macro
|
||||||
|
offers the check as an opt-in ahead of version 4.0.0, where it is planned to become the default (see
|
||||||
|
[#5529](https://github.com/nlohmann/json/issues/5529) and [#5651](https://github.com/nlohmann/json/issues/5651)).
|
||||||
|
|
||||||
|
!!! warning "Opt-in only"
|
||||||
|
|
||||||
|
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
||||||
|
effect.
|
||||||
|
|
||||||
|
!!! note "ABI compatibility"
|
||||||
|
|
||||||
|
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_sbu8`), resulting in
|
||||||
|
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
||||||
|
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example "Default behavior (macro not defined)"
|
||||||
|
|
||||||
|
Without the macro, the bytes are written unchanged:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
auto v = json::to_cbor(json("\xFF"));
|
||||||
|
// v is {0x61, 0xFF}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Opt-in check (macro defined to 1)"
|
||||||
|
|
||||||
|
With the macro, ill-formed UTF-8 is rejected:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_STRICT_BINARY_UTF8 1
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
auto v = json::to_cbor(json("\xFF"));
|
||||||
|
// throws type_error.316: invalid UTF-8 byte at index 0: 0xFF
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [**to_cbor**](../basic_json/to_cbor.md) - create a CBOR serialization of a JSON value
|
||||||
|
- [**to_ubjson**](../basic_json/to_ubjson.md) - create a UBJSON serialization of a JSON value
|
||||||
|
- [**to_bjdata**](../basic_json/to_bjdata.md) - create a BJData serialization of a JSON value
|
||||||
|
- [**to_bson**](../basic_json/to_bson.md) - create a BSON serialization of a JSON value
|
||||||
|
- [**error_handler_t**](../basic_json/error_handler_t.md) - how [`dump`](../basic_json/dump.md) treats ill-formed UTF-8
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
|
- Planned to become the default (with the macro removed) in version 4.0.0.
|
||||||
@@ -63,6 +63,13 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
|
|
||||||
- strings with more than 18446744073709551615 bytes, i.e., 2<sup>64</sup>-1 bytes (theoretical)
|
- strings with more than 18446744073709551615 bytes, i.e., 2<sup>64</sup>-1 bytes (theoretical)
|
||||||
|
|
||||||
|
!!! warning "UTF-8 validation of string values and object keys"
|
||||||
|
|
||||||
|
BJData strings must use UTF-8 encoding. By default, `to_bjdata()` writes the bytes of string values and object keys
|
||||||
|
unchanged, even if they are not valid UTF-8. If
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled, it throws
|
||||||
|
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for ill-formed UTF-8 instead.
|
||||||
|
|
||||||
!!! info "Unused BJData markers"
|
!!! info "Unused BJData markers"
|
||||||
|
|
||||||
The following markers are not used in the conversion:
|
The following markers are not used in the conversion:
|
||||||
@@ -208,6 +215,15 @@ The library maps BJData types to JSON value types as follows:
|
|||||||
|
|
||||||
The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
|
The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
|
||||||
|
|
||||||
|
!!! warning "Ill-formed UTF-8 in string values and object keys"
|
||||||
|
|
||||||
|
BJData strings must use UTF-8 encoding, but this is not enforced on read: `from_bjdata()` accepts a string
|
||||||
|
value or object key whose bytes are not valid UTF-8 and hands them back unchanged. However,
|
||||||
|
[`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
||||||
|
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for such a value, unless an error
|
||||||
|
handler is passed that replaces or ignores the ill-formed bytes. By default, `to_bjdata()` writes such a value
|
||||||
|
back unchanged (see above).
|
||||||
|
|
||||||
!!! info "Round trips"
|
!!! info "Round trips"
|
||||||
|
|
||||||
A value returned by [`from_bjdata`](../../api/basic_json/from_bjdata.md) can be serialized with
|
A value returned by [`from_bjdata`](../../api/basic_json/from_bjdata.md) can be serialized with
|
||||||
|
|||||||
@@ -109,14 +109,16 @@ The library maps BSON record types to JSON value types as follows:
|
|||||||
If BSON input must be validated for strict specification compliance, validate it separately before passing it to
|
If BSON input must be validated for strict specification compliance, validate it separately before passing it to
|
||||||
`from_bson()`.
|
`from_bson()`.
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of string values"
|
!!! warning "Ill-formed UTF-8 in string values"
|
||||||
|
|
||||||
The BSON specification requires `string` values (type `0x02`) to be valid UTF-8. This library validates the
|
The BSON specification requires `string` values (type `0x02`) to be valid UTF-8, but this is not required of a
|
||||||
bytes of every such string at decode time and rejects ill-formed UTF-8 with a
|
decoder. `from_bson()` accepts a `string` value whose bytes are not valid UTF-8 and hands them back unchanged.
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with `allow_exceptions`
|
However, [`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
||||||
set to `false`, a discarded value), rather than only failing later when the resulting value is dumped. Element
|
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for such a value, unless an error handler is
|
||||||
(key) names and `binary` values (type `0x05`) are unaffected and are never validated, since they are read
|
passed that replaces or ignores the ill-formed bytes. By default, `to_bson()` writes such a string value or element
|
||||||
byte-by-byte as a C string, or are not required to hold text, respectively.
|
(key) name unchanged; if [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled, it
|
||||||
|
throws the same exception instead. Element (key) names are never validated on read, since they are read byte-by-byte
|
||||||
|
as a C string. `binary` values (type `0x05`) are unaffected, since they are not required to hold text.
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
|
|||||||
@@ -189,15 +189,16 @@ The library maps CBOR types to JSON value types as follows:
|
|||||||
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
|
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
|
||||||
general-purpose CBOR library instead.
|
general-purpose CBOR library instead.
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of text strings"
|
!!! warning "Ill-formed UTF-8 in text strings"
|
||||||
|
|
||||||
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings
|
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings (major
|
||||||
(major type 3) to be valid UTF-8. This library validates the bytes of every text string (object keys included) at
|
type 3) to be valid UTF-8, but leaves it up to the decoder whether to enforce this. This library does not:
|
||||||
decode time and rejects ill-formed UTF-8 with a
|
`from_cbor()` accepts a text string (object keys included) whose bytes are not valid UTF-8 and hands them back
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with
|
unchanged. However, [`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
||||||
`allow_exceptions` set to `false`, a discarded value), rather than only failing later when the resulting value is
|
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for such a value, unless an error handler is
|
||||||
dumped. Byte strings (major type 2) are unaffected and are never validated, since they are not required to hold
|
passed that replaces or ignores the ill-formed bytes. By default, `to_cbor()` writes such a value back unchanged; if
|
||||||
text.
|
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled, it throws the same exception
|
||||||
|
instead. Byte strings (major type 2) are unaffected, since they are not required to hold text.
|
||||||
|
|
||||||
!!! warning "Tagged items"
|
!!! warning "Tagged items"
|
||||||
|
|
||||||
|
|||||||
@@ -153,14 +153,15 @@ The library maps MessagePack types to JSON value types as follows:
|
|||||||
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
|
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
|
||||||
on. Such input needs a general-purpose MessagePack library instead.
|
on. Such input needs a general-purpose MessagePack library instead.
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of string values"
|
!!! warning "Ill-formed UTF-8 in string values"
|
||||||
|
|
||||||
The MessagePack specification requires `str` values (`fixstr`, `str 8`, `str 16`, `str 32`) to be valid UTF-8.
|
The MessagePack specification explicitly allows a `str` value (`fixstr`, `str 8`, `str 16`, `str 32`) to contain
|
||||||
This library validates the bytes of every such string (object keys included) at decode time and rejects
|
a byte sequence that is not valid UTF-8, and expects a deserializer to hand the original bytes back unchanged.
|
||||||
ill-formed UTF-8 with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or,
|
This library follows that: `from_msgpack()` reads `str` bytes (object keys included) as-is, without validating
|
||||||
with `allow_exceptions` set to `false`, a discarded value), rather than only failing later when the resulting
|
them, and `to_msgpack()` writes them back as-is, so such a value round-trips through `from_msgpack(to_msgpack(j))`
|
||||||
value is dumped. `bin`/`ext`/`fixext` values are unaffected and are never validated, since they are not required
|
byte for byte. However, [`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
||||||
to hold text.
|
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for a value read this way, unless an
|
||||||
|
error handler is passed that replaces or ignores the ill-formed bytes.
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ The library uses the following mapping from JSON values types to UBJSON types ac
|
|||||||
|
|
||||||
- strings with more than 9223372036854775807 bytes (theoretical)
|
- strings with more than 9223372036854775807 bytes (theoretical)
|
||||||
|
|
||||||
|
!!! warning "UTF-8 validation of string values and object keys"
|
||||||
|
|
||||||
|
UBJSON's required string encoding is UTF-8. By default, `to_ubjson()` writes the bytes of string values and object
|
||||||
|
keys unchanged, even if they are not valid UTF-8. If
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled, it throws
|
||||||
|
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for ill-formed UTF-8 instead.
|
||||||
|
|
||||||
!!! info "Unused UBJSON markers"
|
!!! info "Unused UBJSON markers"
|
||||||
|
|
||||||
The following markers are not used in the conversion:
|
The following markers are not used in the conversion:
|
||||||
@@ -120,6 +127,15 @@ The library maps UBJSON types to JSON value types as follows:
|
|||||||
|
|
||||||
The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value.
|
The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value.
|
||||||
|
|
||||||
|
!!! warning "Ill-formed UTF-8 in string values and object keys"
|
||||||
|
|
||||||
|
UBJSON's required string encoding is UTF-8, but this is not enforced on read: `from_ubjson()` accepts a string
|
||||||
|
value or object key whose bytes are not valid UTF-8 and hands them back unchanged. However,
|
||||||
|
[`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
||||||
|
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for such a value, unless an error
|
||||||
|
handler is passed that replaces or ignores the ill-formed bytes. By default, `to_ubjson()` writes such a value
|
||||||
|
back unchanged (see above).
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -138,6 +138,20 @@ using the library with compilers that do not fully support C++11 and may only wo
|
|||||||
|
|
||||||
See [full documentation of `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`](../api/macros/json_skip_unsupported_compiler_check.md).
|
See [full documentation of `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`](../api/macros/json_skip_unsupported_compiler_check.md).
|
||||||
|
|
||||||
|
## `JSON_STRICT_BINARY_UTF8`
|
||||||
|
|
||||||
|
When defined to `1`, [`to_cbor`](../api/basic_json/to_cbor.md), [`to_ubjson`](../api/basic_json/to_ubjson.md),
|
||||||
|
[`to_bjdata`](../api/basic_json/to_bjdata.md), and [`to_bson`](../api/basic_json/to_bson.md) throw
|
||||||
|
[`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) for a string value or object key that is not
|
||||||
|
valid UTF-8. The default value is `0`, which writes the bytes unchanged as before version 3.13.0; this is planned to
|
||||||
|
become the default in version 4.0.0.
|
||||||
|
|
||||||
|
The check can also be enabled with the CMake option
|
||||||
|
[`JSON_StrictBinaryUTF8`](../integration/cmake.md#json_strictbinaryutf8) (`OFF` by default) which sets
|
||||||
|
`JSON_STRICT_BINARY_UTF8` accordingly.
|
||||||
|
|
||||||
|
See [full documentation of `JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md).
|
||||||
|
|
||||||
## `JSON_STRICT_NUL_HANDLING`
|
## `JSON_STRICT_NUL_HANDLING`
|
||||||
|
|
||||||
When defined to `1`, a `'\0'` (NUL) byte anywhere in the input is rejected with `parse_error.101`, like any other
|
When defined to `1`, a `'\0'` (NUL) byte anywhere in the input is rejected with `parse_error.101`, like any other
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ The complete default namespace name is derived as follows:
|
|||||||
`_bics`.
|
`_bics`.
|
||||||
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
||||||
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
|
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
|
||||||
|
- [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) defined non-zero appends `_sbu8`.
|
||||||
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
||||||
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
||||||
below.
|
below.
|
||||||
|
|||||||
@@ -340,8 +340,9 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
|||||||
### json.exception.parse_error.113
|
### json.exception.parse_error.113
|
||||||
|
|
||||||
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
||||||
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
string was read where one was required (for instance as a map key), or the string's length specification is invalid.
|
||||||
the string's bytes are not valid UTF-8.
|
The bytes of a string itself are not checked for valid UTF-8 on read; see the ill-formed UTF-8 notes on the
|
||||||
|
individual [binary format](../features/binary_formats/index.md) pages for how such a string is handled afterward.
|
||||||
|
|
||||||
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
|
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
|
||||||
type (for instance integers or `null`) are therefore not supported; see the notes on
|
type (for instance integers or `null`) are therefore not supported; see the notes on
|
||||||
@@ -364,9 +365,6 @@ type (for instance integers or `null`) are therefore not supported; see the note
|
|||||||
```
|
```
|
||||||
[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative
|
[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative
|
||||||
```
|
```
|
||||||
```
|
|
||||||
[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte
|
|
||||||
```
|
|
||||||
|
|
||||||
### json.exception.parse_error.114
|
### json.exception.parse_error.114
|
||||||
|
|
||||||
@@ -749,6 +747,11 @@ The `unflatten()` function only works for an object whose keys are JSON Pointers
|
|||||||
|
|
||||||
The `dump()` function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded.
|
The `dump()` function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded.
|
||||||
|
|
||||||
|
If [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) is enabled, the binary writers
|
||||||
|
[`to_cbor()`](../api/basic_json/to_cbor.md), [`to_ubjson()`](../api/basic_json/to_ubjson.md),
|
||||||
|
[`to_bjdata()`](../api/basic_json/to_bjdata.md), and [`to_bson()`](../api/basic_json/to_bson.md) throw this exception
|
||||||
|
for a string value or object key that is not valid UTF-8 as well.
|
||||||
|
|
||||||
!!! failure "Example message"
|
!!! failure "Example message"
|
||||||
|
|
||||||
Calling `dump()` on a JSON value containing an ISO 8859-1 encoded string:
|
Calling `dump()` on a JSON value containing an ISO 8859-1 encoded string:
|
||||||
|
|||||||
@@ -204,6 +204,11 @@ Use the non-amalgamated version of the library. This option is `ON` by default.
|
|||||||
|
|
||||||
Treat the library headers like system headers (i.e., adding `SYSTEM` to the [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) call) to check for this library by tools like Clang-Tidy. This option is `OFF` by default.
|
Treat the library headers like system headers (i.e., adding `SYSTEM` to the [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) call) to check for this library by tools like Clang-Tidy. This option is `OFF` by default.
|
||||||
|
|
||||||
|
### `JSON_StrictBinaryUTF8`
|
||||||
|
|
||||||
|
Check string values and object keys for valid UTF-8 in the CBOR, UBJSON, BJData, and BSON writers, by defining the
|
||||||
|
macro [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md). This option is `OFF` by default.
|
||||||
|
|
||||||
### `JSON_StrictNulHandling`
|
### `JSON_StrictNulHandling`
|
||||||
|
|
||||||
Reject a `'\0'` (NUL) byte in the input instead of treating it as end of input, by defining the macro
|
Reject a `'\0'` (NUL) byte in the input instead of treating it as end of input, by defining the macro
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ nav:
|
|||||||
- 'JSON_PRECISE_STREAM_POSITION': api/macros/json_precise_stream_position.md
|
- 'JSON_PRECISE_STREAM_POSITION': api/macros/json_precise_stream_position.md
|
||||||
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
||||||
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
||||||
|
- 'JSON_STRICT_BINARY_UTF8': api/macros/json_strict_binary_utf8.md
|
||||||
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
||||||
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
|
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
|
||||||
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
||||||
|
|||||||
@@ -46,6 +46,10 @@
|
|||||||
#define JSON_STRICT_NUL_HANDLING 0
|
#define JSON_STRICT_NUL_HANDLING 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_STRICT_BINARY_UTF8
|
||||||
|
#define JSON_STRICT_BINARY_UTF8 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -82,14 +86,20 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JSON_STRICT_BINARY_UTF8
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8 _sbu8
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -98,7 +108,8 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -4031,28 +4031,13 @@ class binary_reader
|
|||||||
const NumberType len,
|
const NumberType len,
|
||||||
string_t& result)
|
string_t& result)
|
||||||
{
|
{
|
||||||
// get_bytes() appends to result, and CBOR indefinite-length strings
|
// Strings are taken as is: none of CBOR (RFC 8949 §3.1 leaves the
|
||||||
// collect all their chunks in the same result; validating only the
|
// choice to the decoder), MessagePack (whose spec explicitly allows
|
||||||
// newly read bytes keeps the check linear in the input size
|
// a str object to contain an invalid byte sequence), UBJSON, BJData,
|
||||||
const std::size_t old_size = result.size();
|
// or BSON requires a decoder to reject ill-formed UTF-8. The bytes
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bytes(format, len, "string", result)))
|
// are kept unchanged; dump() and the binary writers are the ones
|
||||||
{
|
// that check them and report type_error.316 if they are not valid.
|
||||||
return false;
|
return get_bytes(format, len, "string", result);
|
||||||
}
|
|
||||||
|
|
||||||
// RFC 8949 (CBOR) §3.1 and the MessagePack/BSON/UBJSON specifications
|
|
||||||
// all require text strings to be valid UTF-8; reject anything else
|
|
||||||
// right here so malformed input is caught at decode time instead of
|
|
||||||
// only surfacing later as a type_error.316 when the value is dumped
|
|
||||||
// (which would defeat allow_exceptions=false / strict discarding).
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_valid_utf8(result, old_size)))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(),
|
|
||||||
parse_error::create(113, chars_read,
|
|
||||||
exception_message(format, "invalid string: ill-formed UTF-8 byte", "string"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
#undef JSON_PRECISE_STREAM_POSITION
|
#undef JSON_PRECISE_STREAM_POSITION
|
||||||
#undef JSON_STRICT_NUL_HANDLING
|
#undef JSON_STRICT_NUL_HANDLING
|
||||||
|
#undef JSON_STRICT_BINARY_UTF8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or an object key is not valid UTF-8
|
||||||
@throw type_error.317 if @a j is not an object
|
@throw type_error.317 if @a j is not an object
|
||||||
*/
|
*/
|
||||||
void write_bson(const BasicJsonType& j)
|
void write_bson(const BasicJsonType& j)
|
||||||
@@ -145,6 +147,8 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or an object key is not valid UTF-8
|
||||||
*/
|
*/
|
||||||
void write_cbor(const BasicJsonType& j)
|
void write_cbor(const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
@@ -211,6 +215,8 @@ class binary_writer
|
|||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
|
check_text_utf8(*j.m_data.m_value.string, j);
|
||||||
|
|
||||||
// step 1: write control byte and the string length
|
// step 1: write control byte and the string length
|
||||||
write_cbor_head(0x60, j.m_data.m_value.string->size());
|
write_cbor_head(0x60, j.m_data.m_value.string->size());
|
||||||
|
|
||||||
@@ -287,6 +293,11 @@ class binary_writer
|
|||||||
// step 2: write each element
|
// step 2: write each element
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
for (const auto& el : *j.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
|
// el.first is checked here, against the object as
|
||||||
|
// diagnostics context, because write_cbor(el.first)
|
||||||
|
// converts it to a temporary basic_json that would be
|
||||||
|
// used as the context instead
|
||||||
|
check_text_utf8(el.first, j);
|
||||||
write_cbor(el.first);
|
write_cbor(el.first);
|
||||||
write_cbor(el.second);
|
write_cbor(el.second);
|
||||||
}
|
}
|
||||||
@@ -629,6 +640,8 @@ class binary_writer
|
|||||||
@param[in] add_prefix whether prefixes need to be used for this value
|
@param[in] add_prefix whether prefixes need to be used for this value
|
||||||
@param[in] use_bjdata whether write in BJData format, default is false
|
@param[in] use_bjdata whether write in BJData format, default is false
|
||||||
@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 JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or an object key is not valid UTF-8
|
||||||
*/
|
*/
|
||||||
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,
|
||||||
@@ -678,6 +691,8 @@ class binary_writer
|
|||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
|
check_text_utf8(*j.m_data.m_value.string, j);
|
||||||
|
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
{
|
{
|
||||||
oa.write_character(to_char_type('S'));
|
oa.write_character(to_char_type('S'));
|
||||||
@@ -840,6 +855,7 @@ class binary_writer
|
|||||||
|
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
for (const auto& el : *j.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
|
check_text_utf8(el.first, j);
|
||||||
write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
|
write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
|
||||||
oa.write_characters(
|
oa.write_characters(
|
||||||
reinterpret_cast<const CharType*>(el.first.data()),
|
reinterpret_cast<const CharType*>(el.first.data()),
|
||||||
@@ -884,6 +900,10 @@ class binary_writer
|
|||||||
/*!
|
/*!
|
||||||
@return The size of a BSON document entry header, including the id marker
|
@return The size of a BSON document entry header, including the id marker
|
||||||
and the entry name size (and its null-terminator).
|
and the entry name size (and its null-terminator).
|
||||||
|
@throw out_of_range.409 if @a name contains U+0000, before anything is
|
||||||
|
written
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a name is
|
||||||
|
not valid UTF-8, before anything is written
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
|
static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
@@ -893,7 +913,8 @@ class binary_writer
|
|||||||
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
static_cast<void>(j);
|
check_text_utf8(name, j);
|
||||||
|
|
||||||
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -949,9 +970,21 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return The size of the BSON-encoded string in @a value
|
@return The size of the BSON-encoded string in @a value
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a value
|
||||||
|
is not valid UTF-8, before anything is written
|
||||||
|
|
||||||
|
@note The UTF-8 check is skipped if @a value is already too long for the
|
||||||
|
32-bit BSON length field (@ref to_bson_length rejects it later, once
|
||||||
|
the size of the whole document is known); this also keeps the check
|
||||||
|
from reading past a StringType that reports a size larger than what
|
||||||
|
it actually holds.
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_string_size(const string_t& value)
|
static std::size_t calc_bson_string_size(const string_t& value, const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
|
if (JSON_HEDLEY_LIKELY(value_in_range_of<std::int32_t>(value.size())))
|
||||||
|
{
|
||||||
|
check_text_utf8(value, j);
|
||||||
|
}
|
||||||
return sizeof(std::int32_t) + value.size() + 1ul;
|
return sizeof(std::int32_t) + value.size() + 1ul;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1080,6 +1113,8 @@ class binary_writer
|
|||||||
is neither an object nor an array
|
is neither an object nor an array
|
||||||
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
|
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
|
||||||
into a byte, before anything is written
|
into a byte, before anything is written
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a j is a
|
||||||
|
string that is not valid UTF-8, before anything is written
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_value_size(const BasicJsonType& j)
|
static std::size_t calc_bson_value_size(const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
@@ -1101,7 +1136,7 @@ class binary_writer
|
|||||||
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
return calc_bson_string_size(*j.m_data.m_value.string);
|
return calc_bson_string_size(*j.m_data.m_value.string, j);
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
return 0ul;
|
return 0ul;
|
||||||
@@ -1214,6 +1249,8 @@ class binary_writer
|
|||||||
written
|
written
|
||||||
@throw out_of_range.415 if a binary value's subtype does not fit into a
|
@throw out_of_range.415 if a binary value's subtype does not fit into a
|
||||||
byte, before anything is written
|
byte, before anything is written
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or a key is not valid UTF-8, before anything is written
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
static std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
||||||
{
|
{
|
||||||
@@ -2092,7 +2129,7 @@ class binary_writer
|
|||||||
*/
|
*/
|
||||||
void write_bon8_string(const string_t& s, bool& string_open, const BasicJsonType& context)
|
void write_bon8_string(const string_t& s, bool& string_open, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
check_bon8_utf8(s, context);
|
check_utf8(s, context);
|
||||||
|
|
||||||
// a string that follows another string terminates it
|
// a string that follows another string terminates it
|
||||||
if (string_open)
|
if (string_open)
|
||||||
@@ -2122,7 +2159,7 @@ class binary_writer
|
|||||||
@throw type_error.316 if @a s is not valid UTF-8; the message names the
|
@throw type_error.316 if @a s is not valid UTF-8; the message names the
|
||||||
first byte of the first invalid or incomplete sequence
|
first byte of the first invalid or incomplete sequence
|
||||||
*/
|
*/
|
||||||
static void check_bon8_utf8(const string_t& s, const BasicJsonType& context)
|
static void check_utf8(const string_t& s, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
static_cast<void>(context); // only used when exceptions are enabled
|
static_cast<void>(context); // only used when exceptions are enabled
|
||||||
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
||||||
@@ -2133,6 +2170,29 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief check a CBOR, UBJSON, BJData, or BSON text string for valid UTF-8
|
||||||
|
|
||||||
|
The check only happens if JSON_STRICT_BINARY_UTF8 is enabled. Otherwise,
|
||||||
|
the bytes are written unchanged, as before version 3.13.0. MessagePack
|
||||||
|
always writes the bytes as is, and BON8 always checks them (see
|
||||||
|
@ref check_utf8).
|
||||||
|
|
||||||
|
@param[in] s the string to check
|
||||||
|
@param[in] context the value that holds @a s (for diagnostics)
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a s is
|
||||||
|
not valid UTF-8
|
||||||
|
*/
|
||||||
|
static void check_text_utf8(const string_t& s, const BasicJsonType& context)
|
||||||
|
{
|
||||||
|
#if JSON_STRICT_BINARY_UTF8
|
||||||
|
check_utf8(s, context);
|
||||||
|
#else
|
||||||
|
static_cast<void>(s);
|
||||||
|
static_cast<void>(context);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write an integer in the shortest encoding
|
@brief write an integer in the shortest encoding
|
||||||
|
|
||||||
|
|||||||
@@ -117,13 +117,14 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally
|
|||||||
written by Björn Hoehrmann. See
|
written by Björn Hoehrmann. See
|
||||||
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||||
|
|
||||||
The library checks UTF-8 well-formedness (RFC 3629, section 4) in four
|
The library checks UTF-8 well-formedness (RFC 3629, section 4) in three
|
||||||
places, which differ in speed, diagnostics, and how they read the input:
|
places, which differ in speed, diagnostics, and how they read the input:
|
||||||
|
|
||||||
- decode() and @ref is_valid_utf8 below: the serializer (to escape and, in
|
- decode() below: the serializer, to escape and, in strict mode, reject
|
||||||
strict mode, reject ill-formed UTF-8 when dumping a string) and the CBOR,
|
ill-formed UTF-8 when dumping a string. The CBOR, MessagePack, BSON,
|
||||||
MessagePack, BSON, UBJSON and BJData readers (to reject ill-formed UTF-8 in
|
UBJSON and BJData readers do not use it: none of those specs requires a
|
||||||
text strings at decode time).
|
decoder to reject ill-formed UTF-8 in text strings, so the readers keep
|
||||||
|
the bytes as is and leave the check to dump() and the binary writers.
|
||||||
- the per-lead-byte switch in lexer::scan_string(): JSON text, with a
|
- the per-lead-byte switch in lexer::scan_string(): JSON text, with a
|
||||||
diagnostic for each kind of error.
|
diagnostic for each kind of error.
|
||||||
- validate_one_utf8() and valid_utf8_prefix() in string_scan.hpp: the lexer's
|
- validate_one_utf8() and valid_utf8_prefix() in string_scan.hpp: the lexer's
|
||||||
@@ -178,38 +179,5 @@ inline std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std:
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief check whether a string consists solely of valid UTF-8
|
|
||||||
|
|
||||||
Used by the CBOR/MessagePack/BSON/UBJSON binary readers to reject text
|
|
||||||
strings that are not valid UTF-8 at decode time (RFC 8949 §3.1 and the
|
|
||||||
MessagePack/BSON specifications all require text strings to be UTF-8), so
|
|
||||||
that malformed input is caught immediately instead of only surfacing later
|
|
||||||
as a type_error.316 when the resulting value is dumped.
|
|
||||||
|
|
||||||
@param[in] s the string to check
|
|
||||||
@param[in] first index of the first byte to check; the bytes before it are
|
|
||||||
assumed to have been validated already and to end on a
|
|
||||||
code point boundary
|
|
||||||
@return whether @a s (from index @a first on) is valid UTF-8
|
|
||||||
*/
|
|
||||||
template<typename StringType>
|
|
||||||
inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noexcept
|
|
||||||
{
|
|
||||||
std::uint8_t state = UTF8_ACCEPT;
|
|
||||||
std::uint32_t codepoint = 0;
|
|
||||||
|
|
||||||
for (std::size_t i = first; i < s.size(); ++i)
|
|
||||||
{
|
|
||||||
decode(state, codepoint, static_cast<std::uint8_t>(s[i]));
|
|
||||||
if (state == UTF8_REJECT)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state == UTF8_ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
+258
-164
@@ -18,16 +18,6 @@
|
|||||||
#ifndef INCLUDE_NLOHMANN_JSON_HPP_
|
#ifndef INCLUDE_NLOHMANN_JSON_HPP_
|
||||||
#define INCLUDE_NLOHMANN_JSON_HPP_
|
#define INCLUDE_NLOHMANN_JSON_HPP_
|
||||||
|
|
||||||
// Workaround for GCC template redefinition errors in C++ modules
|
|
||||||
// When nlohmann/json.hpp is included in a C++20 module preamble after
|
|
||||||
// other module imports, GCC may report spurious redefinition errors for
|
|
||||||
// STL templates. These pragmas suppress those false positives.
|
|
||||||
// See: https://github.com/nlohmann/json/issues/5103
|
|
||||||
#if defined(__GNUC__) && !defined(__clang__) && __cplusplus >= 202002L
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <algorithm> // all_of, find, for_each, none_of
|
#include <algorithm> // all_of, find, for_each, none_of
|
||||||
#include <cmath> // isnan
|
#include <cmath> // isnan
|
||||||
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
|
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
|
||||||
@@ -114,6 +104,10 @@
|
|||||||
#define JSON_STRICT_NUL_HANDLING 0
|
#define JSON_STRICT_NUL_HANDLING 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_STRICT_BINARY_UTF8
|
||||||
|
#define JSON_STRICT_BINARY_UTF8 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -150,14 +144,20 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JSON_STRICT_BINARY_UTF8
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8 _sbu8
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -166,7 +166,8 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
@@ -2617,13 +2618,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP
|
|||||||
#define JSON_NO_THREAD_LOCAL 1
|
#define JSON_NO_THREAD_LOCAL 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// disable documentation warnings on clang
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation"
|
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// allow disabling exceptions
|
// allow disabling exceptions
|
||||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
|
||||||
#define JSON_THROW(exception) throw exception
|
#define JSON_THROW(exception) throw exception
|
||||||
@@ -2682,7 +2676,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP
|
|||||||
{ \
|
{ \
|
||||||
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
||||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
||||||
/* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
/* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
||||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
||||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
auto it = std::find_if(std::begin(m), std::end(m), \
|
||||||
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
||||||
@@ -2696,7 +2690,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP
|
|||||||
{ \
|
{ \
|
||||||
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
||||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
||||||
/* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
/* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
||||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
||||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
auto it = std::find_if(std::begin(m), std::end(m), \
|
||||||
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
||||||
@@ -2735,7 +2729,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
{ \
|
{ \
|
||||||
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
||||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
||||||
/* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
/* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
||||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
||||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
auto it = std::find_if(std::begin(m), std::end(m), \
|
||||||
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
||||||
@@ -2750,7 +2744,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
{ \
|
{ \
|
||||||
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
/* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
|
||||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
||||||
/* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
/* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on <array> */ \
|
||||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
||||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
auto it = std::find_if(std::begin(m), std::end(m), \
|
||||||
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
||||||
@@ -3578,8 +3572,7 @@ inline StringType escape(const StringType& s)
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief string unescaping as described in RFC 6901 (Sect. 4)
|
* @brief string unescaping as described in RFC 6901 (Sect. 4)
|
||||||
* @param[in] s string to unescape
|
* @param[in,out] s string to unescape in place
|
||||||
* @return unescaped string
|
|
||||||
*
|
*
|
||||||
* Note the order of escaping "~1" to "/" and "~0" to "~" is important.
|
* Note the order of escaping "~1" to "/" and "~0" to "~" is important.
|
||||||
*
|
*
|
||||||
@@ -5871,22 +5864,22 @@ void())
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename BasicJsonType, typename T, std::size_t... Idx >
|
template < typename BasicJsonType, typename T, std::size_t... Idx >
|
||||||
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
|
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(const BasicJsonType& j,
|
||||||
identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
|
identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
|
||||||
{
|
{
|
||||||
return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
|
return { { j.at(Idx).template get<T>()... } };
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename BasicJsonType, typename T, std::size_t N >
|
template < typename BasicJsonType, typename T, std::size_t N >
|
||||||
auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
|
auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
|
||||||
-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
|
-> decltype(from_json_inplace_array_impl(j, tag, make_index_sequence<N> {}))
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
|
return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
@@ -6021,54 +6014,54 @@ template<std::size_t PTagValue, typename BasicJsonType, typename... Types>
|
|||||||
using tuple_type = std::tuple < decltype(from_json_tuple_get_impl(std::declval<BasicJsonType>(), detail::identity_tag<Types> {}, detail::priority_tag<PTagValue> {}))... >;
|
using tuple_type = std::tuple < decltype(from_json_tuple_get_impl(std::declval<BasicJsonType>(), detail::identity_tag<Types> {}, detail::priority_tag<PTagValue> {}))... >;
|
||||||
|
|
||||||
template<std::size_t PTagValue, typename... Args, typename BasicJsonType, std::size_t... Idx>
|
template<std::size_t PTagValue, typename... Args, typename BasicJsonType, std::size_t... Idx>
|
||||||
tuple_type<PTagValue, BasicJsonType, Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
|
tuple_type<PTagValue, const BasicJsonType&, Args...> from_json_tuple_impl_base(const BasicJsonType& j, index_sequence<Idx...> /*unused*/)
|
||||||
{
|
{
|
||||||
return tuple_type<PTagValue, BasicJsonType, Args...>(from_json_tuple_get_impl(std::forward<BasicJsonType>(j).at(Idx), detail::identity_tag<Args> {}, detail::priority_tag<PTagValue> {})...);
|
return tuple_type<PTagValue, const BasicJsonType&, Args...>(from_json_tuple_get_impl(j.at(Idx), detail::identity_tag<Args> {}, detail::priority_tag<PTagValue> {})...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t PTagValue, typename BasicJsonType>
|
template<std::size_t PTagValue, typename BasicJsonType>
|
||||||
std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
|
std::tuple<> from_json_tuple_impl_base(const BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename BasicJsonType, class A1, class A2 >
|
template < typename BasicJsonType, class A1, class A2 >
|
||||||
std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
|
std::pair<A1, A2> from_json_tuple_impl(const BasicJsonType& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
|
||||||
{
|
{
|
||||||
return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
|
return {j.at(0).template get<A1>(),
|
||||||
std::forward<BasicJsonType>(j).at(1).template get<A2>()};
|
j.at(1).template get<A2>()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename A1, typename A2>
|
template<typename BasicJsonType, typename A1, typename A2>
|
||||||
inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
|
inline void from_json_tuple_impl(const BasicJsonType& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
|
||||||
{
|
{
|
||||||
p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
|
p = from_json_tuple_impl(j, identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename... Args>
|
template<typename BasicJsonType, typename... Args>
|
||||||
std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
|
std::tuple<Args...> from_json_tuple_impl(const BasicJsonType& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
|
||||||
{
|
{
|
||||||
static_assert(cxpr_and<cxpr_or<cxpr_not<std::is_reference<Args>>, is_compatible_reference_type<BasicJsonType, Args>>...>::value,
|
static_assert(cxpr_and<cxpr_or<cxpr_not<std::is_reference<Args>>, is_compatible_reference_type<const BasicJsonType&, Args>>...>::value,
|
||||||
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
|
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
|
||||||
return from_json_tuple_impl_base<1, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
|
return from_json_tuple_impl_base<1, Args...>(j, index_sequence_for<Args...> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename... Args>
|
template<typename BasicJsonType, typename... Args>
|
||||||
inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
|
inline void from_json_tuple_impl(const BasicJsonType& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
|
||||||
{
|
{
|
||||||
t = from_json_tuple_impl_base<2, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
|
t = from_json_tuple_impl_base<2, Args...>(j, index_sequence_for<Args...> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename TupleRelated>
|
template<typename BasicJsonType, typename TupleRelated>
|
||||||
auto from_json(BasicJsonType&& j, TupleRelated&& t)
|
auto from_json(const BasicJsonType& j, TupleRelated&& t)
|
||||||
-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
|
-> decltype(from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {}))
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
|
return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
||||||
@@ -6153,7 +6146,7 @@ struct from_json_fn
|
|||||||
/// namespace to hold default `from_json` function
|
/// namespace to hold default `from_json` function
|
||||||
/// to see why this is required:
|
/// to see why this is required:
|
||||||
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
||||||
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
|
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces,misc-anonymous-namespace-in-header)
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
|
JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
|
||||||
@@ -6339,13 +6332,14 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally
|
|||||||
written by Björn Hoehrmann. See
|
written by Björn Hoehrmann. See
|
||||||
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||||
|
|
||||||
The library checks UTF-8 well-formedness (RFC 3629, section 4) in four
|
The library checks UTF-8 well-formedness (RFC 3629, section 4) in three
|
||||||
places, which differ in speed, diagnostics, and how they read the input:
|
places, which differ in speed, diagnostics, and how they read the input:
|
||||||
|
|
||||||
- decode() and @ref is_valid_utf8 below: the serializer (to escape and, in
|
- decode() below: the serializer, to escape and, in strict mode, reject
|
||||||
strict mode, reject ill-formed UTF-8 when dumping a string) and the CBOR,
|
ill-formed UTF-8 when dumping a string. The CBOR, MessagePack, BSON,
|
||||||
MessagePack, BSON, UBJSON and BJData readers (to reject ill-formed UTF-8 in
|
UBJSON and BJData readers do not use it: none of those specs requires a
|
||||||
text strings at decode time).
|
decoder to reject ill-formed UTF-8 in text strings, so the readers keep
|
||||||
|
the bytes as is and leave the check to dump() and the binary writers.
|
||||||
- the per-lead-byte switch in lexer::scan_string(): JSON text, with a
|
- the per-lead-byte switch in lexer::scan_string(): JSON text, with a
|
||||||
diagnostic for each kind of error.
|
diagnostic for each kind of error.
|
||||||
- validate_one_utf8() and valid_utf8_prefix() in string_scan.hpp: the lexer's
|
- validate_one_utf8() and valid_utf8_prefix() in string_scan.hpp: the lexer's
|
||||||
@@ -6400,39 +6394,6 @@ inline std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std:
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief check whether a string consists solely of valid UTF-8
|
|
||||||
|
|
||||||
Used by the CBOR/MessagePack/BSON/UBJSON binary readers to reject text
|
|
||||||
strings that are not valid UTF-8 at decode time (RFC 8949 §3.1 and the
|
|
||||||
MessagePack/BSON specifications all require text strings to be UTF-8), so
|
|
||||||
that malformed input is caught immediately instead of only surfacing later
|
|
||||||
as a type_error.316 when the resulting value is dumped.
|
|
||||||
|
|
||||||
@param[in] s the string to check
|
|
||||||
@param[in] first index of the first byte to check; the bytes before it are
|
|
||||||
assumed to have been validated already and to end on a
|
|
||||||
code point boundary
|
|
||||||
@return whether @a s (from index @a first on) is valid UTF-8
|
|
||||||
*/
|
|
||||||
template<typename StringType>
|
|
||||||
inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noexcept
|
|
||||||
{
|
|
||||||
std::uint8_t state = UTF8_ACCEPT;
|
|
||||||
std::uint32_t codepoint = 0;
|
|
||||||
|
|
||||||
for (std::size_t i = first; i < s.size(); ++i)
|
|
||||||
{
|
|
||||||
decode(state, codepoint, static_cast<std::uint8_t>(s[i]));
|
|
||||||
if (state == UTF8_REJECT)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state == UTF8_ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
@@ -6629,11 +6590,11 @@ namespace std
|
|||||||
JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wmismatched-tags")
|
JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wmismatched-tags")
|
||||||
#endif
|
#endif
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp)
|
class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp,bugprone-std-namespace-modification)
|
||||||
: public std::integral_constant<std::size_t, 2> {};
|
: public std::integral_constant<std::size_t, 2> {};
|
||||||
|
|
||||||
template<std::size_t N, typename IteratorType>
|
template<std::size_t N, typename IteratorType>
|
||||||
class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp)
|
class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp,bugprone-std-namespace-modification)
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using type = decltype(
|
using type = decltype(
|
||||||
@@ -7178,7 +7139,7 @@ struct to_json_fn
|
|||||||
/// namespace to hold default `to_json` function
|
/// namespace to hold default `to_json` function
|
||||||
/// to see why this is required:
|
/// to see why this is required:
|
||||||
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
||||||
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
|
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces,misc-anonymous-namespace-in-header)
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
|
JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
|
||||||
@@ -8399,6 +8360,9 @@ struct container_input_adapter_factory< ContainerType,
|
|||||||
|
|
||||||
static adapter_type create(ContainerType&& container)
|
static adapter_type create(ContainerType&& container)
|
||||||
{
|
{
|
||||||
|
// container is forwarded twice on purpose: the resulting begin/end
|
||||||
|
// iterator types must match adapter_type, computed the same way
|
||||||
|
// NOLINTNEXTLINE(bugprone-use-after-move)
|
||||||
return input_adapter(begin(std::forward<ContainerType>(container)), end(std::forward<ContainerType>(container)));
|
return input_adapter(begin(std::forward<ContainerType>(container)), end(std::forward<ContainerType>(container)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -10298,9 +10262,9 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief get codepoint from 4 hex characters following `\u`
|
@brief get codepoint from 4 hex characters following `\\u`
|
||||||
|
|
||||||
For input "\u c1 c2 c3 c4" the codepoint is:
|
For input "\\u c1 c2 c3 c4" the codepoint is:
|
||||||
(c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
|
(c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
|
||||||
= (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
|
= (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
|
||||||
|
|
||||||
@@ -13949,7 +13913,7 @@ class binary_reader
|
|||||||
@brief Parses a C-style string from the BSON input.
|
@brief Parses a C-style string from the BSON input.
|
||||||
@param[in,out] result A reference to the string variable where the read
|
@param[in,out] result A reference to the string variable where the read
|
||||||
string is to be stored.
|
string is to be stored.
|
||||||
@return `true` if the \x00-byte indicating the end of the string was
|
@return `true` if the \\x00-byte indicating the end of the string was
|
||||||
encountered before the EOF; false` indicates an unexpected EOF.
|
encountered before the EOF; false` indicates an unexpected EOF.
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr(string_t& result)
|
bool get_bson_cstr(string_t& result)
|
||||||
@@ -13979,7 +13943,7 @@ class binary_reader
|
|||||||
@brief read a C-style string from contiguous input in one step
|
@brief read a C-style string from contiguous input in one step
|
||||||
|
|
||||||
@param[in,out] result the string to append to
|
@param[in,out] result the string to append to
|
||||||
@return whether the string was read; if the input has no \x00-byte, nothing
|
@return whether the string was read; if the input has no \\x00-byte, nothing
|
||||||
is read, and @ref get_bson_cstr reports the end of the input
|
is read, and @ref get_bson_cstr reports the end of the input
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
||||||
@@ -17559,28 +17523,13 @@ class binary_reader
|
|||||||
const NumberType len,
|
const NumberType len,
|
||||||
string_t& result)
|
string_t& result)
|
||||||
{
|
{
|
||||||
// get_bytes() appends to result, and CBOR indefinite-length strings
|
// Strings are taken as is: none of CBOR (RFC 8949 §3.1 leaves the
|
||||||
// collect all their chunks in the same result; validating only the
|
// choice to the decoder), MessagePack (whose spec explicitly allows
|
||||||
// newly read bytes keeps the check linear in the input size
|
// a str object to contain an invalid byte sequence), UBJSON, BJData,
|
||||||
const std::size_t old_size = result.size();
|
// or BSON requires a decoder to reject ill-formed UTF-8. The bytes
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bytes(format, len, "string", result)))
|
// are kept unchanged; dump() and the binary writers are the ones
|
||||||
{
|
// that check them and report type_error.316 if they are not valid.
|
||||||
return false;
|
return get_bytes(format, len, "string", result);
|
||||||
}
|
|
||||||
|
|
||||||
// RFC 8949 (CBOR) §3.1 and the MessagePack/BSON/UBJSON specifications
|
|
||||||
// all require text strings to be valid UTF-8; reject anything else
|
|
||||||
// right here so malformed input is caught at decode time instead of
|
|
||||||
// only surfacing later as a type_error.316 when the value is dumped
|
|
||||||
// (which would defeat allow_exceptions=false / strict discarding).
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_valid_utf8(result, old_size)))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(),
|
|
||||||
parse_error::create(113, chars_read,
|
|
||||||
exception_message(format, "invalid string: ill-formed UTF-8 byte", "string"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -18636,7 +18585,7 @@ This class implements a both iterators (iterator and const_iterator) for the
|
|||||||
been set (e.g., by a constructor or a copy assignment). If the iterator is
|
been set (e.g., by a constructor or a copy assignment). If the iterator is
|
||||||
default-constructed, it is *uninitialized* and most methods are undefined.
|
default-constructed, it is *uninitialized* and most methods are undefined.
|
||||||
**The library uses assertions to detect calls on uninitialized iterators.**
|
**The library uses assertions to detect calls on uninitialized iterators.**
|
||||||
@requirement REQ-JSON-01 The class satisfies the following concept requirements:
|
This class satisfies the following concept requirements (REQ-JSON-01):
|
||||||
-
|
-
|
||||||
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
||||||
The iterator that can be moved can be moved in both directions (i.e.
|
The iterator that can be moved can be moved in both directions (i.e.
|
||||||
@@ -19400,7 +19349,7 @@ namespace detail
|
|||||||
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
|
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
|
||||||
create @ref const_reverse_iterator).
|
create @ref const_reverse_iterator).
|
||||||
|
|
||||||
@requirement REQ-JSON-02 The class satisfies the following concept requirements:
|
This class satisfies the following concept requirements (REQ-JSON-02):
|
||||||
-
|
-
|
||||||
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
||||||
The iterator that can be moved can be moved in both directions (i.e.
|
The iterator that can be moved can be moved in both directions (i.e.
|
||||||
@@ -19843,11 +19792,11 @@ class json_pointer
|
|||||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
|
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// only triggered on special platforms (like 32bit), see also
|
// the index does not fit into size_type; on 64-bit platforms this is
|
||||||
// https://github.com/nlohmann/json/pull/2203
|
// only SIZE_MAX itself (see #2203 and #5395)
|
||||||
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
|
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE
|
JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<size_type>(res);
|
return static_cast<size_type>(res);
|
||||||
@@ -19881,7 +19830,7 @@ class json_pointer
|
|||||||
/*!
|
/*!
|
||||||
@brief create and return a reference to the pointed to value
|
@brief create and return a reference to the pointed to value
|
||||||
|
|
||||||
@complexity Linear in the number of reference tokens.
|
Complexity: Linear in the number of reference tokens.
|
||||||
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
@throw parse_error.106 if an array index begins with '0'
|
||||||
@throw parse_error.109 if array index is not a number
|
@throw parse_error.109 if array index is not a number
|
||||||
@@ -19968,7 +19917,7 @@ class json_pointer
|
|||||||
|
|
||||||
@return reference to the JSON value pointed to by the JSON pointer
|
@return reference to the JSON value pointed to by the JSON pointer
|
||||||
|
|
||||||
@complexity Linear in the length of the JSON pointer.
|
Complexity: Linear in the length of the JSON pointer.
|
||||||
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
@throw parse_error.106 if an array index begins with '0'
|
||||||
@throw parse_error.109 if an array index was not a number
|
@throw parse_error.109 if an array index was not a number
|
||||||
@@ -20880,6 +20829,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <memory> // shared_ptr, make_shared
|
#include <memory> // shared_ptr, make_shared
|
||||||
#include <string> // basic_string
|
#include <string> // basic_string
|
||||||
|
#include <type_traits> // conditional, integral_constant, is_same
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
@@ -20988,11 +20938,13 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
|
|||||||
: stream(s)
|
: stream(s)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(portability-template-virtual-member-function)
|
||||||
void write_character(CharType c) override
|
void write_character(CharType c) override
|
||||||
{
|
{
|
||||||
stream.put(c);
|
stream.put(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(portability-template-virtual-member-function)
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
stream.write(s, static_cast<std::streamsize>(length));
|
stream.write(s, static_cast<std::streamsize>(length));
|
||||||
@@ -21059,7 +21011,82 @@ class output_adapter_sink
|
|||||||
output_adapter_t<CharType> oa;
|
output_adapter_t<CharType> oa;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename CharType, typename StringType = std::basic_string<CharType>>
|
/// @brief whether std::basic_string<CharType> has a non-deprecated std::char_traits
|
||||||
|
/// specialization, and is therefore usable as output_adapter's default StringType
|
||||||
|
///
|
||||||
|
/// std::char_traits is only guaranteed (and, on some standard libraries, only
|
||||||
|
/// implemented without a deprecation warning) for the character types listed
|
||||||
|
/// below; std::char_traits<T> for any other T (e.g. std::uint8_t, as used by the
|
||||||
|
/// binary writers) is a non-standard extension some standard libraries deprecate.
|
||||||
|
/// See https://github.com/nlohmann/json/issues/5725 item 2.
|
||||||
|
template<typename CharType>
|
||||||
|
struct is_output_adapter_string_char_type : std::integral_constant < bool,
|
||||||
|
std::is_same<CharType, char>::value ||
|
||||||
|
std::is_same<CharType, wchar_t>::value ||
|
||||||
|
std::is_same<CharType, char16_t>::value ||
|
||||||
|
std::is_same<CharType, char32_t>::value
|
||||||
|
#if defined(__cpp_lib_char8_t) && (__cpp_lib_char8_t >= 201907L)
|
||||||
|
|| std::is_same<CharType, char8_t>::value
|
||||||
|
#endif
|
||||||
|
> {};
|
||||||
|
|
||||||
|
/// @brief placeholder type for output_adapter's StringType and (with JSON_NO_IO
|
||||||
|
/// undefined) its std::basic_ostream constructor parameter, for CharType
|
||||||
|
/// with no non-deprecated std::char_traits specialization
|
||||||
|
///
|
||||||
|
/// Never actually used: the StringType- and std::basic_ostream-based
|
||||||
|
/// output_adapter constructors are neither documented nor tested for such
|
||||||
|
/// CharType (only the std::vector-based constructor is used for them, by the
|
||||||
|
/// binary writers). Naming std::basic_string<CharType> or
|
||||||
|
/// std::basic_ostream<CharType> anywhere such a constructor would otherwise be
|
||||||
|
/// declared - even as an unused default template argument or an unused,
|
||||||
|
/// never-called overload - instantiates std::char_traits<CharType> merely to
|
||||||
|
/// name the type, which is exactly what triggers the deprecation warning this
|
||||||
|
/// placeholder avoids.
|
||||||
|
template<typename CharType>
|
||||||
|
struct output_adapter_no_string_type {};
|
||||||
|
|
||||||
|
// Select output_adapter's default StringType (and, below, its ostream
|
||||||
|
// constructor's parameter type) via partial specialization, not
|
||||||
|
// std::conditional: std::conditional<B, T, F> requires both T and F to be named
|
||||||
|
// as template arguments up front, which would still instantiate (and thus name)
|
||||||
|
// std::basic_string<CharType> / std::basic_ostream<CharType> for every CharType,
|
||||||
|
// defeating the point. A bool non-type parameter with two specializations only
|
||||||
|
// ever names the type that is actually selected.
|
||||||
|
template<typename CharType, bool = is_output_adapter_string_char_type<CharType>::value>
|
||||||
|
struct output_adapter_default_string_type
|
||||||
|
{
|
||||||
|
using type = output_adapter_no_string_type<CharType>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename CharType>
|
||||||
|
struct output_adapter_default_string_type<CharType, true>
|
||||||
|
{
|
||||||
|
using type = std::basic_string<CharType>;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef JSON_NO_IO
|
||||||
|
/// distinct from output_adapter_no_string_type, so the placeholder overloads of
|
||||||
|
/// output_adapter's constructor (used when CharType is not a character type)
|
||||||
|
/// stay distinct overloads instead of colliding into a single redeclaration
|
||||||
|
template<typename CharType>
|
||||||
|
struct output_adapter_no_ostream_type {};
|
||||||
|
|
||||||
|
template<typename CharType, bool = is_output_adapter_string_char_type<CharType>::value>
|
||||||
|
struct output_adapter_ostream_type
|
||||||
|
{
|
||||||
|
using type = output_adapter_no_ostream_type<CharType>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename CharType>
|
||||||
|
struct output_adapter_ostream_type<CharType, true>
|
||||||
|
{
|
||||||
|
using type = std::basic_ostream<CharType>;
|
||||||
|
};
|
||||||
|
#endif // JSON_NO_IO
|
||||||
|
|
||||||
|
template < typename CharType, typename StringType =
|
||||||
|
typename output_adapter_default_string_type<CharType>::type >
|
||||||
class output_adapter
|
class output_adapter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -21068,7 +21095,7 @@ class output_adapter
|
|||||||
: oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
|
: oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
output_adapter(std::basic_ostream<CharType>& s)
|
output_adapter(typename output_adapter_ostream_type<CharType>::type& s)
|
||||||
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
|
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
|
|
||||||
@@ -21177,6 +21204,8 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or an object key is not valid UTF-8
|
||||||
@throw type_error.317 if @a j is not an object
|
@throw type_error.317 if @a j is not an object
|
||||||
*/
|
*/
|
||||||
void write_bson(const BasicJsonType& j)
|
void write_bson(const BasicJsonType& j)
|
||||||
@@ -21207,6 +21236,8 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or an object key is not valid UTF-8
|
||||||
*/
|
*/
|
||||||
void write_cbor(const BasicJsonType& j)
|
void write_cbor(const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
@@ -21273,6 +21304,8 @@ class binary_writer
|
|||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
|
check_text_utf8(*j.m_data.m_value.string, j);
|
||||||
|
|
||||||
// step 1: write control byte and the string length
|
// step 1: write control byte and the string length
|
||||||
write_cbor_head(0x60, j.m_data.m_value.string->size());
|
write_cbor_head(0x60, j.m_data.m_value.string->size());
|
||||||
|
|
||||||
@@ -21349,6 +21382,11 @@ class binary_writer
|
|||||||
// step 2: write each element
|
// step 2: write each element
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
for (const auto& el : *j.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
|
// el.first is checked here, against the object as
|
||||||
|
// diagnostics context, because write_cbor(el.first)
|
||||||
|
// converts it to a temporary basic_json that would be
|
||||||
|
// used as the context instead
|
||||||
|
check_text_utf8(el.first, j);
|
||||||
write_cbor(el.first);
|
write_cbor(el.first);
|
||||||
write_cbor(el.second);
|
write_cbor(el.second);
|
||||||
}
|
}
|
||||||
@@ -21691,6 +21729,8 @@ class binary_writer
|
|||||||
@param[in] add_prefix whether prefixes need to be used for this value
|
@param[in] add_prefix whether prefixes need to be used for this value
|
||||||
@param[in] use_bjdata whether write in BJData format, default is false
|
@param[in] use_bjdata whether write in BJData format, default is false
|
||||||
@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 JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or an object key is not valid UTF-8
|
||||||
*/
|
*/
|
||||||
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,
|
||||||
@@ -21740,6 +21780,8 @@ class binary_writer
|
|||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
|
check_text_utf8(*j.m_data.m_value.string, j);
|
||||||
|
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
{
|
{
|
||||||
oa.write_character(to_char_type('S'));
|
oa.write_character(to_char_type('S'));
|
||||||
@@ -21902,6 +21944,7 @@ class binary_writer
|
|||||||
|
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
for (const auto& el : *j.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
|
check_text_utf8(el.first, j);
|
||||||
write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
|
write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
|
||||||
oa.write_characters(
|
oa.write_characters(
|
||||||
reinterpret_cast<const CharType*>(el.first.data()),
|
reinterpret_cast<const CharType*>(el.first.data()),
|
||||||
@@ -21946,6 +21989,10 @@ class binary_writer
|
|||||||
/*!
|
/*!
|
||||||
@return The size of a BSON document entry header, including the id marker
|
@return The size of a BSON document entry header, including the id marker
|
||||||
and the entry name size (and its null-terminator).
|
and the entry name size (and its null-terminator).
|
||||||
|
@throw out_of_range.409 if @a name contains U+0000, before anything is
|
||||||
|
written
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a name is
|
||||||
|
not valid UTF-8, before anything is written
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
|
static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
@@ -21955,7 +22002,8 @@ class binary_writer
|
|||||||
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
static_cast<void>(j);
|
check_text_utf8(name, j);
|
||||||
|
|
||||||
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22011,9 +22059,21 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return The size of the BSON-encoded string in @a value
|
@return The size of the BSON-encoded string in @a value
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a value
|
||||||
|
is not valid UTF-8, before anything is written
|
||||||
|
|
||||||
|
@note The UTF-8 check is skipped if @a value is already too long for the
|
||||||
|
32-bit BSON length field (@ref to_bson_length rejects it later, once
|
||||||
|
the size of the whole document is known); this also keeps the check
|
||||||
|
from reading past a StringType that reports a size larger than what
|
||||||
|
it actually holds.
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_string_size(const string_t& value)
|
static std::size_t calc_bson_string_size(const string_t& value, const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
|
if (JSON_HEDLEY_LIKELY(value_in_range_of<std::int32_t>(value.size())))
|
||||||
|
{
|
||||||
|
check_text_utf8(value, j);
|
||||||
|
}
|
||||||
return sizeof(std::int32_t) + value.size() + 1ul;
|
return sizeof(std::int32_t) + value.size() + 1ul;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22142,6 +22202,8 @@ class binary_writer
|
|||||||
is neither an object nor an array
|
is neither an object nor an array
|
||||||
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
|
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
|
||||||
into a byte, before anything is written
|
into a byte, before anything is written
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a j is a
|
||||||
|
string that is not valid UTF-8, before anything is written
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_value_size(const BasicJsonType& j)
|
static std::size_t calc_bson_value_size(const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
@@ -22163,7 +22225,7 @@ class binary_writer
|
|||||||
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
return calc_bson_string_size(*j.m_data.m_value.string);
|
return calc_bson_string_size(*j.m_data.m_value.string, j);
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
return 0ul;
|
return 0ul;
|
||||||
@@ -22276,6 +22338,8 @@ class binary_writer
|
|||||||
written
|
written
|
||||||
@throw out_of_range.415 if a binary value's subtype does not fit into a
|
@throw out_of_range.415 if a binary value's subtype does not fit into a
|
||||||
byte, before anything is written
|
byte, before anything is written
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and a string
|
||||||
|
value or a key is not valid UTF-8, before anything is written
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
static std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
||||||
{
|
{
|
||||||
@@ -23154,7 +23218,7 @@ class binary_writer
|
|||||||
*/
|
*/
|
||||||
void write_bon8_string(const string_t& s, bool& string_open, const BasicJsonType& context)
|
void write_bon8_string(const string_t& s, bool& string_open, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
check_bon8_utf8(s, context);
|
check_utf8(s, context);
|
||||||
|
|
||||||
// a string that follows another string terminates it
|
// a string that follows another string terminates it
|
||||||
if (string_open)
|
if (string_open)
|
||||||
@@ -23184,7 +23248,7 @@ class binary_writer
|
|||||||
@throw type_error.316 if @a s is not valid UTF-8; the message names the
|
@throw type_error.316 if @a s is not valid UTF-8; the message names the
|
||||||
first byte of the first invalid or incomplete sequence
|
first byte of the first invalid or incomplete sequence
|
||||||
*/
|
*/
|
||||||
static void check_bon8_utf8(const string_t& s, const BasicJsonType& context)
|
static void check_utf8(const string_t& s, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
static_cast<void>(context); // only used when exceptions are enabled
|
static_cast<void>(context); // only used when exceptions are enabled
|
||||||
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
||||||
@@ -23195,6 +23259,29 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief check a CBOR, UBJSON, BJData, or BSON text string for valid UTF-8
|
||||||
|
|
||||||
|
The check only happens if JSON_STRICT_BINARY_UTF8 is enabled. Otherwise,
|
||||||
|
the bytes are written unchanged, as before version 3.13.0. MessagePack
|
||||||
|
always writes the bytes as is, and BON8 always checks them (see
|
||||||
|
@ref check_utf8).
|
||||||
|
|
||||||
|
@param[in] s the string to check
|
||||||
|
@param[in] context the value that holds @a s (for diagnostics)
|
||||||
|
@throw type_error.316 if JSON_STRICT_BINARY_UTF8 is enabled and @a s is
|
||||||
|
not valid UTF-8
|
||||||
|
*/
|
||||||
|
static void check_text_utf8(const string_t& s, const BasicJsonType& context)
|
||||||
|
{
|
||||||
|
#if JSON_STRICT_BINARY_UTF8
|
||||||
|
check_utf8(s, context);
|
||||||
|
#else
|
||||||
|
static_cast<void>(s);
|
||||||
|
static_cast<void>(context);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write an integer in the shortest encoding
|
@brief write an integer in the shortest encoding
|
||||||
|
|
||||||
@@ -24727,7 +24814,7 @@ class serializer
|
|||||||
@param[in] ichar indentation character to use
|
@param[in] ichar indentation character to use
|
||||||
@param[in] pretty_print_ whether the output shall be pretty-printed
|
@param[in] pretty_print_ whether the output shall be pretty-printed
|
||||||
@param[in] ensure_ascii_ If @a ensure_ascii_ is true, all non-ASCII
|
@param[in] ensure_ascii_ If @a ensure_ascii_ is true, all non-ASCII
|
||||||
characters in the output are escaped with `\uXXXX` sequences, and the
|
characters in the output are escaped with `\\uXXXX` sequences, and the
|
||||||
result consists of ASCII characters only.
|
result consists of ASCII characters only.
|
||||||
@param[in] indent_step_ the indent level
|
@param[in] indent_step_ the indent level
|
||||||
@param[in] error_handler_ how to react on decoding errors
|
@param[in] error_handler_ how to react on decoding errors
|
||||||
@@ -25352,7 +25439,7 @@ class serializer
|
|||||||
|
|
||||||
@param[in] s the string to escape
|
@param[in] s the string to escape
|
||||||
|
|
||||||
@complexity Linear in the length of string @a s.
|
Complexity: Linear in the length of string @a s.
|
||||||
*/
|
*/
|
||||||
void dump_escaped(const string_t& s)
|
void dump_escaped(const string_t& s)
|
||||||
{
|
{
|
||||||
@@ -25856,7 +25943,7 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
* @brief write a lowercase "\\uXXXX" escape sequence into @a string_buffer
|
||||||
*
|
*
|
||||||
* Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the
|
* Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the
|
||||||
* string escaping hot path. It writes exactly six characters ('\\', 'u' and
|
* string escaping hot path. It writes exactly six characters ('\\', 'u' and
|
||||||
@@ -26206,7 +26293,7 @@ class serializer
|
|||||||
/// whether to pretty-print the output
|
/// whether to pretty-print the output
|
||||||
const bool pretty_print;
|
const bool pretty_print;
|
||||||
|
|
||||||
/// whether to escape non-ASCII characters with \uXXXX sequences
|
/// whether to escape non-ASCII characters with \\uXXXX sequences
|
||||||
const bool ensure_ascii;
|
const bool ensure_ascii;
|
||||||
|
|
||||||
/// the indent level
|
/// the indent level
|
||||||
@@ -29156,12 +29243,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
@throw what @ref json_serializer<ValueType> `from_json()` method throws
|
@throw what @ref json_serializer<ValueType> `from_json()` method throws
|
||||||
|
|
||||||
@liveexample{The example below shows several conversions from JSON values
|
The example below shows several conversions from JSON values
|
||||||
to other types. There a few things to note: (1) Floating-point numbers can
|
to other types. There a few things to note: (1) Floating-point numbers can
|
||||||
be converted to integers\, (2) A JSON array can be converted to a standard
|
be converted to integers, (2) A JSON array can be converted to a standard
|
||||||
`std::vector<short>`\, (3) A JSON object can be converted to C++
|
`std::vector<short>`, (3) A JSON object can be converted to C++
|
||||||
associative containers such as `std::unordered_map<std::string\,
|
associative containers such as `std::unordered_map<std::string,
|
||||||
json>`.,get__ValueType_const}
|
json>`.
|
||||||
|
|
||||||
@since version 2.1.0
|
@since version 2.1.0
|
||||||
*/
|
*/
|
||||||
@@ -29228,7 +29315,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
@return a copy of *this, converted into @a BasicJsonType
|
@return a copy of *this, converted into @a BasicJsonType
|
||||||
|
|
||||||
@complexity Depending on the implementation of the called `from_json()`
|
Complexity: Depending on the implementation of the called `from_json()`
|
||||||
method.
|
method.
|
||||||
|
|
||||||
@since version 3.2.0
|
@since version 3.2.0
|
||||||
@@ -29252,7 +29339,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
@return a copy of *this
|
@return a copy of *this
|
||||||
|
|
||||||
@complexity Constant.
|
Complexity: Constant.
|
||||||
|
|
||||||
@since version 2.1.0
|
@since version 2.1.0
|
||||||
*/
|
*/
|
||||||
@@ -29298,7 +29385,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
@tparam ValueTypeCV the provided value type
|
@tparam ValueTypeCV the provided value type
|
||||||
@tparam ValueType the returned value type
|
@tparam ValueType the returned value type
|
||||||
|
|
||||||
@return copy of the JSON value, converted to @tparam ValueType if necessary
|
@return copy of the JSON value, converted to @a ValueType if necessary
|
||||||
|
|
||||||
@throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
|
@throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
|
||||||
|
|
||||||
@@ -29336,12 +29423,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
@return pointer to the internally stored JSON value if the requested
|
@return pointer to the internally stored JSON value if the requested
|
||||||
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
|
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
|
||||||
|
|
||||||
@complexity Constant.
|
Complexity: Constant.
|
||||||
|
|
||||||
@liveexample{The example below shows how pointers to internal values of a
|
The example below shows how pointers to internal values of a
|
||||||
JSON value can be requested. Note that no type conversions are made and a
|
JSON value can be requested. Note that no type conversions are made and a
|
||||||
`nullptr` is returned if the value and the requested pointer type does not
|
`nullptr` is returned if the value and the requested pointer type does not
|
||||||
match.,get__PointerType}
|
match.
|
||||||
|
|
||||||
@sa see @ref get_ptr() for explicit pointer-member access
|
@sa see @ref get_ptr() for explicit pointer-member access
|
||||||
|
|
||||||
@@ -29435,14 +29522,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
to the JSON value type (e.g., the JSON value is of type boolean, but a
|
to the JSON value type (e.g., the JSON value is of type boolean, but a
|
||||||
string is requested); see example below
|
string is requested); see example below
|
||||||
|
|
||||||
@complexity Linear in the size of the JSON value.
|
Complexity: Linear in the size of the JSON value.
|
||||||
|
|
||||||
@liveexample{The example below shows several conversions from JSON values
|
The example below shows several conversions from JSON values
|
||||||
to other types. There a few things to note: (1) Floating-point numbers can
|
to other types. There a few things to note: (1) Floating-point numbers can
|
||||||
be converted to integers\, (2) A JSON array can be converted to a standard
|
be converted to integers, (2) A JSON array can be converted to a standard
|
||||||
`std::vector<short>`\, (3) A JSON object can be converted to C++
|
`std::vector<short>`, (3) A JSON object can be converted to C++
|
||||||
associative containers such as `std::unordered_map<std::string\,
|
associative containers such as `std::unordered_map<std::string,
|
||||||
json>`.,operator__ValueType}
|
json>`.
|
||||||
|
|
||||||
@since version 1.0.0
|
@since version 1.0.0
|
||||||
*/
|
*/
|
||||||
@@ -31897,6 +31984,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @deprecated This function is deprecated since 3.8.0 and will be removed in
|
/// @deprecated This function is deprecated since 3.8.0 and will be removed in
|
||||||
/// version 4.0.0 of the library. Please use
|
/// version 4.0.0 of the library. Please use
|
||||||
/// sax_parse(ptr, ptr + len) instead.
|
/// sax_parse(ptr, ptr + len) instead.
|
||||||
|
//
|
||||||
|
// Clang reports "declaration is marked with '@deprecated' command but does
|
||||||
|
// not have a deprecation attribute" for this overload even though
|
||||||
|
// JSON_HEDLEY_DEPRECATED_FOR below does expand to __attribute__((deprecated));
|
||||||
|
// isolated reproductions of this exact declaration shape (doc comment,
|
||||||
|
// template<>, two stacked __attribute__ lines, an overload set of the same
|
||||||
|
// name) do not reproduce it, so this looks like a Clang comment/declaration
|
||||||
|
// association quirk specific to this overload within basic_json, not a
|
||||||
|
// genuine documentation bug. See #5725 item 2.
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdocumentation-deprecated-sync"
|
||||||
|
#endif
|
||||||
template <typename SAX>
|
template <typename SAX>
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
@@ -31913,6 +32013,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
||||||
}
|
}
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
/// @brief deserialize from stream
|
/// @brief deserialize from stream
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
|
||||||
@@ -33575,7 +33678,7 @@ namespace std // NOLINT(cert-dcl58-cpp)
|
|||||||
/// @brief hash value for JSON objects
|
/// @brief hash value for JSON objects
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
|
/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
|
||||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||||
struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp)
|
struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp,bugprone-std-namespace-modification)
|
||||||
{
|
{
|
||||||
std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
|
std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
|
||||||
{
|
{
|
||||||
@@ -33608,7 +33711,7 @@ struct less< ::nlohmann::detail::value_t> // do not remove the space after '<',
|
|||||||
/// @brief exchanges the values of two JSON objects
|
/// @brief exchanges the values of two JSON objects
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/std_swap/
|
/// @sa https://json.nlohmann.me/api/basic_json/std_swap/
|
||||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||||
inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp)
|
inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp,bugprone-std-namespace-modification)
|
||||||
is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
|
is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
|
||||||
is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
|
is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
|
||||||
{
|
{
|
||||||
@@ -33622,7 +33725,7 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
|
|||||||
/// @brief std::formatter specialization for JSON values
|
/// @brief std::formatter specialization for JSON values
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/std_formatter/
|
/// @sa https://json.nlohmann.me/api/basic_json/std_formatter/
|
||||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||||
struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-cpp)
|
struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-cpp,bugprone-std-namespace-modification)
|
||||||
{
|
{
|
||||||
// -1 means compact output (dump()); any value >= 0 means pretty-printed
|
// -1 means compact output (dump()); any value >= 0 means pretty-printed
|
||||||
// output with that many spaces (or indent_char) per level (dump(indent, indent_char)).
|
// output with that many spaces (or indent_char) per level (dump(indent, indent_char)).
|
||||||
@@ -33711,11 +33814,6 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// restore clang diagnostic settings
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
#undef JSON_ASSERT
|
#undef JSON_ASSERT
|
||||||
#undef JSON_INTERNAL_CATCH
|
#undef JSON_INTERNAL_CATCH
|
||||||
@@ -33749,6 +33847,7 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
#undef JSON_PRECISE_STREAM_POSITION
|
#undef JSON_PRECISE_STREAM_POSITION
|
||||||
#undef JSON_STRICT_NUL_HANDLING
|
#undef JSON_STRICT_NUL_HANDLING
|
||||||
|
#undef JSON_STRICT_BINARY_UTF8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
@@ -33916,11 +34015,6 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
|
|
||||||
// IWYU pragma: keep
|
// IWYU pragma: keep
|
||||||
|
|
||||||
// End of GCC diagnostic pragmas for C++ modules support
|
|
||||||
#if defined(__GNUC__) && !defined(__clang__) && __cplusplus >= 202002L
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The user-defined string literals are in a separate header, because their
|
// The user-defined string literals are in a separate header, because their
|
||||||
// bodies instantiate the parser in every translation unit that includes them.
|
// bodies instantiate the parser in every translation unit that includes them.
|
||||||
// Define JSON_NO_AUTOMATIC_UDLS to include <nlohmann/json_literals.hpp> only
|
// Define JSON_NO_AUTOMATIC_UDLS to include <nlohmann/json_literals.hpp> only
|
||||||
|
|||||||
@@ -63,6 +63,10 @@
|
|||||||
#define JSON_STRICT_NUL_HANDLING 0
|
#define JSON_STRICT_NUL_HANDLING 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_STRICT_BINARY_UTF8
|
||||||
|
#define JSON_STRICT_BINARY_UTF8 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -99,14 +103,20 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JSON_STRICT_BINARY_UTF8
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8 _sbu8
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -115,7 +125,8 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ TEST_CASE("default namespace")
|
|||||||
expected += "_snul";
|
expected += "_snul";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JSON_STRICT_BINARY_UTF8
|
||||||
|
expected += "_sbu8";
|
||||||
|
#endif
|
||||||
|
|
||||||
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ TEST_CASE("default namespace without version component")
|
|||||||
expected += "_snul";
|
expected += "_snul";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JSON_STRICT_BINARY_UTF8
|
||||||
|
expected += "_sbu8";
|
||||||
|
#endif
|
||||||
|
|
||||||
expected += "::basic_json";
|
expected += "::basic_json";
|
||||||
|
|
||||||
// fallback for Clang
|
// fallback for Clang
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
|
// The binary writers check strings and object keys for valid UTF-8 only if
|
||||||
|
// JSON_STRICT_BINARY_UTF8 is enabled (planned to be the default in 4.0.0).
|
||||||
|
// Without it, they write the bytes unchanged, as before version 3.13.0; the
|
||||||
|
// tests for that are next to the other tests of each format.
|
||||||
|
#ifdef JSON_STRICT_BINARY_UTF8
|
||||||
|
#undef JSON_STRICT_BINARY_UTF8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JSON_STRICT_BINARY_UTF8 1
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
TEST_CASE("JSON_STRICT_BINARY_UTF8 (see #5529, #5651)")
|
||||||
|
{
|
||||||
|
SECTION("CBOR")
|
||||||
|
{
|
||||||
|
// a string value with ill-formed UTF-8 is rejected
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xFF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xC3")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xED\xA0\x80")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xC0\xAF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is rejected the same way
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_cbor(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
|
||||||
|
// binary values are not text and are unaffected
|
||||||
|
CHECK_NOTHROW(json::to_cbor(json::binary(std::vector<std::uint8_t>({0xFF}))));
|
||||||
|
|
||||||
|
// a value read back from CBOR with ill-formed bytes cannot be written
|
||||||
|
// back either (the reader is lenient regardless of the macro)
|
||||||
|
const json j = json::from_cbor(std::vector<std::uint8_t>({0x62, 0xc0, 0xae}));
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("UBJSON")
|
||||||
|
{
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xFF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xC3")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xED\xA0\x80")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xC0\xAF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is rejected the same way
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("BJData")
|
||||||
|
{
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xFF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xC3")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xED\xA0\x80")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xC0\xAF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is rejected the same way
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("BSON")
|
||||||
|
{
|
||||||
|
// to_bson() rejects the same kind of ill-formed string value, before
|
||||||
|
// any bytes reach the output adapter (the BSON document length
|
||||||
|
// prefix must be known up front, so nothing is written incrementally)
|
||||||
|
std::vector<std::uint8_t> out{0x42}; // a sentinel byte the writer must not touch
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}, nlohmann::detail::output_adapter<std::uint8_t>(out)), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
CHECK(out == std::vector<std::uint8_t> {0x42});
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC3"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is rejected as well; unlike
|
||||||
|
// the reader (which never validates element names), the writer
|
||||||
|
// checks both string values and object keys
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("MessagePack and BON8 are unaffected")
|
||||||
|
{
|
||||||
|
// MessagePack allows any bytes in a str, so to_msgpack() writes them as
|
||||||
|
// is; BON8 always checks, because the lead bytes mark where strings end
|
||||||
|
CHECK(json::to_msgpack(json("\xFF")) == std::vector<std::uint8_t>({0xa1, 0xff}));
|
||||||
|
CHECK_THROWS_AS(json::to_bon8(json("\xFF")), json::type_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3907,6 +3907,43 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
CHECK(json::to_bjdata(j) == v);
|
CHECK(json::to_bjdata(j) == v);
|
||||||
CHECK(json::from_bjdata(v) == j);
|
CHECK(json::from_bjdata(v) == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("ill-formed UTF-8 (see #5529, #5651)")
|
||||||
|
{
|
||||||
|
// none of the binary format specs requires a decoder to reject
|
||||||
|
// ill-formed UTF-8 in a text string, so a value whose bytes are
|
||||||
|
// not valid UTF-8 (0xC0 0xAE is an overlong encoding of '.')
|
||||||
|
// round-trips byte for byte as a string value; to_bjdata() writes
|
||||||
|
// the bytes unchanged, as before 3.13.0, unless
|
||||||
|
// JSON_STRICT_BINARY_UTF8 is enabled (see
|
||||||
|
// unit-binary_utf8_strict.cpp)
|
||||||
|
const std::vector<uint8_t> v = {'S', 'i', 2, 0xc0, 0xae};
|
||||||
|
json j;
|
||||||
|
CHECK_NOTHROW(j = json::from_bjdata(v));
|
||||||
|
REQUIRE(j.is_string());
|
||||||
|
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
|
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(j)) == j);
|
||||||
|
|
||||||
|
// the same bytes as an object key round-trip as well
|
||||||
|
const std::vector<uint8_t> v_key = {'{', 'i', 2, 0xc0, 0xae, 'i', 1, '}'};
|
||||||
|
json j_key;
|
||||||
|
CHECK_NOTHROW(j_key = json::from_bjdata(v_key));
|
||||||
|
REQUIRE(j_key.is_object());
|
||||||
|
CHECK(j_key.contains(std::string("\xc0\xae")));
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(j_key)) == j_key);
|
||||||
|
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(json("\xFF"))) == json("\xFF"));
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(json("\xC3"))) == json("\xC3"));
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(json("\xED\xA0\x80"))) == json("\xED\xA0\x80"));
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(json("\xC0\xAF"))) == json("\xC0\xAF"));
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is kept the same way
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Array Type")
|
SECTION("Array Type")
|
||||||
|
|||||||
@@ -154,6 +154,43 @@ TEST_CASE("BSON")
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("ill-formed UTF-8 (see #5529, #5651)")
|
||||||
|
{
|
||||||
|
// a BSON document {"s": "\xC0\xAE"} (0xC0 0xAE is an overlong
|
||||||
|
// encoding of '.'); the BSON spec does not require a decoder to
|
||||||
|
// reject ill-formed UTF-8 in a string value, so the reader hands the
|
||||||
|
// bytes back unchanged
|
||||||
|
const std::vector<uint8_t> v =
|
||||||
|
{
|
||||||
|
0x0F, 0x00, 0x00, 0x00, // document length
|
||||||
|
0x02, 's', 0x00, // type 0x02 (string), key "s"
|
||||||
|
0x03, 0x00, 0x00, 0x00, // string length (including null)
|
||||||
|
0xc0, 0xae, 0x00, // string content and its null terminator
|
||||||
|
0x00 // document terminator
|
||||||
|
};
|
||||||
|
json j;
|
||||||
|
CHECK_NOTHROW(j = json::from_bson(v));
|
||||||
|
REQUIRE(j.is_object());
|
||||||
|
REQUIRE(j.contains("s"));
|
||||||
|
CHECK(j["s"].get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
|
// dump() still requires valid UTF-8 and throws for such a value
|
||||||
|
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||||
|
// to_bson() writes the bytes back unchanged, as before 3.13.0,
|
||||||
|
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
||||||
|
CHECK(json::from_bson(json::to_bson(j)) == j);
|
||||||
|
|
||||||
|
CHECK(json::from_bson(json::to_bson(json{{"s", "\xFF"}})) == json{{"s", "\xFF"}});
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK(json::from_bson(json::to_bson(json{{"s", "\xC3"}})) == json{{"s", "\xC3"}});
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK(json::from_bson(json::to_bson(json{{"s", "\xED\xA0\x80"}})) == json{{"s", "\xED\xA0\x80"}});
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK(json::from_bson(json::to_bson(json{{"s", "\xC0\xAF"}})) == json{{"s", "\xC0\xAF"}});
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is kept as well
|
||||||
|
CHECK(json::from_bson(json::to_bson(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("lengths exceeding INT32_MAX cannot be serialized to BSON")
|
SECTION("lengths exceeding INT32_MAX cannot be serialized to BSON")
|
||||||
{
|
{
|
||||||
// out_of_range.412 is thrown from a single shared helper
|
// out_of_range.412 is thrown from a single shared helper
|
||||||
|
|||||||
+67
-18
@@ -1801,19 +1801,41 @@ TEST_CASE("CBOR")
|
|||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0x7C, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0x7C, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("invalid UTF-8 in string (see #5529)")
|
SECTION("ill-formed UTF-8 in string (see #5529, #5651)")
|
||||||
{
|
{
|
||||||
|
// RFC 8949 §3.1 leaves it up to the decoder whether to reject
|
||||||
|
// ill-formed UTF-8 in a text string; this library does not, and
|
||||||
|
// hands the original bytes back unchanged, matching the
|
||||||
|
// MessagePack reader and the behavior before #5185/#5531 (not in
|
||||||
|
// any release)
|
||||||
|
|
||||||
// a two-character text string (major type 3) whose bytes are not
|
// a two-character text string (major type 3) whose bytes are not
|
||||||
// valid UTF-8 (0xC0 0xAE is an overlong encoding of '.') must be
|
// valid UTF-8 (0xC0 0xAE is an overlong encoding of '.') round-trips
|
||||||
// rejected at decode time, matching every other kind of
|
// byte for byte as a string value
|
||||||
// malformed binary input, rather than only failing later when
|
const std::vector<uint8_t> ill_formed_value = {0x62, 0xc0, 0xae};
|
||||||
// the resulting value is dumped
|
json j_value;
|
||||||
json _;
|
CHECK_NOTHROW(j_value = json::from_cbor(ill_formed_value));
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x62, 0xc0, 0xae})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
REQUIRE(j_value.is_string());
|
||||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x62, 0xc0, 0xae}), true, false).is_discarded());
|
CHECK(j_value.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
|
// dump() still requires valid UTF-8 and throws for such a value,
|
||||||
|
// unless an error handler that replaces or ignores the bytes is
|
||||||
|
// passed
|
||||||
|
CHECK_THROWS_AS(j_value.dump(), json::type_error&);
|
||||||
|
// to_cbor() writes the bytes back unchanged, as before 3.13.0,
|
||||||
|
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(j_value)) == j_value);
|
||||||
|
|
||||||
|
// the same bytes as an object key round-trip as well
|
||||||
|
const std::vector<uint8_t> ill_formed_key = {0xa1, 0x62, 0xc0, 0xae, 0x01};
|
||||||
|
json j_key;
|
||||||
|
CHECK_NOTHROW(j_key = json::from_cbor(ill_formed_key));
|
||||||
|
REQUIRE(j_key.is_object());
|
||||||
|
CHECK(j_key.contains(std::string("\xc0\xae")));
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(j_key)) == j_key);
|
||||||
|
|
||||||
// a CBOR byte string (major type 2) with the very same bytes is
|
// a CBOR byte string (major type 2) with the very same bytes is
|
||||||
// NOT text and must still be accepted as-is
|
// NOT text and must still be accepted as-is
|
||||||
|
json _;
|
||||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x42, 0xc0, 0xae})));
|
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x42, 0xc0, 0xae})));
|
||||||
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
||||||
|
|
||||||
@@ -1822,17 +1844,47 @@ TEST_CASE("CBOR")
|
|||||||
CHECK(json::from_cbor(json::to_cbor(j)) == j);
|
CHECK(json::from_cbor(json::to_cbor(j)) == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("invalid UTF-8 in indefinite-length string")
|
SECTION("to_cbor keeps ill-formed UTF-8 (see #5651)")
|
||||||
|
{
|
||||||
|
// to_cbor() writes the bytes unchanged, as before 3.13.0, unless
|
||||||
|
// JSON_STRICT_BINARY_UTF8 is enabled (see
|
||||||
|
// unit-binary_utf8_strict.cpp); from_cbor() reads them back as is
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(json("\xFF"))) == json("\xFF"));
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(json("\xC3"))) == json("\xC3"));
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(json("\xED\xA0\x80"))) == json("\xED\xA0\x80"));
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(json("\xC0\xAF"))) == json("\xC0\xAF"));
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is kept the same way
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
||||||
|
|
||||||
|
// binary values are not text and are unaffected
|
||||||
|
CHECK_NOTHROW(json::to_cbor(json::binary(std::vector<std::uint8_t>({0xFF}))));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ill-formed UTF-8 in indefinite-length string")
|
||||||
{
|
{
|
||||||
json _;
|
json _;
|
||||||
|
|
||||||
// every chunk must be valid UTF-8 on its own (RFC 8949, Section
|
// the chunks are concatenated as is, without checking that each
|
||||||
// 3.2.3), so a code point split across two chunks is rejected
|
// chunk is valid UTF-8 on its own (RFC 8949, Section 3.2.3), so
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
// a code point split across two chunks yields a valid string
|
||||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff}), true, false).is_discarded());
|
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff})));
|
||||||
|
CHECK(_ == "\xc3\xa9");
|
||||||
|
CHECK(_.dump() == "\"\xc3\xa9\"");
|
||||||
|
|
||||||
// an ill-formed later chunk is rejected after valid ones
|
// a truncated code point is kept as is
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc0, 0xae, 0xff})), "[json.exception.parse_error.113] parse error at byte 7: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0xff})));
|
||||||
|
CHECK(_ == "\xc3");
|
||||||
|
CHECK_THROWS_AS(_.dump(), json::type_error&);
|
||||||
|
CHECK(json::from_cbor(json::to_cbor(_)) == _);
|
||||||
|
|
||||||
|
// an ill-formed later chunk is kept after valid ones
|
||||||
|
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc0, 0xae, 0xff})));
|
||||||
|
CHECK(_ == "\xc3\xa9\xc0\xae");
|
||||||
|
CHECK_THROWS_AS(_.dump(), json::type_error&);
|
||||||
|
|
||||||
// valid multi-byte chunks are accepted
|
// valid multi-byte chunks are accepted
|
||||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
|
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
|
||||||
@@ -1840,9 +1892,6 @@ TEST_CASE("CBOR")
|
|||||||
|
|
||||||
SECTION("many chunks in indefinite-length string")
|
SECTION("many chunks in indefinite-length string")
|
||||||
{
|
{
|
||||||
// only the newly read chunk is validated, not the whole string
|
|
||||||
// collected so far; validating the latter made this input take
|
|
||||||
// quadratic time (about ten seconds for 100000 chunks)
|
|
||||||
constexpr std::size_t chunks = 100000;
|
constexpr std::size_t chunks = 100000;
|
||||||
std::vector<uint8_t> v{0x7f};
|
std::vector<uint8_t> v{0x7f};
|
||||||
for (std::size_t i = 0; i < chunks; ++i)
|
for (std::size_t i = 0; i < chunks; ++i)
|
||||||
|
|||||||
@@ -1540,19 +1540,39 @@ TEST_CASE("MessagePack")
|
|||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("invalid UTF-8 in string (see #5529)")
|
SECTION("ill-formed UTF-8 in string (see #5529, #5651)")
|
||||||
{
|
{
|
||||||
|
// the MessagePack specification explicitly allows a str object to
|
||||||
|
// contain a byte sequence that is not valid UTF-8 and expects a
|
||||||
|
// deserializer to hand the original bytes back unchanged; this
|
||||||
|
// library follows that, unlike CBOR/UBJSON/BJData/BSON, whose
|
||||||
|
// specifications require text strings to be valid UTF-8
|
||||||
|
|
||||||
// a fixstr of length 2 (0xA0 | 2) whose bytes are not valid UTF-8
|
// a fixstr of length 2 (0xA0 | 2) whose bytes are not valid UTF-8
|
||||||
// (0xC0 0xAE is an overlong encoding of '.') must be rejected at
|
// (0xC0 0xAE is an overlong encoding of '.') round-trips byte for
|
||||||
// decode time, matching every other kind of malformed binary
|
// byte as a string value
|
||||||
// input, rather than only failing later when the resulting
|
const std::vector<uint8_t> ill_formed_value = {0xa2, 0xc0, 0xae};
|
||||||
// value is dumped
|
json j_value;
|
||||||
json _;
|
CHECK_NOTHROW(j_value = json::from_msgpack(ill_formed_value));
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0xa2, 0xc0, 0xae})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing MessagePack string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
REQUIRE(j_value.is_string());
|
||||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xa2, 0xc0, 0xae}), true, false).is_discarded());
|
CHECK(j_value.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(j_value)) == j_value);
|
||||||
|
// dump() still requires valid UTF-8 and throws for such a value,
|
||||||
|
// unless an error handler that replaces or ignores the bytes is
|
||||||
|
// passed
|
||||||
|
CHECK_THROWS_AS(j_value.dump(), json::type_error&);
|
||||||
|
|
||||||
|
// the same bytes as an object key round-trip as well
|
||||||
|
const std::vector<uint8_t> ill_formed_key = {0x81, 0xa2, 0xc0, 0xae, 0x01};
|
||||||
|
json j_key;
|
||||||
|
CHECK_NOTHROW(j_key = json::from_msgpack(ill_formed_key));
|
||||||
|
REQUIRE(j_key.is_object());
|
||||||
|
CHECK(j_key.contains(std::string("\xc0\xae")));
|
||||||
|
CHECK(json::from_msgpack(json::to_msgpack(j_key)) == j_key);
|
||||||
|
|
||||||
// a MessagePack bin8 blob with the very same bytes is NOT text
|
// a MessagePack bin8 blob with the very same bytes is NOT text
|
||||||
// and must still be accepted as-is
|
// and must still be accepted as-is
|
||||||
|
json _;
|
||||||
CHECK_NOTHROW(_ = json::from_msgpack(std::vector<uint8_t>({0xc4, 0x02, 0xc0, 0xae})));
|
CHECK_NOTHROW(_ = json::from_msgpack(std::vector<uint8_t>({0xc4, 0x02, 0xc0, 0xae})));
|
||||||
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
||||||
|
|
||||||
|
|||||||
@@ -2505,6 +2505,43 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
CHECK(json::to_ubjson(j) == v);
|
CHECK(json::to_ubjson(j) == v);
|
||||||
CHECK(json::from_ubjson(v) == j);
|
CHECK(json::from_ubjson(v) == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("ill-formed UTF-8 (see #5529, #5651)")
|
||||||
|
{
|
||||||
|
// none of the binary format specs requires a decoder to reject
|
||||||
|
// ill-formed UTF-8 in a text string, so a value whose bytes are
|
||||||
|
// not valid UTF-8 (0xC0 0xAE is an overlong encoding of '.')
|
||||||
|
// round-trips byte for byte as a string value; to_ubjson() writes
|
||||||
|
// the bytes unchanged, as before 3.13.0, unless
|
||||||
|
// JSON_STRICT_BINARY_UTF8 is enabled (see
|
||||||
|
// unit-binary_utf8_strict.cpp)
|
||||||
|
const std::vector<uint8_t> v = {'S', 'i', 2, 0xc0, 0xae};
|
||||||
|
json j;
|
||||||
|
CHECK_NOTHROW(j = json::from_ubjson(v));
|
||||||
|
REQUIRE(j.is_string());
|
||||||
|
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
|
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(j)) == j);
|
||||||
|
|
||||||
|
// the same bytes as an object key round-trip as well
|
||||||
|
const std::vector<uint8_t> v_key = {'{', 'i', 2, 0xc0, 0xae, 'i', 1, '}'};
|
||||||
|
json j_key;
|
||||||
|
CHECK_NOTHROW(j_key = json::from_ubjson(v_key));
|
||||||
|
REQUIRE(j_key.is_object());
|
||||||
|
CHECK(j_key.contains(std::string("\xc0\xae")));
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(j_key)) == j_key);
|
||||||
|
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(json("\xFF"))) == json("\xFF"));
|
||||||
|
// a truncated multi-byte sequence
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(json("\xC3"))) == json("\xC3"));
|
||||||
|
// an encoded surrogate half (U+D800)
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(json("\xED\xA0\x80"))) == json("\xED\xA0\x80"));
|
||||||
|
// an overlong encoding of '.'
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(json("\xC0\xAF"))) == json("\xC0\xAF"));
|
||||||
|
|
||||||
|
// an object key with ill-formed UTF-8 is kept the same way
|
||||||
|
CHECK(json::from_ubjson(json::to_ubjson(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Array Type")
|
SECTION("Array Type")
|
||||||
|
|||||||
Reference in New Issue
Block a user