mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 04:00:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87a315b803 | ||
|
|
72a292627e | ||
|
|
e8c8c940e0 | ||
|
|
23febcee51 | ||
|
|
58250e9b3f | ||
|
|
83b3bc6831 | ||
|
|
a0fe86d087 | ||
|
|
668fddc766 | ||
|
|
dec05e7f7a |
@@ -55,10 +55,6 @@ This implementation does exactly follow this approach, as it uses double precisi
|
|||||||
smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally
|
smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally
|
||||||
and be serialized to `null`.
|
and be serialized to `null`.
|
||||||
|
|
||||||
During deserialization (from JSON text or any of the binary formats), a finite number that does not fit into
|
|
||||||
`number_float_t` is rejected with [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406), for
|
|
||||||
example a double-precision number in a binary format when `number_float_t` is `#!cpp float`.
|
|
||||||
|
|
||||||
#### Storage
|
#### Storage
|
||||||
|
|
||||||
Floating-point number values are stored directly inside a `basic_json` type.
|
Floating-point number values are stored directly inside a `basic_json` type.
|
||||||
|
|||||||
@@ -47,9 +47,8 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va
|
|||||||
|
|
||||||
When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and
|
When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and
|
||||||
the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of
|
the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of
|
||||||
range will yield over/underflow when used in a constructor. During deserialization (from JSON text or any of the binary
|
range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers
|
||||||
formats), too large or small integer numbers will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md)
|
will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md).
|
||||||
or [`number_float_t`](number_float_t.md).
|
|
||||||
|
|
||||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||||
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
|
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
|
||||||
|
|||||||
@@ -48,9 +48,8 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default
|
|||||||
|
|
||||||
When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and
|
When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and
|
||||||
the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow
|
the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow
|
||||||
when used in a constructor. During deserialization (from JSON text or any of the binary formats), too large or small
|
when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored
|
||||||
integer numbers will automatically be stored as [`number_integer_t`](number_integer_t.md) or
|
as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md).
|
||||||
[`number_float_t`](number_float_t.md).
|
|
||||||
|
|
||||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||||
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
|
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
|
||||||
|
|||||||
@@ -168,9 +168,9 @@ The library maps CBOR types to JSON value types as follows:
|
|||||||
!!! warning "Negative integer overflow"
|
!!! warning "Negative integer overflow"
|
||||||
|
|
||||||
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
|
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
|
||||||
result to fit into `number_integer_t` (`std::int64_t` by default), the result is stored as `number_float_t`, like
|
result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
|
||||||
a too small integer in JSON text. For example, `-18446744073709551616` (`0x3B` followed by eight `0xFF` bytes) is
|
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
|
||||||
stored as `-1.8446744073709552e+19`.
|
silently.
|
||||||
|
|
||||||
!!! warning "Object keys"
|
!!! warning "Object keys"
|
||||||
|
|
||||||
|
|||||||
@@ -331,6 +331,9 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
|||||||
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
|
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
|
[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow
|
||||||
|
```
|
||||||
|
```
|
||||||
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -851,18 +854,13 @@ The JSON Patch operations 'remove' and 'add' cannot be applied to the root eleme
|
|||||||
|
|
||||||
### json.exception.out_of_range.406
|
### json.exception.out_of_range.406
|
||||||
|
|
||||||
A parsed number could not be stored without changing it to NaN or INF. For the binary formats, this happens when a
|
A parsed number could not be stored as without changing it to NaN or INF.
|
||||||
finite floating-point number does not fit into [`number_float_t`](../api/basic_json/number_float_t.md), for example a
|
|
||||||
double-precision number when `number_float_t` is `#!cpp float`.
|
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example message"
|
||||||
|
|
||||||
```
|
```
|
||||||
number overflow parsing '10E1000'
|
number overflow parsing '10E1000'
|
||||||
```
|
```
|
||||||
```
|
|
||||||
[json.exception.out_of_range.406] syntax error while parsing CBOR value: number overflow
|
|
||||||
```
|
|
||||||
|
|
||||||
### json.exception.out_of_range.407
|
### json.exception.out_of_range.407
|
||||||
|
|
||||||
|
|||||||
@@ -559,7 +559,7 @@ class binary_reader
|
|||||||
case 0x01: // double
|
case 0x01: // double
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number<double, true>(input_format_t::bson, number) && emit_float(input_format_t::bson, number);
|
return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x02: // string
|
case 0x02: // string
|
||||||
@@ -600,19 +600,19 @@ class binary_reader
|
|||||||
case 0x10: // int32
|
case 0x10: // int32
|
||||||
{
|
{
|
||||||
std::int32_t value{};
|
std::int32_t value{};
|
||||||
return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
|
return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x12: // int64
|
case 0x12: // int64
|
||||||
{
|
{
|
||||||
std::int64_t value{};
|
std::int64_t value{};
|
||||||
return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
|
return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x11: // uint64
|
case 0x11: // uint64
|
||||||
{
|
{
|
||||||
std::uint64_t value{};
|
std::uint64_t value{};
|
||||||
return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(input_format_t::bson, value);
|
return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
default: // anything else is not supported (yet)
|
default: // anything else is not supported (yet)
|
||||||
@@ -638,19 +638,14 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const auto max_val = static_cast<NumberType>((std::numeric_limits<number_integer_t>::max)());
|
||||||
// the value is -1 - number, which fits into number_integer_t
|
if (number > max_val)
|
||||||
// whenever number does
|
|
||||||
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
|
|
||||||
{
|
{
|
||||||
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
|
return sax->parse_error(chars_read, get_token_string(),
|
||||||
|
parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
|
||||||
}
|
}
|
||||||
|
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
|
||||||
// like the lexer does for JSON text, store a value too small for
|
|
||||||
// number_integer_t as number_float_t; compute it as long double so
|
|
||||||
// that emit_float sees a finite value and can detect an overflow of
|
|
||||||
// number_float_t
|
|
||||||
return emit_float(input_format_t::cbor, static_cast<long double>(-1) - static_cast<long double>(number));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -707,25 +702,25 @@ class binary_reader
|
|||||||
case 0x18: // Unsigned integer (one-byte uint8_t follows)
|
case 0x18: // Unsigned integer (one-byte uint8_t follows)
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x19: // Unsigned integer (two-byte uint16_t follows)
|
case 0x19: // Unsigned integer (two-byte uint16_t follows)
|
||||||
{
|
{
|
||||||
std::uint16_t number{};
|
std::uint16_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x1A: // Unsigned integer (four-byte uint32_t follows)
|
case 0x1A: // Unsigned integer (four-byte uint32_t follows)
|
||||||
{
|
{
|
||||||
std::uint32_t number{};
|
std::uint32_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
|
case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
|
||||||
{
|
{
|
||||||
std::uint64_t number{};
|
std::uint64_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative integer -1-0x00..-1-0x17 (-1..-24)
|
// Negative integer -1-0x00..-1-0x17 (-1..-24)
|
||||||
@@ -1170,13 +1165,13 @@ class binary_reader
|
|||||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
default: // anything else (0xFF is handled inside the other types)
|
default: // anything else (0xFF is handled inside the other types)
|
||||||
@@ -1940,61 +1935,61 @@ class binary_reader
|
|||||||
case 0xCA: // float 32
|
case 0xCA: // float 32
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCB: // float 64
|
case 0xCB: // float 64
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCC: // uint 8
|
case 0xCC: // uint 8
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCD: // uint 16
|
case 0xCD: // uint 16
|
||||||
{
|
{
|
||||||
std::uint16_t number{};
|
std::uint16_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCE: // uint 32
|
case 0xCE: // uint 32
|
||||||
{
|
{
|
||||||
std::uint32_t number{};
|
std::uint32_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCF: // uint 64
|
case 0xCF: // uint 64
|
||||||
{
|
{
|
||||||
std::uint64_t number{};
|
std::uint64_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD0: // int 8
|
case 0xD0: // int 8
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD1: // int 16
|
case 0xD1: // int 16
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD2: // int 32
|
case 0xD2: // int 32
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD3: // int 64
|
case 0xD3: // int 64
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDC: // array 16
|
case 0xDC: // array 16
|
||||||
@@ -2927,7 +2922,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(input_format, i)))
|
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -3059,37 +3054,37 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
@@ -3099,7 +3094,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint16_t number{};
|
std::uint16_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
@@ -3109,7 +3104,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint32_t number{};
|
std::uint32_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -3119,7 +3114,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint64_t number{};
|
std::uint64_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -3177,13 +3172,13 @@ class binary_reader
|
|||||||
case 'd':
|
case 'd':
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format, number) && emit_float(input_format, number);
|
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'D':
|
case 'D':
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format, number) && emit_float(input_format, number);
|
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'H':
|
case 'H':
|
||||||
@@ -3650,13 +3645,13 @@ class binary_reader
|
|||||||
case 0x8E: // binary32
|
case 0x8E: // binary32
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
|
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x8F: // binary64
|
case 0x8F: // binary64
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
|
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xF8:
|
case 0xF8:
|
||||||
@@ -3722,9 +3717,7 @@ class binary_reader
|
|||||||
@brief pass an integer to the SAX parser
|
@brief pass an integer to the SAX parser
|
||||||
|
|
||||||
Non-negative integers are passed as unsigned, negative integers as signed
|
Non-negative integers are passed as unsigned, negative integers as signed
|
||||||
numbers, like the other binary formats do. A value that does not fit the
|
numbers, like the other binary formats do.
|
||||||
number type is passed as described for @ref emit_unsigned and
|
|
||||||
@ref emit_signed.
|
|
||||||
|
|
||||||
@param[in] number the integer
|
@param[in] number the integer
|
||||||
@return whether the SAX parser accepted the value
|
@return whether the SAX parser accepted the value
|
||||||
@@ -3733,9 +3726,9 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (number >= 0)
|
if (number >= 0)
|
||||||
{
|
{
|
||||||
return emit_unsigned(input_format_t::bon8, static_cast<std::uint64_t>(number));
|
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
|
||||||
}
|
}
|
||||||
return emit_signed(input_format_t::bon8, number);
|
return sax->number_integer(static_cast<number_integer_t>(number));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -3792,7 +3785,8 @@ class binary_reader
|
|||||||
value = (value << 8) | static_cast<std::int64_t>(current);
|
value = (value << 8) | static_cast<std::int64_t>(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
return emit_bon8_integer(negative ? -(value + offset) : value + offset);
|
return negative ? sax->number_integer(static_cast<number_integer_t>(-(value + offset)))
|
||||||
|
: sax->number_unsigned(static_cast<number_unsigned_t>(value + offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4089,88 +4083,6 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass a signed integer read from the input to the SAX parser
|
|
||||||
|
|
||||||
Like the lexer does for JSON text, a value that does not fit into
|
|
||||||
number_integer_t is passed as number_unsigned_t if it is non-negative and
|
|
||||||
fits there, and as number_float_t otherwise. With the default number
|
|
||||||
types, every integer the binary formats can encode fits, so this only
|
|
||||||
matters for narrower custom number types.
|
|
||||||
|
|
||||||
@tparam NumberType a signed integer type
|
|
||||||
@param[in] format the current format (for diagnostics)
|
|
||||||
@param[in] number the integer
|
|
||||||
@return whether the SAX parser accepted the value
|
|
||||||
|
|
||||||
@throw out_of_range.406 if @a number overflows number_float_t (see
|
|
||||||
@ref emit_float)
|
|
||||||
*/
|
|
||||||
template<typename NumberType>
|
|
||||||
bool emit_signed(const input_format_t format, const NumberType number)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
|
|
||||||
{
|
|
||||||
return sax->number_integer(static_cast<number_integer_t>(number));
|
|
||||||
}
|
|
||||||
if (value_in_range_of<number_unsigned_t>(number))
|
|
||||||
{
|
|
||||||
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
|
|
||||||
}
|
|
||||||
return emit_float(format, number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass an unsigned integer read from the input to the SAX parser
|
|
||||||
|
|
||||||
Like the lexer does for JSON text, a value that does not fit into
|
|
||||||
number_unsigned_t is passed as number_float_t.
|
|
||||||
|
|
||||||
@tparam NumberType an unsigned integer type
|
|
||||||
@param[in] format the current format (for diagnostics)
|
|
||||||
@param[in] number the integer
|
|
||||||
@return whether the SAX parser accepted the value
|
|
||||||
|
|
||||||
@throw out_of_range.406 if @a number overflows number_float_t (see
|
|
||||||
@ref emit_float)
|
|
||||||
*/
|
|
||||||
template<typename NumberType>
|
|
||||||
bool emit_unsigned(const input_format_t format, const NumberType number)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number)))
|
|
||||||
{
|
|
||||||
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
|
|
||||||
}
|
|
||||||
return emit_float(format, number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass a floating-point number read from the input to the SAX parser
|
|
||||||
|
|
||||||
Like the lexer does for JSON text, a finite value that overflows
|
|
||||||
number_float_t is rejected instead of silently becoming infinity. Infinity
|
|
||||||
and NaN in the input are passed on unchanged. Integers only overflow if
|
|
||||||
number_float_t cannot represent 2^64, e.g., a half-precision type.
|
|
||||||
|
|
||||||
@tparam NumberType a floating-point or integer type
|
|
||||||
@param[in] format the current format (for diagnostics)
|
|
||||||
@param[in] number the number
|
|
||||||
@return whether the SAX parser accepted the value
|
|
||||||
|
|
||||||
@throw out_of_range.406 if a finite @a number overflows number_float_t
|
|
||||||
*/
|
|
||||||
template<typename NumberType>
|
|
||||||
bool emit_float(const input_format_t format, const NumberType number)
|
|
||||||
{
|
|
||||||
const auto result = static_cast<number_float_t>(number);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(),
|
|
||||||
out_of_range::create(406, exception_message(format, "number overflow", "value"), nullptr));
|
|
||||||
}
|
|
||||||
return sax->number_float(result, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief create a string by reading characters from the input
|
@brief create a string by reading characters from the input
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -2278,20 +2279,10 @@ class binary_writer
|
|||||||
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
||||||
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", hex_byte(data[valid])), &context));
|
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return a byte as two uppercase hexadecimal digits
|
|
||||||
static std::string hex_byte(const std::uint8_t byte)
|
|
||||||
{
|
|
||||||
std::string result = "00";
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
|
||||||
result[0] = nibble_to_hex[byte / 16];
|
|
||||||
result[1] = nibble_to_hex[byte % 16];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write an integer in the shortest encoding
|
@brief write an integer in the shortest encoding
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // copy
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <iterator> // back_inserter
|
|
||||||
#include <memory> // shared_ptr, make_shared
|
#include <memory> // shared_ptr, make_shared
|
||||||
#include <string> // basic_string
|
#include <string> // basic_string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
@@ -31,6 +29,10 @@ namespace detail
|
|||||||
template<typename CharType> struct output_adapter_protocol
|
template<typename CharType> struct output_adapter_protocol
|
||||||
{
|
{
|
||||||
virtual void write_character(CharType c) = 0;
|
virtual void write_character(CharType c) = 0;
|
||||||
|
/// @param[in] s pointer to the characters to write; binary_writer legitimately
|
||||||
|
/// passes a null pointer together with length 0 for an empty
|
||||||
|
/// string or binary value, so implementations must tolerate that
|
||||||
|
/// @param[in] length number of characters at @a s
|
||||||
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
||||||
virtual ~output_adapter_protocol() = default;
|
virtual ~output_adapter_protocol() = default;
|
||||||
|
|
||||||
@@ -97,7 +99,6 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
|
|||||||
sink.write_character(c);
|
sink.write_character(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
sink.write_characters(s, length);
|
sink.write_characters(s, length);
|
||||||
@@ -122,7 +123,6 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
|
|||||||
stream.put(c);
|
stream.put(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
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));
|
||||||
@@ -147,7 +147,6 @@ class output_string_adapter : public output_adapter_protocol<CharType>
|
|||||||
str.push_back(c);
|
str.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
str.append(s, length);
|
str.append(s, length);
|
||||||
|
|||||||
@@ -3,24 +3,23 @@
|
|||||||
// | | |__ | | | | | | version 3.12.0
|
// | | |__ | | | | | | version 3.12.0
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
//
|
//
|
||||||
// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // reverse, remove, fill, find, none_of, min
|
#include <algorithm> // remove, fill, find, none_of, min
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <clocale> // localeconv, lconv
|
#include <clocale> // localeconv, lconv
|
||||||
#include <cmath> // labs, isfinite, isnan, signbit
|
#include <cmath> // isfinite
|
||||||
#include <cstddef> // size_t, ptrdiff_t
|
#include <cstddef> // size_t, ptrdiff_t
|
||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
#include <cstring> // memcpy, memset
|
#include <cstring> // memcpy, memset
|
||||||
|
#include <iterator> // next
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <string> // string, char_traits
|
#include <string> // string, char_traits
|
||||||
#include <type_traits> // is_same
|
#include <type_traits> // is_same
|
||||||
#include <utility> // move
|
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/conversions/to_chars.hpp>
|
#include <nlohmann/detail/conversions/to_chars.hpp>
|
||||||
@@ -28,7 +27,6 @@
|
|||||||
#include <nlohmann/detail/input/string_scan.hpp>
|
#include <nlohmann/detail/input/string_scan.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||||
#include <nlohmann/detail/output/binary_writer.hpp>
|
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
@@ -83,7 +81,6 @@ class serializer
|
|||||||
const std::size_t indent_step_ = 0,
|
const std::size_t indent_step_ = 0,
|
||||||
error_handler_t error_handler_ = error_handler_t::strict)
|
error_handler_t error_handler_ = error_handler_t::strict)
|
||||||
: o(&s)
|
: o(&s)
|
||||||
, locale(std::localeconv())
|
|
||||||
, indent_char(ichar)
|
, indent_char(ichar)
|
||||||
, pretty_print(pretty_print_)
|
, pretty_print(pretty_print_)
|
||||||
, ensure_ascii(ensure_ascii_)
|
, ensure_ascii(ensure_ascii_)
|
||||||
@@ -106,9 +103,10 @@ class serializer
|
|||||||
additional parameter. Arrays and objects are serialized without recursion,
|
additional parameter. Arrays and objects are serialized without recursion,
|
||||||
however deeply they are nested.
|
however deeply they are nested.
|
||||||
|
|
||||||
- strings and object keys are escaped using `escape_string()`
|
- strings and object keys are escaped using @ref dump_escaped
|
||||||
- integer numbers are converted implicitly via `operator<<`
|
- integer numbers are converted using a digit-pair lookup table (@ref dump_integer)
|
||||||
- floating-point numbers are converted to a string using `"%g"` format
|
- floating-point numbers are converted to a string using @ref dump_float, which
|
||||||
|
uses `to_chars` for IEEE-754 types and `snprintf` otherwise
|
||||||
- binary values are serialized as objects containing the subtype and the
|
- binary values are serialized as objects containing the subtype and the
|
||||||
byte array
|
byte array
|
||||||
|
|
||||||
@@ -283,127 +281,16 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
|
||||||
put_char('"');
|
|
||||||
dump_escaped(*val.m_data.m_value.string);
|
|
||||||
put_char('"');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
{
|
|
||||||
if (pretty_print)
|
|
||||||
{
|
|
||||||
put_literal("{\n");
|
|
||||||
|
|
||||||
// variable to hold indentation for recursive calls
|
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
|
||||||
|
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"bytes\": [");
|
|
||||||
|
|
||||||
if (!val.m_data.m_value.binary->empty())
|
|
||||||
{
|
|
||||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
|
||||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_byte(*i);
|
|
||||||
put_literal(", ");
|
|
||||||
}
|
|
||||||
dump_byte(val.m_data.m_value.binary->back());
|
|
||||||
}
|
|
||||||
|
|
||||||
put_literal("],\n");
|
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"subtype\": ");
|
|
||||||
if (val.m_data.m_value.binary->has_subtype())
|
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.binary->subtype());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("null");
|
|
||||||
}
|
|
||||||
put_char('\n');
|
|
||||||
put_indent(current_indent);
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("{\"bytes\":[");
|
|
||||||
|
|
||||||
if (!val.m_data.m_value.binary->empty())
|
|
||||||
{
|
|
||||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
|
||||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_byte(*i);
|
|
||||||
put_char(',');
|
|
||||||
}
|
|
||||||
dump_byte(val.m_data.m_value.binary->back());
|
|
||||||
}
|
|
||||||
|
|
||||||
put_literal("],\"subtype\":");
|
|
||||||
if (val.m_data.m_value.binary->has_subtype())
|
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.binary->subtype());
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("null}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
{
|
|
||||||
if (val.m_data.m_value.boolean)
|
|
||||||
{
|
|
||||||
put_literal("true");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("false");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_integer:
|
case value_t::number_integer:
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.number_integer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.number_unsigned);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
{
|
|
||||||
dump_float(val.m_data.m_value.number_float);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
{
|
|
||||||
put_literal("<discarded>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
{
|
default:
|
||||||
put_literal("null");
|
dump_scalar(val, current_indent);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,9 +447,9 @@ class serializer
|
|||||||
@brief serialize the value @a val, but not the elements of a container
|
@brief serialize the value @a val, but not the elements of a container
|
||||||
|
|
||||||
An object or array with elements is opened and pushed onto @a stack for
|
An object or array with elements is opened and pushed onto @a stack for
|
||||||
@ref dump_internal to walk; everything else - including a binary value,
|
@ref dump_iteratively to walk; everything else - including a binary value,
|
||||||
which looks like an object but has no elements to descend into - is written
|
which looks like an object but has no elements to descend into - is written
|
||||||
out here in full.
|
out in full by @ref dump_scalar.
|
||||||
*/
|
*/
|
||||||
void dump_value(const BasicJsonType& val,
|
void dump_value(const BasicJsonType& val,
|
||||||
const std::size_t current_indent,
|
const std::size_t current_indent,
|
||||||
@@ -620,6 +507,35 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::discarded:
|
||||||
|
case value_t::null:
|
||||||
|
default:
|
||||||
|
dump_scalar(val, current_indent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief serialize the value @a val, which is neither an object nor an array
|
||||||
|
|
||||||
|
Shared by @ref dump_internal and @ref dump_value, so that a value is written
|
||||||
|
the same way however deeply it is nested. A binary value is written out here
|
||||||
|
in full: it looks like an object, but has no elements to descend into.
|
||||||
|
|
||||||
|
@param[in] val value to serialize; not an object or array
|
||||||
|
@param[in] current_indent the indentation of @a val, used for a
|
||||||
|
pretty-printed binary value
|
||||||
|
*/
|
||||||
|
void dump_scalar(const BasicJsonType& val, const std::size_t current_indent)
|
||||||
|
{
|
||||||
|
switch (val.m_data.m_type)
|
||||||
|
{
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
put_char('"');
|
put_char('"');
|
||||||
@@ -740,6 +656,8 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case value_t::object: // LCOV_EXCL_LINE
|
||||||
|
case value_t::array: // LCOV_EXCL_LINE
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@@ -768,7 +686,7 @@ class serializer
|
|||||||
Escape a string by replacing certain special characters by a sequence of an
|
Escape a string by replacing certain special characters by a sequence of an
|
||||||
escape character (backslash) and another character and other control
|
escape character (backslash) and another character and other control
|
||||||
characters by a sequence of "\u" followed by a four-digit hex
|
characters by a sequence of "\u" followed by a four-digit hex
|
||||||
representation. The escaped string is written to output stream @a o.
|
representation. The escaped string is appended to @ref write_buffer.
|
||||||
|
|
||||||
@param[in] s the string to escape
|
@param[in] s the string to escape
|
||||||
|
|
||||||
@@ -962,7 +880,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
|
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
@@ -995,9 +913,9 @@ class serializer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
|
string_buffer[bytes++] = '\xEF';
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
|
string_buffer[bytes++] = '\xBF';
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
|
string_buffer[bytes++] = '\xBD';
|
||||||
}
|
}
|
||||||
|
|
||||||
// write buffer and reset index; there must be 13 bytes
|
// write buffer and reset index; there must be 13 bytes
|
||||||
@@ -1054,7 +972,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s[s.size() - 1] | 0))), nullptr));
|
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
@@ -1275,20 +1193,6 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief convert a byte to a uppercase hex representation
|
|
||||||
* @param[in] byte byte to represent
|
|
||||||
* @return representation ("00".."FF")
|
|
||||||
*/
|
|
||||||
static std::string hex_bytes(std::uint8_t byte)
|
|
||||||
{
|
|
||||||
std::string result = "FF";
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
|
||||||
result[0] = nibble_to_hex[byte / 16];
|
|
||||||
result[1] = nibble_to_hex[byte % 16];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
||||||
*
|
*
|
||||||
@@ -1402,7 +1306,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief dump an integer
|
@brief dump an integer
|
||||||
|
|
||||||
Dump a given integer to output stream @a o. Works internally with
|
Dump a given integer, appending it to @ref write_buffer. Works internally with
|
||||||
@a number_buffer.
|
@a number_buffer.
|
||||||
|
|
||||||
@param[in] x integer number (signed or unsigned) to dump
|
@param[in] x integer number (signed or unsigned) to dump
|
||||||
@@ -1439,7 +1343,7 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use a pointer to fill the buffer
|
// use a pointer to fill the buffer
|
||||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto)
|
||||||
|
|
||||||
number_unsigned_t abs_value;
|
number_unsigned_t abs_value;
|
||||||
|
|
||||||
@@ -1493,7 +1397,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief dump a floating-point number
|
@brief dump a floating-point number
|
||||||
|
|
||||||
Dump a given floating-point number to output stream @a o. Works internally
|
Dump a given floating-point number, appending it to @ref write_buffer. Works internally
|
||||||
with @a number_buffer.
|
with @a number_buffer.
|
||||||
|
|
||||||
@param[in] x floating-point number to dump
|
@param[in] x floating-point number to dump
|
||||||
@@ -1554,21 +1458,28 @@ class serializer
|
|||||||
// check if the buffer was large enough
|
// check if the buffer was large enough
|
||||||
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
|
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
|
||||||
|
|
||||||
|
// look up the locale's thousands separator and decimal point now,
|
||||||
|
// matching what snprintf_float() just used (see lexer::get_decimal_point())
|
||||||
|
const auto* loc = std::localeconv();
|
||||||
|
JSON_ASSERT(loc != nullptr);
|
||||||
|
const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
|
||||||
|
const char decimal_point = (loc->decimal_point == nullptr) ? '\0' : *loc->decimal_point;
|
||||||
|
|
||||||
// erase thousands separators
|
// erase thousands separators
|
||||||
if (locale.thousands_sep != '\0')
|
if (thousands_sep != '\0')
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||||
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, locale.thousands_sep);
|
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
|
||||||
std::fill(end, number_buffer.end(), '\0');
|
std::fill(end, number_buffer.end(), '\0');
|
||||||
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
||||||
len = (end - number_buffer.begin());
|
len = (end - number_buffer.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert decimal point to '.'
|
// convert decimal point to '.'
|
||||||
if (locale.decimal_point != '\0' && locale.decimal_point != '.')
|
if (decimal_point != '\0' && decimal_point != '.')
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||||
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), locale.decimal_point);
|
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
|
||||||
if (dec_pos != number_buffer.end())
|
if (dec_pos != number_buffer.end())
|
||||||
{
|
{
|
||||||
*dec_pos = '.';
|
*dec_pos = '.';
|
||||||
@@ -1613,34 +1524,17 @@ class serializer
|
|||||||
*/
|
*/
|
||||||
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
||||||
{
|
{
|
||||||
JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
|
JSON_ASSERT(x < 0);
|
||||||
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// the locale's thousand separator and decimal point characters
|
|
||||||
struct locale_chars
|
|
||||||
{
|
|
||||||
explicit locale_chars(const std::lconv* loc) noexcept
|
|
||||||
: thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
|
|
||||||
, decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
const char thousands_sep;
|
|
||||||
const char decimal_point;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
||||||
output_adapter_protocol<char>* o = nullptr;
|
output_adapter_protocol<char>* o = nullptr;
|
||||||
|
|
||||||
/// a (hopefully) large enough character buffer
|
/// a (hopefully) large enough character buffer
|
||||||
std::array<char, 64> number_buffer{{}};
|
std::array<char, 64> number_buffer{{}};
|
||||||
|
|
||||||
/// computed once from std::localeconv() at construction; @ref
|
|
||||||
/// locale_chars keeps std::localeconv()'s pointer from having to be held
|
|
||||||
/// past the constructor, while still letting these stay const
|
|
||||||
const locale_chars locale;
|
|
||||||
|
|
||||||
/// string buffer
|
/// string buffer
|
||||||
std::array<char, 512> string_buffer{{}};
|
std::array<char, 512> string_buffer{{}};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// | | |__ | | | | | | version 3.12.0
|
// | | |__ | | | | | | version 3.12.0
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
//
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
@@ -36,6 +37,16 @@ StringType to_string(std::size_t value)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return a byte as two uppercase hexadecimal digits
|
||||||
|
inline std::string hex_byte(const std::uint8_t byte)
|
||||||
|
{
|
||||||
|
std::string result = "00";
|
||||||
|
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
||||||
|
result[0] = nibble_to_hex[byte / 16];
|
||||||
|
result[1] = nibble_to_hex[byte % 16];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
// UTF-8 decoding //
|
// UTF-8 decoding //
|
||||||
///////////////////
|
///////////////////
|
||||||
|
|||||||
+126
-319
@@ -6199,6 +6199,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
// | | |__ | | | | | | version 3.12.0
|
// | | |__ | | | | | | version 3.12.0
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
//
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
@@ -6234,6 +6235,16 @@ StringType to_string(std::size_t value)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return a byte as two uppercase hexadecimal digits
|
||||||
|
inline std::string hex_byte(const std::uint8_t byte)
|
||||||
|
{
|
||||||
|
std::string result = "00";
|
||||||
|
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
||||||
|
result[0] = nibble_to_hex[byte / 16];
|
||||||
|
result[1] = nibble_to_hex[byte % 16];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
// UTF-8 decoding //
|
// UTF-8 decoding //
|
||||||
///////////////////
|
///////////////////
|
||||||
@@ -13326,7 +13337,7 @@ class binary_reader
|
|||||||
case 0x01: // double
|
case 0x01: // double
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number<double, true>(input_format_t::bson, number) && emit_float(input_format_t::bson, number);
|
return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x02: // string
|
case 0x02: // string
|
||||||
@@ -13367,19 +13378,19 @@ class binary_reader
|
|||||||
case 0x10: // int32
|
case 0x10: // int32
|
||||||
{
|
{
|
||||||
std::int32_t value{};
|
std::int32_t value{};
|
||||||
return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
|
return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x12: // int64
|
case 0x12: // int64
|
||||||
{
|
{
|
||||||
std::int64_t value{};
|
std::int64_t value{};
|
||||||
return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
|
return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x11: // uint64
|
case 0x11: // uint64
|
||||||
{
|
{
|
||||||
std::uint64_t value{};
|
std::uint64_t value{};
|
||||||
return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(input_format_t::bson, value);
|
return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
default: // anything else is not supported (yet)
|
default: // anything else is not supported (yet)
|
||||||
@@ -13405,19 +13416,14 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const auto max_val = static_cast<NumberType>((std::numeric_limits<number_integer_t>::max)());
|
||||||
// the value is -1 - number, which fits into number_integer_t
|
if (number > max_val)
|
||||||
// whenever number does
|
|
||||||
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
|
|
||||||
{
|
{
|
||||||
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
|
return sax->parse_error(chars_read, get_token_string(),
|
||||||
|
parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
|
||||||
}
|
}
|
||||||
|
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
|
||||||
// like the lexer does for JSON text, store a value too small for
|
|
||||||
// number_integer_t as number_float_t; compute it as long double so
|
|
||||||
// that emit_float sees a finite value and can detect an overflow of
|
|
||||||
// number_float_t
|
|
||||||
return emit_float(input_format_t::cbor, static_cast<long double>(-1) - static_cast<long double>(number));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -13474,25 +13480,25 @@ class binary_reader
|
|||||||
case 0x18: // Unsigned integer (one-byte uint8_t follows)
|
case 0x18: // Unsigned integer (one-byte uint8_t follows)
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x19: // Unsigned integer (two-byte uint16_t follows)
|
case 0x19: // Unsigned integer (two-byte uint16_t follows)
|
||||||
{
|
{
|
||||||
std::uint16_t number{};
|
std::uint16_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x1A: // Unsigned integer (four-byte uint32_t follows)
|
case 0x1A: // Unsigned integer (four-byte uint32_t follows)
|
||||||
{
|
{
|
||||||
std::uint32_t number{};
|
std::uint32_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
|
case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
|
||||||
{
|
{
|
||||||
std::uint64_t number{};
|
std::uint64_t number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative integer -1-0x00..-1-0x17 (-1..-24)
|
// Negative integer -1-0x00..-1-0x17 (-1..-24)
|
||||||
@@ -13937,13 +13943,13 @@ class binary_reader
|
|||||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
|
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
default: // anything else (0xFF is handled inside the other types)
|
default: // anything else (0xFF is handled inside the other types)
|
||||||
@@ -14707,61 +14713,61 @@ class binary_reader
|
|||||||
case 0xCA: // float 32
|
case 0xCA: // float 32
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCB: // float 64
|
case 0xCB: // float 64
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCC: // uint 8
|
case 0xCC: // uint 8
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCD: // uint 16
|
case 0xCD: // uint 16
|
||||||
{
|
{
|
||||||
std::uint16_t number{};
|
std::uint16_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCE: // uint 32
|
case 0xCE: // uint 32
|
||||||
{
|
{
|
||||||
std::uint32_t number{};
|
std::uint32_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xCF: // uint 64
|
case 0xCF: // uint 64
|
||||||
{
|
{
|
||||||
std::uint64_t number{};
|
std::uint64_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD0: // int 8
|
case 0xD0: // int 8
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD1: // int 16
|
case 0xD1: // int 16
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD2: // int 32
|
case 0xD2: // int 32
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD3: // int 64
|
case 0xD3: // int 64
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
|
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDC: // array 16
|
case 0xDC: // array 16
|
||||||
@@ -15694,7 +15700,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(input_format, i)))
|
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -15826,37 +15832,37 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
return get_number(input_format, number) && emit_signed(input_format, number);
|
return get_number(input_format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
@@ -15866,7 +15872,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint16_t number{};
|
std::uint16_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
@@ -15876,7 +15882,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint32_t number{};
|
std::uint32_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -15886,7 +15892,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint64_t number{};
|
std::uint64_t number{};
|
||||||
return get_number(input_format, number) && emit_unsigned(input_format, number);
|
return get_number(input_format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -15944,13 +15950,13 @@ class binary_reader
|
|||||||
case 'd':
|
case 'd':
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format, number) && emit_float(input_format, number);
|
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'D':
|
case 'D':
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format, number) && emit_float(input_format, number);
|
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'H':
|
case 'H':
|
||||||
@@ -16417,13 +16423,13 @@ class binary_reader
|
|||||||
case 0x8E: // binary32
|
case 0x8E: // binary32
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
|
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x8F: // binary64
|
case 0x8F: // binary64
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
|
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xF8:
|
case 0xF8:
|
||||||
@@ -16489,9 +16495,7 @@ class binary_reader
|
|||||||
@brief pass an integer to the SAX parser
|
@brief pass an integer to the SAX parser
|
||||||
|
|
||||||
Non-negative integers are passed as unsigned, negative integers as signed
|
Non-negative integers are passed as unsigned, negative integers as signed
|
||||||
numbers, like the other binary formats do. A value that does not fit the
|
numbers, like the other binary formats do.
|
||||||
number type is passed as described for @ref emit_unsigned and
|
|
||||||
@ref emit_signed.
|
|
||||||
|
|
||||||
@param[in] number the integer
|
@param[in] number the integer
|
||||||
@return whether the SAX parser accepted the value
|
@return whether the SAX parser accepted the value
|
||||||
@@ -16500,9 +16504,9 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (number >= 0)
|
if (number >= 0)
|
||||||
{
|
{
|
||||||
return emit_unsigned(input_format_t::bon8, static_cast<std::uint64_t>(number));
|
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
|
||||||
}
|
}
|
||||||
return emit_signed(input_format_t::bon8, number);
|
return sax->number_integer(static_cast<number_integer_t>(number));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -16559,7 +16563,8 @@ class binary_reader
|
|||||||
value = (value << 8) | static_cast<std::int64_t>(current);
|
value = (value << 8) | static_cast<std::int64_t>(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
return emit_bon8_integer(negative ? -(value + offset) : value + offset);
|
return negative ? sax->number_integer(static_cast<number_integer_t>(-(value + offset)))
|
||||||
|
: sax->number_unsigned(static_cast<number_unsigned_t>(value + offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -16856,88 +16861,6 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass a signed integer read from the input to the SAX parser
|
|
||||||
|
|
||||||
Like the lexer does for JSON text, a value that does not fit into
|
|
||||||
number_integer_t is passed as number_unsigned_t if it is non-negative and
|
|
||||||
fits there, and as number_float_t otherwise. With the default number
|
|
||||||
types, every integer the binary formats can encode fits, so this only
|
|
||||||
matters for narrower custom number types.
|
|
||||||
|
|
||||||
@tparam NumberType a signed integer type
|
|
||||||
@param[in] format the current format (for diagnostics)
|
|
||||||
@param[in] number the integer
|
|
||||||
@return whether the SAX parser accepted the value
|
|
||||||
|
|
||||||
@throw out_of_range.406 if @a number overflows number_float_t (see
|
|
||||||
@ref emit_float)
|
|
||||||
*/
|
|
||||||
template<typename NumberType>
|
|
||||||
bool emit_signed(const input_format_t format, const NumberType number)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
|
|
||||||
{
|
|
||||||
return sax->number_integer(static_cast<number_integer_t>(number));
|
|
||||||
}
|
|
||||||
if (value_in_range_of<number_unsigned_t>(number))
|
|
||||||
{
|
|
||||||
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
|
|
||||||
}
|
|
||||||
return emit_float(format, number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass an unsigned integer read from the input to the SAX parser
|
|
||||||
|
|
||||||
Like the lexer does for JSON text, a value that does not fit into
|
|
||||||
number_unsigned_t is passed as number_float_t.
|
|
||||||
|
|
||||||
@tparam NumberType an unsigned integer type
|
|
||||||
@param[in] format the current format (for diagnostics)
|
|
||||||
@param[in] number the integer
|
|
||||||
@return whether the SAX parser accepted the value
|
|
||||||
|
|
||||||
@throw out_of_range.406 if @a number overflows number_float_t (see
|
|
||||||
@ref emit_float)
|
|
||||||
*/
|
|
||||||
template<typename NumberType>
|
|
||||||
bool emit_unsigned(const input_format_t format, const NumberType number)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number)))
|
|
||||||
{
|
|
||||||
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
|
|
||||||
}
|
|
||||||
return emit_float(format, number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass a floating-point number read from the input to the SAX parser
|
|
||||||
|
|
||||||
Like the lexer does for JSON text, a finite value that overflows
|
|
||||||
number_float_t is rejected instead of silently becoming infinity. Infinity
|
|
||||||
and NaN in the input are passed on unchanged. Integers only overflow if
|
|
||||||
number_float_t cannot represent 2^64, e.g., a half-precision type.
|
|
||||||
|
|
||||||
@tparam NumberType a floating-point or integer type
|
|
||||||
@param[in] format the current format (for diagnostics)
|
|
||||||
@param[in] number the number
|
|
||||||
@return whether the SAX parser accepted the value
|
|
||||||
|
|
||||||
@throw out_of_range.406 if a finite @a number overflows number_float_t
|
|
||||||
*/
|
|
||||||
template<typename NumberType>
|
|
||||||
bool emit_float(const input_format_t format, const NumberType number)
|
|
||||||
{
|
|
||||||
const auto result = static_cast<number_float_t>(number);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(),
|
|
||||||
out_of_range::create(406, exception_message(format, "number overflow", "value"), nullptr));
|
|
||||||
}
|
|
||||||
return sax->number_float(result, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief create a string by reading characters from the input
|
@brief create a string by reading characters from the input
|
||||||
|
|
||||||
@@ -20234,9 +20157,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <algorithm> // copy
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <iterator> // back_inserter
|
|
||||||
#include <memory> // shared_ptr, make_shared
|
#include <memory> // shared_ptr, make_shared
|
||||||
#include <string> // basic_string
|
#include <string> // basic_string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
@@ -20258,6 +20179,10 @@ namespace detail
|
|||||||
template<typename CharType> struct output_adapter_protocol
|
template<typename CharType> struct output_adapter_protocol
|
||||||
{
|
{
|
||||||
virtual void write_character(CharType c) = 0;
|
virtual void write_character(CharType c) = 0;
|
||||||
|
/// @param[in] s pointer to the characters to write; binary_writer legitimately
|
||||||
|
/// passes a null pointer together with length 0 for an empty
|
||||||
|
/// string or binary value, so implementations must tolerate that
|
||||||
|
/// @param[in] length number of characters at @a s
|
||||||
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
||||||
virtual ~output_adapter_protocol() = default;
|
virtual ~output_adapter_protocol() = default;
|
||||||
|
|
||||||
@@ -20324,7 +20249,6 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
|
|||||||
sink.write_character(c);
|
sink.write_character(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
sink.write_characters(s, length);
|
sink.write_characters(s, length);
|
||||||
@@ -20349,7 +20273,6 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
|
|||||||
stream.put(c);
|
stream.put(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
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));
|
||||||
@@ -20374,7 +20297,6 @@ class output_string_adapter : public output_adapter_protocol<CharType>
|
|||||||
str.push_back(c);
|
str.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
str.append(s, length);
|
str.append(s, length);
|
||||||
@@ -20447,6 +20369,8 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
// #include <nlohmann/detail/string_concat.hpp>
|
// #include <nlohmann/detail/string_concat.hpp>
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/string_utils.hpp>
|
||||||
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -22696,20 +22620,10 @@ class binary_writer
|
|||||||
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
||||||
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", hex_byte(data[valid])), &context));
|
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", detail::hex_byte(data[valid])), &context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return a byte as two uppercase hexadecimal digits
|
|
||||||
static std::string hex_byte(const std::uint8_t byte)
|
|
||||||
{
|
|
||||||
std::string result = "00";
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
|
||||||
result[0] = nibble_to_hex[byte / 16];
|
|
||||||
result[1] = nibble_to_hex[byte % 16];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write an integer in the shortest encoding
|
@brief write an integer in the shortest encoding
|
||||||
|
|
||||||
@@ -23042,24 +22956,23 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
// | | |__ | | | | | | version 3.12.0
|
// | | |__ | | | | | | version 3.12.0
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
//
|
//
|
||||||
// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <algorithm> // reverse, remove, fill, find, none_of, min
|
#include <algorithm> // remove, fill, find, none_of, min
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <clocale> // localeconv, lconv
|
#include <clocale> // localeconv, lconv
|
||||||
#include <cmath> // labs, isfinite, isnan, signbit
|
#include <cmath> // isfinite
|
||||||
#include <cstddef> // size_t, ptrdiff_t
|
#include <cstddef> // size_t, ptrdiff_t
|
||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
#include <cstring> // memcpy, memset
|
#include <cstring> // memcpy, memset
|
||||||
|
#include <iterator> // next
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <string> // string, char_traits
|
#include <string> // string, char_traits
|
||||||
#include <type_traits> // is_same
|
#include <type_traits> // is_same
|
||||||
#include <utility> // move
|
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
// #include <nlohmann/detail/conversions/to_chars.hpp>
|
// #include <nlohmann/detail/conversions/to_chars.hpp>
|
||||||
@@ -24191,8 +24104,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
// #include <nlohmann/detail/meta/cpp_future.hpp>
|
// #include <nlohmann/detail/meta/cpp_future.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/detail/output/binary_writer.hpp>
|
|
||||||
|
|
||||||
// #include <nlohmann/detail/output/output_adapters.hpp>
|
// #include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/detail/recursion_depth_limit.hpp>
|
// #include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
@@ -24252,7 +24163,6 @@ class serializer
|
|||||||
const std::size_t indent_step_ = 0,
|
const std::size_t indent_step_ = 0,
|
||||||
error_handler_t error_handler_ = error_handler_t::strict)
|
error_handler_t error_handler_ = error_handler_t::strict)
|
||||||
: o(&s)
|
: o(&s)
|
||||||
, locale(std::localeconv())
|
|
||||||
, indent_char(ichar)
|
, indent_char(ichar)
|
||||||
, pretty_print(pretty_print_)
|
, pretty_print(pretty_print_)
|
||||||
, ensure_ascii(ensure_ascii_)
|
, ensure_ascii(ensure_ascii_)
|
||||||
@@ -24275,9 +24185,10 @@ class serializer
|
|||||||
additional parameter. Arrays and objects are serialized without recursion,
|
additional parameter. Arrays and objects are serialized without recursion,
|
||||||
however deeply they are nested.
|
however deeply they are nested.
|
||||||
|
|
||||||
- strings and object keys are escaped using `escape_string()`
|
- strings and object keys are escaped using @ref dump_escaped
|
||||||
- integer numbers are converted implicitly via `operator<<`
|
- integer numbers are converted using a digit-pair lookup table (@ref dump_integer)
|
||||||
- floating-point numbers are converted to a string using `"%g"` format
|
- floating-point numbers are converted to a string using @ref dump_float, which
|
||||||
|
uses `to_chars` for IEEE-754 types and `snprintf` otherwise
|
||||||
- binary values are serialized as objects containing the subtype and the
|
- binary values are serialized as objects containing the subtype and the
|
||||||
byte array
|
byte array
|
||||||
|
|
||||||
@@ -24452,127 +24363,16 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
|
||||||
put_char('"');
|
|
||||||
dump_escaped(*val.m_data.m_value.string);
|
|
||||||
put_char('"');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
{
|
|
||||||
if (pretty_print)
|
|
||||||
{
|
|
||||||
put_literal("{\n");
|
|
||||||
|
|
||||||
// variable to hold indentation for recursive calls
|
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
|
||||||
|
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"bytes\": [");
|
|
||||||
|
|
||||||
if (!val.m_data.m_value.binary->empty())
|
|
||||||
{
|
|
||||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
|
||||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_byte(*i);
|
|
||||||
put_literal(", ");
|
|
||||||
}
|
|
||||||
dump_byte(val.m_data.m_value.binary->back());
|
|
||||||
}
|
|
||||||
|
|
||||||
put_literal("],\n");
|
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"subtype\": ");
|
|
||||||
if (val.m_data.m_value.binary->has_subtype())
|
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.binary->subtype());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("null");
|
|
||||||
}
|
|
||||||
put_char('\n');
|
|
||||||
put_indent(current_indent);
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("{\"bytes\":[");
|
|
||||||
|
|
||||||
if (!val.m_data.m_value.binary->empty())
|
|
||||||
{
|
|
||||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
|
||||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_byte(*i);
|
|
||||||
put_char(',');
|
|
||||||
}
|
|
||||||
dump_byte(val.m_data.m_value.binary->back());
|
|
||||||
}
|
|
||||||
|
|
||||||
put_literal("],\"subtype\":");
|
|
||||||
if (val.m_data.m_value.binary->has_subtype())
|
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.binary->subtype());
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("null}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
{
|
|
||||||
if (val.m_data.m_value.boolean)
|
|
||||||
{
|
|
||||||
put_literal("true");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("false");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_integer:
|
case value_t::number_integer:
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.number_integer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.number_unsigned);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
{
|
|
||||||
dump_float(val.m_data.m_value.number_float);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
{
|
|
||||||
put_literal("<discarded>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
{
|
default:
|
||||||
put_literal("null");
|
dump_scalar(val, current_indent);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24729,9 +24529,9 @@ class serializer
|
|||||||
@brief serialize the value @a val, but not the elements of a container
|
@brief serialize the value @a val, but not the elements of a container
|
||||||
|
|
||||||
An object or array with elements is opened and pushed onto @a stack for
|
An object or array with elements is opened and pushed onto @a stack for
|
||||||
@ref dump_internal to walk; everything else - including a binary value,
|
@ref dump_iteratively to walk; everything else - including a binary value,
|
||||||
which looks like an object but has no elements to descend into - is written
|
which looks like an object but has no elements to descend into - is written
|
||||||
out here in full.
|
out in full by @ref dump_scalar.
|
||||||
*/
|
*/
|
||||||
void dump_value(const BasicJsonType& val,
|
void dump_value(const BasicJsonType& val,
|
||||||
const std::size_t current_indent,
|
const std::size_t current_indent,
|
||||||
@@ -24789,6 +24589,35 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::discarded:
|
||||||
|
case value_t::null:
|
||||||
|
default:
|
||||||
|
dump_scalar(val, current_indent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief serialize the value @a val, which is neither an object nor an array
|
||||||
|
|
||||||
|
Shared by @ref dump_internal and @ref dump_value, so that a value is written
|
||||||
|
the same way however deeply it is nested. A binary value is written out here
|
||||||
|
in full: it looks like an object, but has no elements to descend into.
|
||||||
|
|
||||||
|
@param[in] val value to serialize; not an object or array
|
||||||
|
@param[in] current_indent the indentation of @a val, used for a
|
||||||
|
pretty-printed binary value
|
||||||
|
*/
|
||||||
|
void dump_scalar(const BasicJsonType& val, const std::size_t current_indent)
|
||||||
|
{
|
||||||
|
switch (val.m_data.m_type)
|
||||||
|
{
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
put_char('"');
|
put_char('"');
|
||||||
@@ -24909,6 +24738,8 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case value_t::object: // LCOV_EXCL_LINE
|
||||||
|
case value_t::array: // LCOV_EXCL_LINE
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@@ -24937,7 +24768,7 @@ class serializer
|
|||||||
Escape a string by replacing certain special characters by a sequence of an
|
Escape a string by replacing certain special characters by a sequence of an
|
||||||
escape character (backslash) and another character and other control
|
escape character (backslash) and another character and other control
|
||||||
characters by a sequence of "\u" followed by a four-digit hex
|
characters by a sequence of "\u" followed by a four-digit hex
|
||||||
representation. The escaped string is written to output stream @a o.
|
representation. The escaped string is appended to @ref write_buffer.
|
||||||
|
|
||||||
@param[in] s the string to escape
|
@param[in] s the string to escape
|
||||||
|
|
||||||
@@ -25131,7 +24962,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
|
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
@@ -25164,9 +24995,9 @@ class serializer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
|
string_buffer[bytes++] = '\xEF';
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
|
string_buffer[bytes++] = '\xBF';
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
|
string_buffer[bytes++] = '\xBD';
|
||||||
}
|
}
|
||||||
|
|
||||||
// write buffer and reset index; there must be 13 bytes
|
// write buffer and reset index; there must be 13 bytes
|
||||||
@@ -25223,7 +25054,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s[s.size() - 1] | 0))), nullptr));
|
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
@@ -25444,20 +25275,6 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief convert a byte to a uppercase hex representation
|
|
||||||
* @param[in] byte byte to represent
|
|
||||||
* @return representation ("00".."FF")
|
|
||||||
*/
|
|
||||||
static std::string hex_bytes(std::uint8_t byte)
|
|
||||||
{
|
|
||||||
std::string result = "FF";
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
|
||||||
result[0] = nibble_to_hex[byte / 16];
|
|
||||||
result[1] = nibble_to_hex[byte % 16];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
||||||
*
|
*
|
||||||
@@ -25571,7 +25388,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief dump an integer
|
@brief dump an integer
|
||||||
|
|
||||||
Dump a given integer to output stream @a o. Works internally with
|
Dump a given integer, appending it to @ref write_buffer. Works internally with
|
||||||
@a number_buffer.
|
@a number_buffer.
|
||||||
|
|
||||||
@param[in] x integer number (signed or unsigned) to dump
|
@param[in] x integer number (signed or unsigned) to dump
|
||||||
@@ -25608,7 +25425,7 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use a pointer to fill the buffer
|
// use a pointer to fill the buffer
|
||||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto)
|
||||||
|
|
||||||
number_unsigned_t abs_value;
|
number_unsigned_t abs_value;
|
||||||
|
|
||||||
@@ -25662,7 +25479,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief dump a floating-point number
|
@brief dump a floating-point number
|
||||||
|
|
||||||
Dump a given floating-point number to output stream @a o. Works internally
|
Dump a given floating-point number, appending it to @ref write_buffer. Works internally
|
||||||
with @a number_buffer.
|
with @a number_buffer.
|
||||||
|
|
||||||
@param[in] x floating-point number to dump
|
@param[in] x floating-point number to dump
|
||||||
@@ -25723,21 +25540,28 @@ class serializer
|
|||||||
// check if the buffer was large enough
|
// check if the buffer was large enough
|
||||||
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
|
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
|
||||||
|
|
||||||
|
// look up the locale's thousands separator and decimal point now,
|
||||||
|
// matching what snprintf_float() just used (see lexer::get_decimal_point())
|
||||||
|
const auto* loc = std::localeconv();
|
||||||
|
JSON_ASSERT(loc != nullptr);
|
||||||
|
const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
|
||||||
|
const char decimal_point = (loc->decimal_point == nullptr) ? '\0' : *loc->decimal_point;
|
||||||
|
|
||||||
// erase thousands separators
|
// erase thousands separators
|
||||||
if (locale.thousands_sep != '\0')
|
if (thousands_sep != '\0')
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||||
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, locale.thousands_sep);
|
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
|
||||||
std::fill(end, number_buffer.end(), '\0');
|
std::fill(end, number_buffer.end(), '\0');
|
||||||
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
||||||
len = (end - number_buffer.begin());
|
len = (end - number_buffer.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert decimal point to '.'
|
// convert decimal point to '.'
|
||||||
if (locale.decimal_point != '\0' && locale.decimal_point != '.')
|
if (decimal_point != '\0' && decimal_point != '.')
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||||
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), locale.decimal_point);
|
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
|
||||||
if (dec_pos != number_buffer.end())
|
if (dec_pos != number_buffer.end())
|
||||||
{
|
{
|
||||||
*dec_pos = '.';
|
*dec_pos = '.';
|
||||||
@@ -25782,34 +25606,17 @@ class serializer
|
|||||||
*/
|
*/
|
||||||
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
||||||
{
|
{
|
||||||
JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
|
JSON_ASSERT(x < 0);
|
||||||
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// the locale's thousand separator and decimal point characters
|
|
||||||
struct locale_chars
|
|
||||||
{
|
|
||||||
explicit locale_chars(const std::lconv* loc) noexcept
|
|
||||||
: thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
|
|
||||||
, decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
const char thousands_sep;
|
|
||||||
const char decimal_point;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
||||||
output_adapter_protocol<char>* o = nullptr;
|
output_adapter_protocol<char>* o = nullptr;
|
||||||
|
|
||||||
/// a (hopefully) large enough character buffer
|
/// a (hopefully) large enough character buffer
|
||||||
std::array<char, 64> number_buffer{{}};
|
std::array<char, 64> number_buffer{{}};
|
||||||
|
|
||||||
/// computed once from std::localeconv() at construction; @ref
|
|
||||||
/// locale_chars keeps std::localeconv()'s pointer from having to be held
|
|
||||||
/// past the constructor, while still letting these stay const
|
|
||||||
const locale_chars locale;
|
|
||||||
|
|
||||||
/// string buffer
|
/// string buffer
|
||||||
std::array<char, 512> string_buffer{{}};
|
std::array<char, 512> string_buffer{{}};
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,7 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <limits>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "make_test_data_available.hpp"
|
#include "make_test_data_available.hpp"
|
||||||
|
|
||||||
TEST_CASE("Binary Formats" * doctest::skip())
|
TEST_CASE("Binary Formats" * doctest::skip())
|
||||||
@@ -229,139 +224,3 @@ TEST_CASE("Binary Formats" * doctest::skip())
|
|||||||
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(89.450));
|
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(89.450));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// the binary formats as function pointers for "Binary formats with narrow number types";
|
|
||||||
// named functions rather than lambdas, because clang 3.5 cannot convert a lambda
|
|
||||||
// to a function pointer in the braced initializer of the format table
|
|
||||||
using narrow_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint32_t, float>;
|
|
||||||
using bytes = std::vector<std::uint8_t>;
|
|
||||||
|
|
||||||
bytes encode_cbor(const json& j)
|
|
||||||
{
|
|
||||||
return json::to_cbor(j);
|
|
||||||
}
|
|
||||||
narrow_json decode_cbor(const bytes& v, bool allow_exceptions)
|
|
||||||
{
|
|
||||||
return narrow_json::from_cbor(v, true, allow_exceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes encode_msgpack(const json& j)
|
|
||||||
{
|
|
||||||
return json::to_msgpack(j);
|
|
||||||
}
|
|
||||||
narrow_json decode_msgpack(const bytes& v, bool allow_exceptions)
|
|
||||||
{
|
|
||||||
return narrow_json::from_msgpack(v, true, allow_exceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes encode_ubjson(const json& j)
|
|
||||||
{
|
|
||||||
return json::to_ubjson(j);
|
|
||||||
}
|
|
||||||
narrow_json decode_ubjson(const bytes& v, bool allow_exceptions)
|
|
||||||
{
|
|
||||||
return narrow_json::from_ubjson(v, true, allow_exceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes encode_bjdata(const json& j)
|
|
||||||
{
|
|
||||||
return json::to_bjdata(j);
|
|
||||||
}
|
|
||||||
narrow_json decode_bjdata(const bytes& v, bool allow_exceptions)
|
|
||||||
{
|
|
||||||
return narrow_json::from_bjdata(v, true, allow_exceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// BSON can only store numbers as object members
|
|
||||||
bytes encode_bson(const json& j)
|
|
||||||
{
|
|
||||||
return json::to_bson(json{{"a", j}});
|
|
||||||
}
|
|
||||||
narrow_json decode_bson(const bytes& v, bool allow_exceptions)
|
|
||||||
{
|
|
||||||
const auto result = narrow_json::from_bson(v, true, allow_exceptions);
|
|
||||||
return result.is_discarded() ? result : result.at("a");
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes encode_bon8(const json& j)
|
|
||||||
{
|
|
||||||
return json::to_bon8(j);
|
|
||||||
}
|
|
||||||
narrow_json decode_bon8(const bytes& v, bool allow_exceptions)
|
|
||||||
{
|
|
||||||
return narrow_json::from_bon8(v, true, allow_exceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("Binary formats with narrow number types")
|
|
||||||
{
|
|
||||||
// Numbers that do not fit the number types are handled like the lexer
|
|
||||||
// handles them in JSON text: an integer that fits neither integer type is
|
|
||||||
// stored as a floating-point number, and a finite floating-point number
|
|
||||||
// that overflows number_float_t is rejected with out_of_range.406.
|
|
||||||
struct binary_format
|
|
||||||
{
|
|
||||||
const char* name;
|
|
||||||
bytes (*encode)(const json&);
|
|
||||||
narrow_json (*decode)(const bytes&, bool);
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::vector<binary_format> formats =
|
|
||||||
{
|
|
||||||
{"CBOR", encode_cbor, decode_cbor},
|
|
||||||
{"MessagePack", encode_msgpack, decode_msgpack},
|
|
||||||
{"UBJSON", encode_ubjson, decode_ubjson},
|
|
||||||
{"BJData", encode_bjdata, decode_bjdata},
|
|
||||||
{"BSON", encode_bson, decode_bson},
|
|
||||||
{"BON8", encode_bon8, decode_bon8},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& format : formats)
|
|
||||||
{
|
|
||||||
const std::string name = format.name;
|
|
||||||
INFO("format := ", name);
|
|
||||||
const auto roundtrip = [&format](const json & j)
|
|
||||||
{
|
|
||||||
return format.decode(format.encode(j), true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// integers that fit keep their type
|
|
||||||
CHECK(roundtrip(json(-5)).is_number_integer());
|
|
||||||
CHECK(roundtrip(json(-5)).get<std::int32_t>() == -5);
|
|
||||||
CHECK(roundtrip(json(3000000000u)).is_number_unsigned());
|
|
||||||
CHECK(roundtrip(json(3000000000u)).get<std::uint32_t>() == 3000000000u);
|
|
||||||
|
|
||||||
// integers that fit neither integer type are stored as float
|
|
||||||
CHECK(roundtrip(json(5000000000u)).is_number_float());
|
|
||||||
CHECK(roundtrip(json(5000000000u)).get<float>() == 5000000000.0f);
|
|
||||||
if (name != "BON8") // BON8 cannot encode integers above INT64_MAX
|
|
||||||
{
|
|
||||||
CHECK(roundtrip(json(10000000000000000000u)).is_number_float());
|
|
||||||
CHECK(roundtrip(json(10000000000000000000u)).get<float>() == 10000000000000000000.0f);
|
|
||||||
}
|
|
||||||
CHECK(roundtrip(json(-3000000000LL)).is_number_float());
|
|
||||||
CHECK(roundtrip(json(-3000000000LL)).get<float>() == -3000000000.0f);
|
|
||||||
CHECK(roundtrip(json(-5000000000LL)).is_number_float());
|
|
||||||
CHECK(roundtrip(json(-5000000000LL)).get<float>() == -5000000000.0f);
|
|
||||||
|
|
||||||
// floating-point numbers that fit
|
|
||||||
CHECK(roundtrip(json(1.5)).get<float>() == 1.5f);
|
|
||||||
const auto just_above_max = std::nextafter(static_cast<double>((std::numeric_limits<float>::max)()),
|
|
||||||
std::numeric_limits<double>::infinity());
|
|
||||||
CHECK(roundtrip(json(just_above_max)).get<float>() == (std::numeric_limits<float>::max)());
|
|
||||||
|
|
||||||
// infinity and NaN are passed on
|
|
||||||
CHECK(std::isinf(roundtrip(json(std::numeric_limits<double>::infinity())).get<float>()));
|
|
||||||
CHECK(std::isnan(roundtrip(json(std::numeric_limits<double>::quiet_NaN())).get<float>()));
|
|
||||||
|
|
||||||
// finite floating-point numbers that overflow number_float_t are rejected
|
|
||||||
const std::string message = "[json.exception.out_of_range.406] syntax error while parsing " + name
|
|
||||||
+ " value: number overflow";
|
|
||||||
CHECK_THROWS_WITH_AS(roundtrip(json(1e300)), message.c_str(), narrow_json::out_of_range&);
|
|
||||||
CHECK_THROWS_WITH_AS(roundtrip(json(-1e300)), message.c_str(), narrow_json::out_of_range&);
|
|
||||||
CHECK(format.decode(format.encode(json(1e300)), false).is_discarded());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+14
-16
@@ -3187,8 +3187,7 @@ TEST_CASE("Tagged values")
|
|||||||
// CBOR encodes negative integers as: result = -1 - n
|
// CBOR encodes negative integers as: result = -1 - n
|
||||||
// For type 0x3B, n is an 8-byte uint64_t. Valid range for n with
|
// For type 0x3B, n is an 8-byte uint64_t. Valid range for n with
|
||||||
// the default int64_t is [0, INT64_MAX], producing results in [INT64_MIN, -1].
|
// the default int64_t is [0, INT64_MAX], producing results in [INT64_MIN, -1].
|
||||||
// When n > INT64_MAX, the result exceeds int64_t range and is stored
|
// When n > INT64_MAX, the result exceeds int64_t range and is rejected.
|
||||||
// as a floating-point number, as the lexer does for JSON text.
|
|
||||||
|
|
||||||
SECTION("n = 0 is valid (result = -1)")
|
SECTION("n = 0 is valid (result = -1)")
|
||||||
{
|
{
|
||||||
@@ -3209,34 +3208,33 @@ TEST_CASE("Tagged values")
|
|||||||
CHECK(result.get<int64_t>() == (std::numeric_limits<int64_t>::min)());
|
CHECK(result.get<int64_t>() == (std::numeric_limits<int64_t>::min)());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("n = INT64_MAX + 1 is stored as float")
|
SECTION("n = INT64_MAX + 1 is rejected (overflow)")
|
||||||
{
|
{
|
||||||
// n = INT64_MAX + 1 (0x8000000000000000)
|
// n = INT64_MAX + 1 (0x8000000000000000)
|
||||||
// result = -1 - n = -9223372036854775809, which exceeds int64_t range;
|
// result = -1 - n = -9223372036854775809, which exceeds int64_t range
|
||||||
// the nearest double is -9223372036854775808.0
|
|
||||||
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
const auto result = json::from_cbor(input);
|
json _;
|
||||||
CHECK(result.is_number_float());
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input),
|
||||||
CHECK(result.get<double>() == -9223372036854775808.0);
|
"[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow",
|
||||||
CHECK(result == json::parse("-9223372036854775809"));
|
json::parse_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("n = UINT64_MAX is stored as float")
|
SECTION("n = UINT64_MAX is rejected (overflow)")
|
||||||
{
|
{
|
||||||
// n = UINT64_MAX (0xFFFFFFFFFFFFFFFF)
|
// n = UINT64_MAX (0xFFFFFFFFFFFFFFFF)
|
||||||
// result = -1 - n = -18446744073709551616, which exceeds int64_t range
|
// result = -1 - n = -18446744073709551616, which exceeds int64_t range
|
||||||
const std::vector<uint8_t> input = {0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
const std::vector<uint8_t> input = {0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
const auto result = json::from_cbor(input);
|
json _;
|
||||||
CHECK(result.is_number_float());
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input),
|
||||||
CHECK(result.get<double>() == -18446744073709551616.0);
|
"[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow",
|
||||||
CHECK(result == json::parse("-18446744073709551616"));
|
json::parse_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("overflow with allow_exceptions=false is not an error")
|
SECTION("overflow with allow_exceptions=false returns discarded")
|
||||||
{
|
{
|
||||||
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
const auto result = json::from_cbor(input, true, false);
|
const auto result = json::from_cbor(input, true, false);
|
||||||
CHECK(result.is_number_float());
|
CHECK(result.is_discarded());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ using nlohmann::json;
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <ostream>
|
||||||
|
#include <streambuf>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -385,3 +388,92 @@ TEST_CASE("locale with a multi-byte decimal point")
|
|||||||
|
|
||||||
CHECK(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
CHECK(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// a streambuf that switches LC_NUMERIC the first time anything is written to
|
||||||
|
// it, so a dump() in progress can be made to change locale mid-flight: after
|
||||||
|
// the serializer was constructed (and, before #5709 item 3, after it had
|
||||||
|
// cached std::localeconv() for the whole call) but before a later float is
|
||||||
|
// converted
|
||||||
|
struct LocaleSwitchingStreambuf final : std::streambuf
|
||||||
|
{
|
||||||
|
explicit LocaleSwitchingStreambuf(const char* switch_to)
|
||||||
|
: locale_after_first_write(switch_to)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string data {}; // NOLINT(readability-redundant-member-init)
|
||||||
|
std::string locale_after_first_write;
|
||||||
|
bool switched = false;
|
||||||
|
|
||||||
|
std::streamsize xsputn(const char* s, std::streamsize n) override
|
||||||
|
{
|
||||||
|
if (!switched)
|
||||||
|
{
|
||||||
|
switched = std::setlocale(LC_NUMERIC, locale_after_first_write.c_str()) != nullptr;
|
||||||
|
}
|
||||||
|
data.append(s, static_cast<std::size_t>(n));
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("locale changes during a single dump() (#5709 item 3)")
|
||||||
|
{
|
||||||
|
// dump_float() only reads the locale on the snprintf path, taken for a
|
||||||
|
// number_float_t that is not an IEEE-754 single or double, i.e. not
|
||||||
|
// (is_iec559 && digits == 24 && max_exponent == 128) and not (is_iec559
|
||||||
|
// && digits == 53 && max_exponent == 1024) - see dump_float(). Checking
|
||||||
|
// is_iec559 alone is not enough: on x86_64, long double is a 64-bit
|
||||||
|
// (80-bit extended) format for which is_iec559 is also true, so it still
|
||||||
|
// takes the snprintf path this test means to exercise. Only a
|
||||||
|
// number_float_t whose digits/max_exponent match float or double (e.g.
|
||||||
|
// long double on 64-bit Arm, where it is IEEE-754 double) takes the
|
||||||
|
// locale-independent to_chars() path instead, and this test is a no-op
|
||||||
|
// there.
|
||||||
|
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||||
|
using ld_limits = std::numeric_limits<long_double_json::number_float_t>;
|
||||||
|
const bool is_ieee_single_or_double =
|
||||||
|
(ld_limits::is_iec559 && ld_limits::digits == 24 && ld_limits::max_exponent == 128) ||
|
||||||
|
(ld_limits::is_iec559 && ld_limits::digits == 53 && ld_limits::max_exponent == 1024);
|
||||||
|
if (is_ieee_single_or_double)
|
||||||
|
{
|
||||||
|
MESSAGE("long double is IEEE-754 single or double on this platform; dump_float()'s snprintf/locale path is not exercised here");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* de_DE_name = "de_DE.UTF-8";
|
||||||
|
if (std::setlocale(LC_NUMERIC, de_DE_name) == nullptr)
|
||||||
|
{
|
||||||
|
de_DE_name = "de_DE";
|
||||||
|
if (std::setlocale(LC_NUMERIC, de_DE_name) == nullptr)
|
||||||
|
{
|
||||||
|
MESSAGE("locale de_DE is not usable");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const std::string decimal_point = std::localeconv()->decimal_point;
|
||||||
|
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||||
|
if (decimal_point != ",")
|
||||||
|
{
|
||||||
|
MESSAGE("de_DE's decimal point is not ',' on this platform, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a string long enough to overflow the serializer's internal write
|
||||||
|
// buffer, so that it is flushed to the output adapter - and the locale
|
||||||
|
// switched - before the number after it is converted
|
||||||
|
const std::string padding(5000, 'a');
|
||||||
|
const long_double_json j = { padding, 1234.5L };
|
||||||
|
|
||||||
|
LocaleSwitchingStreambuf buf(de_DE_name);
|
||||||
|
std::ostream os(&buf);
|
||||||
|
os << j;
|
||||||
|
CHECK(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||||
|
|
||||||
|
REQUIRE(buf.switched);
|
||||||
|
// whatever locale was in effect when the float was actually converted,
|
||||||
|
// the output is normalized to use '.' as the decimal point: it must be
|
||||||
|
// looked up at conversion time, not once for the whole dump() - the same
|
||||||
|
// fix #5597 made on the parser side
|
||||||
|
CHECK(buf.data == "[\"" + padding + "\",1234.5]");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user