Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5cd9db7b3 | ||
|
|
cc31370a78 | ||
|
|
ce7498b54c | ||
|
|
e3e46253ed |
No files matched your search
@@ -13,15 +13,9 @@ function never throws an exception for invalid input, and the returned `#!cpp bo
|
|||||||
is returned.
|
is returned.
|
||||||
|
|
||||||
The result is the same as that of `BasicJsonType::accept()` by design; the difference is how the check is made. This
|
The result is the same as that of `BasicJsonType::accept()` by design; the difference is how the check is made. This
|
||||||
function runs the document's builder, which checks the text as [`parse()`](parse.md) does, while
|
function runs the document's index builder (and discards the index), while `BasicJsonType::accept()` runs the SAX
|
||||||
`BasicJsonType::accept()` runs the SAX parser with a handler that ignores every event. Use this one if you go on to
|
parser with a handler that ignores every event. Use this one if you go on to parse the same input with
|
||||||
parse the same input with [`parse()`](parse.md) anyway, so that both use the same code; either is fine for a plain
|
[`parse()`](parse.md) anyway, so that both use the same code; either is fine for a plain yes/no answer.
|
||||||
yes/no answer.
|
|
||||||
|
|
||||||
When both `ignore_comments` and `ignore_trailing_commas` are `#!cpp false` (the default), the builder writes no index.
|
|
||||||
With either option set, it builds the index as [`parse()`](parse.md) does and discards it: the result is the same, only
|
|
||||||
the time differs. (Neither option needs the index; the variant that skips it exists only for the default options to
|
|
||||||
keep the code size down.)
|
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <bit> //byteswap
|
#include <bit> //byteswap
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <nlohmann/detail/bit_ops.hpp>
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
#include <nlohmann/detail/exceptions.hpp>
|
||||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
#include <nlohmann/detail/input/json_sax.hpp>
|
#include <nlohmann/detail/input/json_sax.hpp>
|
||||||
@@ -741,11 +742,13 @@ class binary_reader
|
|||||||
bool get_cbor_negative_integer()
|
bool get_cbor_negative_integer()
|
||||||
{
|
{
|
||||||
NumberType number{};
|
NumberType number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(number)))
|
return get_number(number) && emit_cbor_negative_integer(number);
|
||||||
{
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// pass the CBOR negative integer -1 - @a number to the SAX parser
|
||||||
|
template<typename NumberType>
|
||||||
|
bool emit_cbor_negative_integer(const NumberType number)
|
||||||
|
{
|
||||||
// the value is -1 - number, which fits into number_integer_t
|
// the value is -1 - number, which fits into number_integer_t
|
||||||
// whenever number does; the outer cast undoes the integral promotion
|
// whenever number does; the outer cast undoes the integral promotion
|
||||||
// for number_integer_t types narrower than int
|
// for number_integer_t types narrower than int
|
||||||
@@ -970,7 +973,7 @@ class binary_reader
|
|||||||
case 0x95:
|
case 0x95:
|
||||||
case 0x96:
|
case 0x96:
|
||||||
case 0x97:
|
case 0x97:
|
||||||
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_cbor_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
||||||
|
|
||||||
case 0x98: // array (one-byte uint8_t for n follows)
|
case 0x98: // array (one-byte uint8_t for n follows)
|
||||||
case 0x99: // array (two-byte uint16_t for n follow)
|
case 0x99: // array (two-byte uint16_t for n follow)
|
||||||
@@ -979,7 +982,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -1133,16 +1136,10 @@ class binary_reader
|
|||||||
return get_half_float(false);
|
return get_half_float(false);
|
||||||
|
|
||||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<float>();
|
||||||
float number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<double>();
|
||||||
double number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // anything else (0xFF is handled inside the other types)
|
default: // anything else (0xFF is handled inside the other types)
|
||||||
{
|
{
|
||||||
@@ -1151,6 +1148,16 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// read a single- or double-precision float (kept out of
|
||||||
|
/// parse_cbor_value, which is inlined into parse_cbor_internal)
|
||||||
|
template<typename NumberType>
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool get_cbor_float()
|
||||||
|
{
|
||||||
|
NumberType number{};
|
||||||
|
return get_number(number) && emit_float(number);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reports a nested indefinite-length CBOR string or byte array
|
@brief reports a nested indefinite-length CBOR string or byte array
|
||||||
@param[in] type_name name of the rejected string type
|
@param[in] type_name name of the rejected string type
|
||||||
@@ -1676,6 +1683,157 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read a big-endian number of type @a NumberType at @a data
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
NumberType load_big_endian(const unsigned char* data) const noexcept
|
||||||
|
{
|
||||||
|
NumberType result{};
|
||||||
|
std::memcpy(&result, data, sizeof(NumberType));
|
||||||
|
if (is_little_endian)
|
||||||
|
{
|
||||||
|
byte_swap(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the integers, floats, booleans, and nulls that follow in a
|
||||||
|
definite-length array, without the per-element bookkeeping of
|
||||||
|
@ref parse_cbor_internal
|
||||||
|
|
||||||
|
Stops at the end of the array, at any other kind of value, and when fewer
|
||||||
|
than 9 bytes (the longest of these values) are left in the input, leaving
|
||||||
|
those to @ref parse_cbor_value, which reads them the same way.
|
||||||
|
|
||||||
|
@param[in,out] remaining the number of elements of the array not yet read
|
||||||
|
@return whether the SAX parser accepted the values
|
||||||
|
*/
|
||||||
|
bool parse_cbor_scalar_run(std::size_t& remaining)
|
||||||
|
{
|
||||||
|
while (remaining != 0 && ia.bulk_remaining() >= 9)
|
||||||
|
{
|
||||||
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
|
const unsigned char head = data[0];
|
||||||
|
std::size_t size = 0;
|
||||||
|
bool accepted = true;
|
||||||
|
switch (head)
|
||||||
|
{
|
||||||
|
case 0x18:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_unsigned(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x19:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x38:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_cbor_negative_integer(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x39:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xF4:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(false);
|
||||||
|
break;
|
||||||
|
case 0xF5:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(true);
|
||||||
|
break;
|
||||||
|
case 0xF6:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->null();
|
||||||
|
break;
|
||||||
|
case 0xFA:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_float(load_big_endian<float>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xFB:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_float(load_big_endian<double>(data + 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (head <= 0x17)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_unsigned(static_cast<number_unsigned_t>(head));
|
||||||
|
}
|
||||||
|
else if (head >= 0x20 && head <= 0x37)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - head));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// any other value is read by parse_cbor_value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// as if read by get() and get_number(): current is the head byte
|
||||||
|
ia.bulk_skip(size);
|
||||||
|
chars_read += size;
|
||||||
|
current = head;
|
||||||
|
--remaining;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!accepted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief open a definite-length array and read the scalars it begins with
|
||||||
|
(see @ref parse_cbor_scalar_run)
|
||||||
|
|
||||||
|
Only the start of an array is tried, which covers arrays of numbers
|
||||||
|
(e.g. coordinates) but costs arrays of other values only one test, not one
|
||||||
|
per element.
|
||||||
|
|
||||||
|
@param[in] len the number of elements
|
||||||
|
@return whether the SAX parser accepted the array and the values read
|
||||||
|
*/
|
||||||
|
bool enter_cbor_array(const std::size_t len)
|
||||||
|
{
|
||||||
|
return enter_array(len) && (len == 0 || parse_cbor_array_start(std::integral_constant<bool, bulk_scan> {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read the scalars the array just opened begins with; out of line, so
|
||||||
|
/// that @ref parse_cbor_value stays small enough to be inlined into
|
||||||
|
/// @ref parse_cbor_internal
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool parse_cbor_array_start(std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
return parse_cbor_scalar_run(container_stack.back().remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input that is not contiguous: every value is read by parse_cbor_value
|
||||||
|
bool parse_cbor_array_start(std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a CBOR value and everything nested inside it
|
@brief read a CBOR value and everything nested inside it
|
||||||
|
|
||||||
@@ -1705,6 +1863,7 @@ class binary_reader
|
|||||||
// pop_back() below, which destroys the container_stack element
|
// pop_back() below, which destroys the container_stack element
|
||||||
// it would otherwise alias
|
// it would otherwise alias
|
||||||
const container_frame top = container_stack.back();
|
const container_frame top = container_stack.back();
|
||||||
|
const bool is_object = top.is_object;
|
||||||
bool at_end = false;
|
bool at_end = false;
|
||||||
|
|
||||||
if (top.remaining != npos)
|
if (top.remaining != npos)
|
||||||
@@ -1716,7 +1875,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
// claim the element about to be read
|
// claim the element about to be read
|
||||||
--container_stack.back().remaining;
|
--container_stack.back().remaining;
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
}
|
}
|
||||||
@@ -1729,7 +1888,7 @@ class binary_reader
|
|||||||
// Testing for it consumes a byte, which is the first byte
|
// Testing for it consumes a byte, which is the first byte
|
||||||
// of the next element when it is not one.
|
// of the next element when it is not one.
|
||||||
at_end = (get() == 0xFF);
|
at_end = (get() == 0xFF);
|
||||||
fetch = top.is_object;
|
fetch = is_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
@@ -1746,7 +1905,7 @@ class binary_reader
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||||
{
|
{
|
||||||
@@ -1971,7 +2130,7 @@ class binary_reader
|
|||||||
case 0x9D:
|
case 0x9D:
|
||||||
case 0x9E:
|
case 0x9E:
|
||||||
case 0x9F:
|
case 0x9F:
|
||||||
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
|
return enter_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
|
||||||
|
|
||||||
// fixstr
|
// fixstr
|
||||||
case 0xA0:
|
case 0xA0:
|
||||||
@@ -2102,13 +2261,13 @@ class binary_reader
|
|||||||
case 0xDC: // array 16
|
case 0xDC: // array 16
|
||||||
{
|
{
|
||||||
std::uint16_t len{};
|
std::uint16_t len{};
|
||||||
return get_number(len) && enter_array(static_cast<std::size_t>(len));
|
return get_number(len) && enter_msgpack_array(static_cast<std::size_t>(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDD: // array 32
|
case 0xDD: // array 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(len) && enter_array(conditional_static_cast<std::size_t>(len));
|
return get_number(len) && enter_msgpack_array(conditional_static_cast<std::size_t>(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDE: // map 16
|
case 0xDE: // map 16
|
||||||
@@ -2450,6 +2609,138 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the integers, floats, booleans, and nils that follow in a
|
||||||
|
MessagePack array, without the per-element bookkeeping of
|
||||||
|
@ref parse_msgpack_internal
|
||||||
|
|
||||||
|
Stops at the end of the array, at any other kind of value, and when fewer
|
||||||
|
than 9 bytes (the longest of these values) are left in the input, leaving
|
||||||
|
those to @ref parse_msgpack_value, which reads them the same way.
|
||||||
|
|
||||||
|
@param[in,out] remaining the number of elements of the array not yet read
|
||||||
|
@return whether the SAX parser accepted the values
|
||||||
|
*/
|
||||||
|
bool parse_msgpack_scalar_run(std::size_t& remaining)
|
||||||
|
{
|
||||||
|
while (remaining != 0 && ia.bulk_remaining() >= 9)
|
||||||
|
{
|
||||||
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
|
const unsigned char head = data[0];
|
||||||
|
std::size_t size = 0;
|
||||||
|
bool accepted = true;
|
||||||
|
switch (head)
|
||||||
|
{
|
||||||
|
case 0xC0: // nil
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->null();
|
||||||
|
break;
|
||||||
|
case 0xC2: // false
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(false);
|
||||||
|
break;
|
||||||
|
case 0xC3: // true
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(true);
|
||||||
|
break;
|
||||||
|
case 0xCA: // float 32
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_float(load_big_endian<float>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCB: // float 64
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_float(load_big_endian<double>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCC: // uint 8
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_unsigned(data[1]);
|
||||||
|
break;
|
||||||
|
case 0xCD: // uint 16
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCE: // uint 32
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCF: // uint 64
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xD0: // int 8
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_signed(static_cast<std::int8_t>(data[1]));
|
||||||
|
break;
|
||||||
|
case 0xD1: // int 16
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_signed(load_big_endian<std::int16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xD2: // int 32
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_signed(load_big_endian<std::int32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xD3: // int 64
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_signed(load_big_endian<std::int64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (head <= 0x7F)
|
||||||
|
{
|
||||||
|
// positive fixint
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_unsigned(static_cast<number_unsigned_t>(head));
|
||||||
|
}
|
||||||
|
else if (head >= 0xE0)
|
||||||
|
{
|
||||||
|
// negative fixint
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_integer(static_cast<std::int8_t>(head));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// any other value is read by parse_msgpack_value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// as if read by get() and get_number(): current is the last byte
|
||||||
|
ia.bulk_skip(size);
|
||||||
|
chars_read += size;
|
||||||
|
current = data[size - 1];
|
||||||
|
--remaining;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!accepted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief open a MessagePack array and read the scalars it begins with (see
|
||||||
|
@ref parse_msgpack_scalar_run)
|
||||||
|
@param[in] len the number of elements
|
||||||
|
@return whether the SAX parser accepted the array and the values read
|
||||||
|
*/
|
||||||
|
bool enter_msgpack_array(const std::size_t len)
|
||||||
|
{
|
||||||
|
return enter_array(len) && (len == 0 || parse_msgpack_array_start(std::integral_constant<bool, bulk_scan> {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read the scalars the array just opened begins with; out of line, so
|
||||||
|
/// that @ref parse_msgpack_value stays small enough to be inlined
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool parse_msgpack_array_start(std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
return parse_msgpack_scalar_run(container_stack.back().remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input that is not contiguous: every value is read by parse_msgpack_value
|
||||||
|
bool parse_msgpack_array_start(std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a MessagePack value and everything nested inside it
|
@brief read a MessagePack value and everything nested inside it
|
||||||
|
|
||||||
@@ -3691,13 +3982,13 @@ class binary_reader
|
|||||||
return sax->null();
|
return sax->null();
|
||||||
|
|
||||||
case 0xFB:
|
case 0xFB:
|
||||||
return sax->number_float(static_cast<number_float_t>(-1.0), "");
|
return sax->number_float(static_cast<number_float_t>(-1.0), no_source);
|
||||||
|
|
||||||
case 0xFC:
|
case 0xFC:
|
||||||
return sax->number_float(static_cast<number_float_t>(0.0), "");
|
return sax->number_float(static_cast<number_float_t>(0.0), no_source);
|
||||||
|
|
||||||
case 0xFD:
|
case 0xFD:
|
||||||
return sax->number_float(static_cast<number_float_t>(1.0), "");
|
return sax->number_float(static_cast<number_float_t>(1.0), no_source);
|
||||||
|
|
||||||
case 0xFF: // empty string
|
case 0xFF: // empty string
|
||||||
{
|
{
|
||||||
@@ -4066,6 +4357,27 @@ class binary_reader
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a double with a single byteswap(): GCC does
|
||||||
|
/// not merge the byte-wise swap of the generic version into one
|
||||||
|
static void byte_swap(double& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(double) == sizeof(std::uint64_t), "double must have 64 bits");
|
||||||
|
std::uint64_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a float with a single byteswap()
|
||||||
|
static void byte_swap(float& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(float) == sizeof(std::uint32_t), "float must have 32 bits");
|
||||||
|
std::uint32_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
template<class NumberType>
|
template<class NumberType>
|
||||||
static void byte_swap(NumberType& number)
|
static void byte_swap(NumberType& number)
|
||||||
{
|
{
|
||||||
@@ -4201,7 +4513,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
||||||
}
|
}
|
||||||
return sax->number_float(result, "");
|
return sax->number_float(result, no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4261,7 +4573,7 @@ class binary_reader
|
|||||||
}();
|
}();
|
||||||
return sax->number_float((half & 0x8000u) != 0
|
return sax->number_float((half & 0x8000u) != 0
|
||||||
? static_cast<number_float_t>(-val)
|
? static_cast<number_float_t>(-val)
|
||||||
: static_cast<number_float_t>(val), "");
|
: static_cast<number_float_t>(val), no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4586,6 +4898,10 @@ class binary_reader
|
|||||||
/// the SAX parser
|
/// the SAX parser
|
||||||
json_sax_t* sax = nullptr;
|
json_sax_t* sax = nullptr;
|
||||||
|
|
||||||
|
/// the (empty) source text of every number_float event: binary formats
|
||||||
|
/// have none, and a string_t built for each float would cost more
|
||||||
|
const string_t no_source{};
|
||||||
|
|
||||||
/// the containers that have been opened and not closed yet; see @ref container_frame
|
/// the containers that have been opened and not closed yet; see @ref container_frame
|
||||||
std::vector<container_frame> container_stack{};
|
std::vector<container_frame> container_stack{};
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,7 @@ struct parse_failure
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
||||||
/// Store: false for accept(): the grammar, number, string and UTF-8 checks run as for parse(), but no node is written
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel>
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel, bool Store>
|
|
||||||
class builder
|
class builder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -246,87 +245,11 @@ class builder
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the string from p, the first backslash, control character, ill-formed UTF-8 byte, or the end, checked and not
|
|
||||||
/// decoded (accept() stores nothing, so the arena is not written): the checks, errors and error positions of
|
|
||||||
/// slow_string(), for everything up to the closing quote
|
|
||||||
NLOHMANN_VIEW_NOINLINE decoded check_string(const unsigned char* p)
|
|
||||||
{
|
|
||||||
const unsigned char* const end = m_end;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char c = *p;
|
|
||||||
if (c == '"')
|
|
||||||
{
|
|
||||||
++p;
|
|
||||||
return decoded{p, 0, 0};
|
|
||||||
}
|
|
||||||
if (c != '\\')
|
|
||||||
{
|
|
||||||
return failed(c < 0x20 ? error_code::string_control_character : error_code::string_utf8, p);
|
|
||||||
}
|
|
||||||
++p;
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char d = *p++;
|
|
||||||
if (d == 'u')
|
|
||||||
{
|
|
||||||
const int cp = hex4(p); // (moves p past the four digits)
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cp < 0))
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY((cp & 0xF800) == 0xD800)) // a surrogate
|
|
||||||
{
|
|
||||||
if (cp >= 0xDC00)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_low, p);
|
|
||||||
}
|
|
||||||
if (end - p < 2 || p[0] != '\\' || p[1] != 'u')
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
p += 2;
|
|
||||||
const int lo = hex4(p); // (moves p past the four digits)
|
|
||||||
if (lo < 0)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (lo < 0xDC00 || lo > 0xDFFF)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!(d == '"' || d == '\\' || d == '/' || d == 'b' || d == 'f' || d == 'n' || d == 'r' || d == 't'))
|
|
||||||
{
|
|
||||||
--p;
|
|
||||||
return failed(error_code::string_escape, p);
|
|
||||||
}
|
|
||||||
if (p != end && *p == '\\')
|
|
||||||
{
|
|
||||||
continue; // (escapes in a row: no run of text between them)
|
|
||||||
}
|
|
||||||
p = scan_string_run(p, end); // the text up to the next backslash, quote, control character, or ill-formed UTF-8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// s points to the start of the string, p to a backslash, a control
|
/// s points to the start of the string, p to a backslash, a control
|
||||||
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
||||||
/// the arena
|
/// the arena
|
||||||
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// (accept(): the string is checked, not decoded)
|
|
||||||
static_cast<void>(s);
|
|
||||||
return check_string(p);
|
|
||||||
}
|
|
||||||
// single-character escapes; 0: invalid (and 'u', handled separately)
|
// single-character escapes; 0: invalid (and 'u', handled separately)
|
||||||
static const std::array<char, 128> simple_escape =
|
static const std::array<char, 128> simple_escape =
|
||||||
{
|
{
|
||||||
@@ -687,13 +610,10 @@ run_found:
|
|||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
||||||
{
|
{
|
||||||
if (enabled(Store))
|
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
||||||
{
|
base = cold.m_doc.nodes;
|
||||||
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
out = base;
|
||||||
base = cold.m_doc.nodes;
|
cap = base + cold.m_doc.node_capacity;
|
||||||
out = base;
|
|
||||||
cap = base + cold.m_doc.node_capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
||||||
{
|
{
|
||||||
@@ -877,10 +797,7 @@ close_object:
|
|||||||
// an array pays nothing for this)
|
// an array pays nothing for this)
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
||||||
{
|
{
|
||||||
if (enabled(Store))
|
cold.note_large_object(cur_idx);
|
||||||
{
|
|
||||||
cold.note_large_object(cur_idx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close_container:
|
close_container:
|
||||||
@@ -1040,12 +957,6 @@ indent_done:
|
|||||||
/// an integer's value); two stores on little-endian targets
|
/// an integer's value); two stores on little-endian targets
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// accept(): nothing is stored, so what computes the arguments is not run either. base and out stay
|
|
||||||
// null: their difference, which open() takes, is 0.
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
||||||
{
|
{
|
||||||
const auto n = static_cast<std::size_t>(out - base);
|
const auto n = static_cast<std::size_t>(out - base);
|
||||||
@@ -1104,12 +1015,9 @@ indent_done:
|
|||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
||||||
{
|
{
|
||||||
if (enabled(Store))
|
node& n = base[cur_idx];
|
||||||
{
|
n.len = cur_count;
|
||||||
node& n = base[cur_idx];
|
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||||
n.len = cur_count;
|
|
||||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
|
||||||
}
|
|
||||||
if (--depth != 0)
|
if (--depth != 0)
|
||||||
{
|
{
|
||||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||||
@@ -1284,44 +1192,34 @@ indent_done:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// run the builder with compile-time options
|
/// run the builder with compile-time options
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Store>
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd>
|
||||||
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
||||||
{
|
{
|
||||||
if (sentinel)
|
if (sentinel)
|
||||||
{
|
{
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
||||||
/// number_float_t of the document; validate_only: accept(), only the result is
|
/// number_float_t of the document
|
||||||
/// used
|
|
||||||
template<typename FloatType, bool NulIsEnd>
|
template<typename FloatType, bool NulIsEnd>
|
||||||
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, bool validate_only,
|
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, parse_failure& failure)
|
||||||
parse_failure& failure)
|
|
||||||
{
|
{
|
||||||
if (validate_only && !comments && !trailing_commas)
|
|
||||||
{
|
|
||||||
// accept() of plain JSON: nothing is stored. The other option sets
|
|
||||||
// could skip the stores just as well; they keep the builder that
|
|
||||||
// stores only so that there are two more instantiations of the parse
|
|
||||||
// loop, not eight.
|
|
||||||
return build_with<FloatType, false, false, NulIsEnd, false>(d, src, size, sentinel, failure);
|
|
||||||
}
|
|
||||||
if (comments)
|
if (comments)
|
||||||
{
|
{
|
||||||
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, true, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, true, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, false, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, false, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
|
|||||||
@@ -1060,7 +1060,7 @@ class basic_json_document
|
|||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_range(first, last, error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.read_range(first, last, allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,7 +1075,7 @@ class basic_json_document
|
|||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
append_input(buf, std::forward<InputType>(input));
|
append_input(buf, std::forward<InputType>(input));
|
||||||
d.build_owned(std::move(buf), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.build_owned(std::move(buf), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1088,8 +1088,7 @@ class basic_json_document
|
|||||||
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_kind(std::forward<InputType>(input), on_error::validate, ignore_comments, ignore_trailing_commas,
|
d.read(std::forward<InputType>(input), false, ignore_comments, ignore_trailing_commas);
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
|
||||||
return !d.is_discarded();
|
return !d.is_discarded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1105,7 +1104,7 @@ class basic_json_document
|
|||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
read_kind(std::forward<InputType>(input), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas,
|
read_kind(std::forward<InputType>(input), allow_exceptions, ignore_comments, ignore_trailing_commas,
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1464,22 +1463,6 @@ class basic_json_document
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// what a failed parse does
|
|
||||||
enum class on_error : std::uint8_t
|
|
||||||
{
|
|
||||||
discard, ///< the document is discarded
|
|
||||||
raise, ///< parse_error is thrown
|
|
||||||
validate ///< accept(): the document is discarded, and no node is stored while parsing
|
|
||||||
};
|
|
||||||
|
|
||||||
static on_error error_mode(bool allow_exceptions) noexcept
|
|
||||||
{
|
|
||||||
return allow_exceptions ? on_error::raise : on_error::discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NulIsEnd of the builder: a null byte ends the input unless strict_nul_handling
|
|
||||||
static constexpr bool nul_is_end = !detail::abi_config::strict_nul_handling;
|
|
||||||
|
|
||||||
/// create the storage (sized for the input) on first use
|
/// create the storage (sized for the input) on first use
|
||||||
void ensure_data(const char* src, std::size_t size)
|
void ensure_data(const char* src, std::size_t size)
|
||||||
{
|
{
|
||||||
@@ -1491,7 +1474,7 @@ class basic_json_document
|
|||||||
|
|
||||||
/// parse a buffer the document takes ownership of (it replaces the
|
/// parse a buffer the document takes ownership of (it replaces the
|
||||||
/// previous owned copy)
|
/// previous owned copy)
|
||||||
void build_owned(std::string&& buf, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_owned(std::string&& buf, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
ensure_data(buf.data(), buf.size());
|
ensure_data(buf.data(), buf.size());
|
||||||
m_data->owned = std::move(buf);
|
m_data->owned = std::move(buf);
|
||||||
@@ -1501,7 +1484,7 @@ class basic_json_document
|
|||||||
/// parse a copy of the input; the copy is appended to the cleared owned
|
/// parse a copy of the input; the copy is appended to the cleared owned
|
||||||
/// buffer, so that its capacity is reused
|
/// buffer, so that its capacity is reused
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void build_copy(T&& input, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_copy(T&& input, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
if (!m_data)
|
if (!m_data)
|
||||||
{
|
{
|
||||||
@@ -1522,7 +1505,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
||||||
void build(const char* src, std::size_t size, on_error allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
void build(const char* src, std::size_t size, bool allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
||||||
{
|
{
|
||||||
ensure_data(src, size);
|
ensure_data(src, size);
|
||||||
document_data& d = *m_data;
|
document_data& d = *m_data;
|
||||||
@@ -1549,8 +1532,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ok = detail::view::build<typename BasicJsonType::number_float_t, nul_is_end>(d, src, size, comments, trailing_commas, sentinel,
|
ok = detail::view::build < typename BasicJsonType::number_float_t, !detail::abi_config::strict_nul_handling > (d, src, size, comments, trailing_commas, sentinel, failure);
|
||||||
allow_exceptions == on_error::validate, failure);
|
|
||||||
}
|
}
|
||||||
if (NLOHMANN_VIEW_LIKELY(ok))
|
if (NLOHMANN_VIEW_LIKELY(ok))
|
||||||
{
|
{
|
||||||
@@ -1563,7 +1545,7 @@ class basic_json_document
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::vector<std::uint32_t>().swap(d.large_objects);
|
std::vector<std::uint32_t>().swap(d.large_objects);
|
||||||
if (allow_exceptions == on_error::raise)
|
if (allow_exceptions)
|
||||||
{
|
{
|
||||||
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
||||||
}
|
}
|
||||||
@@ -1571,13 +1553,13 @@ class basic_json_document
|
|||||||
|
|
||||||
// --- input dispatch (see detail::view::input_kind) ---
|
// --- input dispatch (see detail::view::input_kind) ---
|
||||||
|
|
||||||
void read_kind(std::string&& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
void read_kind(std::string&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
||||||
{
|
{
|
||||||
build_owned(std::move(s), ae, c, tc);
|
build_owned(std::move(s), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -1591,7 +1573,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename Array>
|
template<typename Array>
|
||||||
void read_kind(Array& a, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
void read_kind(Array& a, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
||||||
{
|
{
|
||||||
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
||||||
const std::size_t n = std::extent<Array>::value;
|
const std::size_t n = std::extent<Array>::value;
|
||||||
@@ -1602,7 +1584,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
||||||
{
|
{
|
||||||
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
||||||
const auto size = static_cast<std::size_t>(s.size());
|
const auto size = static_cast<std::size_t>(s.size());
|
||||||
@@ -1611,13 +1593,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(s, ae, c, tc);
|
build_copy(s, ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -1629,13 +1611,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(T&& input, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
void read_kind(T&& input, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(std::forward<T>(input), ae, c, tc);
|
build_copy(std::forward<T>(input), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range(IteratorType first, IteratorType last, on_error ae, bool c, bool tc)
|
void read_range(IteratorType first, IteratorType last, bool ae, bool c, bool tc)
|
||||||
{
|
{
|
||||||
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
||||||
// borrow the range where the library's input adapter scans it as one
|
// borrow the range where the library's input adapter scans it as one
|
||||||
@@ -1646,14 +1628,14 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
||||||
{
|
{
|
||||||
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
||||||
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::false_type /*other*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::false_type /*other*/)
|
||||||
{
|
{
|
||||||
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+567
-250
@@ -8852,6 +8852,233 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <bit> //byteswap
|
#include <bit> //byteswap
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/bit_ops.hpp>
|
||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstdint> // uint64_t
|
||||||
|
#include <cstring> // memcpy
|
||||||
|
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) && (!defined(__SIZEOF_INT128__) || (!defined(__GNUC__) && !defined(__clang__)))
|
||||||
|
#include <intrin0.h> // __umulh, _umul128, _BitScanForward64, _BitScanReverse64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <cstdlib> // _byteswap_ushort, _byteswap_ulong, _byteswap_uint64
|
||||||
|
#endif
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
#include <intrin.h> // _BitScanForward, _BitScanReverse
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/macro_scope.hpp>
|
||||||
|
// JSON_HEDLEY_ALWAYS_INLINE, NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_bit_cast) || defined(__cpp_lib_bitops) || defined(__cpp_lib_byteswap)
|
||||||
|
#include <bit> // bit_cast, byteswap, countl_zero, countr_zero
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Portable bit-level helpers for the number and string scanners. They use
|
||||||
|
// compiler builtins or platform-specific intrinsics where available, the
|
||||||
|
// C++20 <bit> functions next, and plain C++ otherwise, so they work regardless
|
||||||
|
// of byte order. (The builtins and intrinsics come first: MSVC's
|
||||||
|
// std::countl_zero checks for LZCNT at run time unless AVX2 is enabled.)
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/// number of leading zero bits of x (x != 0)
|
||||||
|
inline int count_leading_zeros(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_clzll(x);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||||
|
_BitScanReverse64(&index, x);
|
||||||
|
return 63 - static_cast<int>(index);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
||||||
|
if (_BitScanReverse(&index, static_cast<unsigned long>(x >> 32u))) // NOLINT(runtime/int)
|
||||||
|
{
|
||||||
|
return 31 - static_cast<int>(index);
|
||||||
|
}
|
||||||
|
_BitScanReverse(&index, static_cast<unsigned long>(x)); // NOLINT(runtime/int)
|
||||||
|
return 63 - static_cast<int>(index);
|
||||||
|
#elif defined(__cpp_lib_bitops)
|
||||||
|
return std::countl_zero(x);
|
||||||
|
#else
|
||||||
|
int n = 0;
|
||||||
|
for (int shift = 32; shift != 0; shift >>= 1)
|
||||||
|
{
|
||||||
|
if ((x >> (64 - shift)) == 0)
|
||||||
|
{
|
||||||
|
n += shift;
|
||||||
|
x <<= shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// number of trailing zero bits of x (x != 0)
|
||||||
|
inline int count_trailing_zeros(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_ctzll(x);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||||
|
_BitScanForward64(&index, x);
|
||||||
|
return static_cast<int>(index);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
||||||
|
if (_BitScanForward(&index, static_cast<unsigned long>(x))) // NOLINT(runtime/int)
|
||||||
|
{
|
||||||
|
return static_cast<int>(index);
|
||||||
|
}
|
||||||
|
_BitScanForward(&index, static_cast<unsigned long>(x >> 32u)); // NOLINT(runtime/int)
|
||||||
|
return 32 + static_cast<int>(index);
|
||||||
|
#elif defined(__cpp_lib_bitops)
|
||||||
|
return std::countr_zero(x);
|
||||||
|
#else
|
||||||
|
int n = 0;
|
||||||
|
for (int shift = 32; shift != 0; shift >>= 1)
|
||||||
|
{
|
||||||
|
if ((x << (64 - shift)) == 0)
|
||||||
|
{
|
||||||
|
n += shift;
|
||||||
|
x >>= shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the object representation of @a from as a @a To (std::bit_cast in C++20)
|
||||||
|
template<typename To, typename From>
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE To bit_cast(const From& from) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(To) == sizeof(From), "bit_cast needs types of the same size");
|
||||||
|
#if defined(__cpp_lib_bit_cast)
|
||||||
|
return std::bit_cast<To>(from);
|
||||||
|
#else
|
||||||
|
To to {};
|
||||||
|
std::memcpy(&to, &from, sizeof(To));
|
||||||
|
return to;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x with its bytes in reverse order (std::byteswap in C++23); compilers lower
|
||||||
|
/// it to a single bswap/rev/movbe
|
||||||
|
inline std::uint16_t byteswap(std::uint16_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_bswap16(x);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
return _byteswap_ushort(x);
|
||||||
|
#elif defined(__cpp_lib_byteswap)
|
||||||
|
return std::byteswap(x);
|
||||||
|
#else
|
||||||
|
return static_cast<std::uint16_t>((x >> 8u) | (x << 8u));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x with its bytes in reverse order (std::byteswap in C++23)
|
||||||
|
inline std::uint32_t byteswap(std::uint32_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_bswap32(x);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
return _byteswap_ulong(x);
|
||||||
|
#elif defined(__cpp_lib_byteswap)
|
||||||
|
return std::byteswap(x);
|
||||||
|
#else
|
||||||
|
return ((x & 0x000000FFu) << 24u) | ((x & 0x0000FF00u) << 8u)
|
||||||
|
| ((x & 0x00FF0000u) >> 8u) | ((x & 0xFF000000u) >> 24u);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x with its bytes in reverse order (std::byteswap in C++23)
|
||||||
|
inline std::uint64_t byteswap(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_bswap64(x);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
return _byteswap_uint64(x);
|
||||||
|
#elif defined(__cpp_lib_byteswap)
|
||||||
|
return std::byteswap(x);
|
||||||
|
#else
|
||||||
|
x = ((x & 0x00000000FFFFFFFFull) << 32u) | ((x & 0xFFFFFFFF00000000ull) >> 32u);
|
||||||
|
x = ((x & 0x0000FFFF0000FFFFull) << 16u) | ((x & 0xFFFF0000FFFF0000ull) >> 16u);
|
||||||
|
x = ((x & 0x00FF00FF00FF00FFull) << 8u) | ((x & 0xFF00FF00FF00FF00ull) >> 8u);
|
||||||
|
return x;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the 128-bit product of two 64-bit numbers
|
||||||
|
struct uint128_parts
|
||||||
|
{
|
||||||
|
std::uint64_t low;
|
||||||
|
std::uint64_t high;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__SIZEOF_INT128__)
|
||||||
|
__extension__ using uint128 = unsigned __int128;
|
||||||
|
const uint128 r = static_cast<uint128>(a) * b;
|
||||||
|
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
|
||||||
|
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||||
|
std::uint64_t high = 0;
|
||||||
|
const std::uint64_t low = _umul128(a, b, &high);
|
||||||
|
return {low, high};
|
||||||
|
#elif defined(_MSC_VER) && defined(_M_ARM64)
|
||||||
|
return {a * b, __umulh(a, b)};
|
||||||
|
#else
|
||||||
|
const std::uint64_t a_lo = a & 0xFFFFFFFFu;
|
||||||
|
const std::uint64_t a_hi = a >> 32u;
|
||||||
|
const std::uint64_t b_lo = b & 0xFFFFFFFFu;
|
||||||
|
const std::uint64_t b_hi = b >> 32u;
|
||||||
|
const std::uint64_t lo_lo = a_lo * b_lo;
|
||||||
|
const std::uint64_t hi_lo = a_hi * b_lo;
|
||||||
|
const std::uint64_t lo_hi = a_lo * b_hi;
|
||||||
|
const std::uint64_t hi_hi = a_hi * b_hi;
|
||||||
|
const std::uint64_t cross = (lo_lo >> 32u) + (hi_lo & 0xFFFFFFFFu) + lo_hi;
|
||||||
|
return {(cross << 32u) | (lo_lo & 0xFFFFFFFFu), (hi_lo >> 32u) + (cross >> 32u) + hi_hi};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// eight bytes as a little-endian word (a single load on little-endian
|
||||||
|
/// targets; always inlined, as GCC otherwise calls it in the number loops)
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const unsigned char* b) noexcept
|
||||||
|
{
|
||||||
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
|
// the byte order already matches
|
||||||
|
std::uint64_t result = 0;
|
||||||
|
std::memcpy(&result, b, sizeof(result));
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
||||||
|
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
||||||
|
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
||||||
|
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// eight bytes as a little-endian word
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const char* p) noexcept
|
||||||
|
{
|
||||||
|
return read_eight_bytes(reinterpret_cast<const unsigned char*>(p)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
// #include <nlohmann/detail/exceptions.hpp>
|
// #include <nlohmann/detail/exceptions.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/detail/input/input_adapters.hpp>
|
// #include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
@@ -10316,231 +10543,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
// #include <nlohmann/detail/bit_ops.hpp>
|
// #include <nlohmann/detail/bit_ops.hpp>
|
||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <cstdint> // uint64_t
|
|
||||||
#include <cstring> // memcpy
|
|
||||||
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) && (!defined(__SIZEOF_INT128__) || (!defined(__GNUC__) && !defined(__clang__)))
|
|
||||||
#include <intrin0.h> // __umulh, _umul128, _BitScanForward64, _BitScanReverse64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#include <cstdlib> // _byteswap_ushort, _byteswap_ulong, _byteswap_uint64
|
|
||||||
#endif
|
|
||||||
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_IX86) || defined(_M_ARM))
|
|
||||||
#include <intrin.h> // _BitScanForward, _BitScanReverse
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// #include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
// JSON_HEDLEY_ALWAYS_INLINE, NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
|
|
||||||
#if defined(__cpp_lib_bit_cast) || defined(__cpp_lib_bitops) || defined(__cpp_lib_byteswap)
|
|
||||||
#include <bit> // bit_cast, byteswap, countl_zero, countr_zero
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Portable bit-level helpers for the number and string scanners. They use
|
|
||||||
// compiler builtins or platform-specific intrinsics where available, the
|
|
||||||
// C++20 <bit> functions next, and plain C++ otherwise, so they work regardless
|
|
||||||
// of byte order. (The builtins and intrinsics come first: MSVC's
|
|
||||||
// std::countl_zero checks for LZCNT at run time unless AVX2 is enabled.)
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
|
|
||||||
/// number of leading zero bits of x (x != 0)
|
|
||||||
inline int count_leading_zeros(std::uint64_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_clzll(x);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
|
||||||
_BitScanReverse64(&index, x);
|
|
||||||
return 63 - static_cast<int>(index);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
|
||||||
if (_BitScanReverse(&index, static_cast<unsigned long>(x >> 32u))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return 31 - static_cast<int>(index);
|
|
||||||
}
|
|
||||||
_BitScanReverse(&index, static_cast<unsigned long>(x)); // NOLINT(runtime/int)
|
|
||||||
return 63 - static_cast<int>(index);
|
|
||||||
#elif defined(__cpp_lib_bitops)
|
|
||||||
return std::countl_zero(x);
|
|
||||||
#else
|
|
||||||
int n = 0;
|
|
||||||
for (int shift = 32; shift != 0; shift >>= 1)
|
|
||||||
{
|
|
||||||
if ((x >> (64 - shift)) == 0)
|
|
||||||
{
|
|
||||||
n += shift;
|
|
||||||
x <<= shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// number of trailing zero bits of x (x != 0)
|
|
||||||
inline int count_trailing_zeros(std::uint64_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_ctzll(x);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
|
||||||
_BitScanForward64(&index, x);
|
|
||||||
return static_cast<int>(index);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
|
||||||
if (_BitScanForward(&index, static_cast<unsigned long>(x))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return static_cast<int>(index);
|
|
||||||
}
|
|
||||||
_BitScanForward(&index, static_cast<unsigned long>(x >> 32u)); // NOLINT(runtime/int)
|
|
||||||
return 32 + static_cast<int>(index);
|
|
||||||
#elif defined(__cpp_lib_bitops)
|
|
||||||
return std::countr_zero(x);
|
|
||||||
#else
|
|
||||||
int n = 0;
|
|
||||||
for (int shift = 32; shift != 0; shift >>= 1)
|
|
||||||
{
|
|
||||||
if ((x << (64 - shift)) == 0)
|
|
||||||
{
|
|
||||||
n += shift;
|
|
||||||
x >>= shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the object representation of @a from as a @a To (std::bit_cast in C++20)
|
|
||||||
template<typename To, typename From>
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE To bit_cast(const From& from) noexcept
|
|
||||||
{
|
|
||||||
static_assert(sizeof(To) == sizeof(From), "bit_cast needs types of the same size");
|
|
||||||
#if defined(__cpp_lib_bit_cast)
|
|
||||||
return std::bit_cast<To>(from);
|
|
||||||
#else
|
|
||||||
To to {};
|
|
||||||
std::memcpy(&to, &from, sizeof(To));
|
|
||||||
return to;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// x with its bytes in reverse order (std::byteswap in C++23); compilers lower
|
|
||||||
/// it to a single bswap/rev/movbe
|
|
||||||
inline std::uint16_t byteswap(std::uint16_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_bswap16(x);
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
return _byteswap_ushort(x);
|
|
||||||
#elif defined(__cpp_lib_byteswap)
|
|
||||||
return std::byteswap(x);
|
|
||||||
#else
|
|
||||||
return static_cast<std::uint16_t>((x >> 8u) | (x << 8u));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// x with its bytes in reverse order (std::byteswap in C++23)
|
|
||||||
inline std::uint32_t byteswap(std::uint32_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_bswap32(x);
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
return _byteswap_ulong(x);
|
|
||||||
#elif defined(__cpp_lib_byteswap)
|
|
||||||
return std::byteswap(x);
|
|
||||||
#else
|
|
||||||
return ((x & 0x000000FFu) << 24u) | ((x & 0x0000FF00u) << 8u)
|
|
||||||
| ((x & 0x00FF0000u) >> 8u) | ((x & 0xFF000000u) >> 24u);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// x with its bytes in reverse order (std::byteswap in C++23)
|
|
||||||
inline std::uint64_t byteswap(std::uint64_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_bswap64(x);
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
return _byteswap_uint64(x);
|
|
||||||
#elif defined(__cpp_lib_byteswap)
|
|
||||||
return std::byteswap(x);
|
|
||||||
#else
|
|
||||||
x = ((x & 0x00000000FFFFFFFFull) << 32u) | ((x & 0xFFFFFFFF00000000ull) >> 32u);
|
|
||||||
x = ((x & 0x0000FFFF0000FFFFull) << 16u) | ((x & 0xFFFF0000FFFF0000ull) >> 16u);
|
|
||||||
x = ((x & 0x00FF00FF00FF00FFull) << 8u) | ((x & 0xFF00FF00FF00FF00ull) >> 8u);
|
|
||||||
return x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the 128-bit product of two 64-bit numbers
|
|
||||||
struct uint128_parts
|
|
||||||
{
|
|
||||||
std::uint64_t low;
|
|
||||||
std::uint64_t high;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__SIZEOF_INT128__)
|
|
||||||
__extension__ using uint128 = unsigned __int128;
|
|
||||||
const uint128 r = static_cast<uint128>(a) * b;
|
|
||||||
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
|
|
||||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
|
||||||
std::uint64_t high = 0;
|
|
||||||
const std::uint64_t low = _umul128(a, b, &high);
|
|
||||||
return {low, high};
|
|
||||||
#elif defined(_MSC_VER) && defined(_M_ARM64)
|
|
||||||
return {a * b, __umulh(a, b)};
|
|
||||||
#else
|
|
||||||
const std::uint64_t a_lo = a & 0xFFFFFFFFu;
|
|
||||||
const std::uint64_t a_hi = a >> 32u;
|
|
||||||
const std::uint64_t b_lo = b & 0xFFFFFFFFu;
|
|
||||||
const std::uint64_t b_hi = b >> 32u;
|
|
||||||
const std::uint64_t lo_lo = a_lo * b_lo;
|
|
||||||
const std::uint64_t hi_lo = a_hi * b_lo;
|
|
||||||
const std::uint64_t lo_hi = a_lo * b_hi;
|
|
||||||
const std::uint64_t hi_hi = a_hi * b_hi;
|
|
||||||
const std::uint64_t cross = (lo_lo >> 32u) + (hi_lo & 0xFFFFFFFFu) + lo_hi;
|
|
||||||
return {(cross << 32u) | (lo_lo & 0xFFFFFFFFu), (hi_lo >> 32u) + (cross >> 32u) + hi_hi};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// eight bytes as a little-endian word (a single load on little-endian
|
|
||||||
/// targets; always inlined, as GCC otherwise calls it in the number loops)
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const unsigned char* b) noexcept
|
|
||||||
{
|
|
||||||
#if JSON_LITTLE_ENDIAN_TARGET
|
|
||||||
// the byte order already matches
|
|
||||||
std::uint64_t result = 0;
|
|
||||||
std::memcpy(&result, b, sizeof(result));
|
|
||||||
return result;
|
|
||||||
#else
|
|
||||||
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
|
||||||
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
|
||||||
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
|
||||||
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// eight bytes as a little-endian word
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const char* p) noexcept
|
|
||||||
{
|
|
||||||
return read_eight_bytes(reinterpret_cast<const unsigned char*>(p)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
|
||||||
|
|
||||||
// #include <nlohmann/detail/input/input_adapters.hpp>
|
// #include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
|
|
||||||
@@ -18517,11 +18519,13 @@ class binary_reader
|
|||||||
bool get_cbor_negative_integer()
|
bool get_cbor_negative_integer()
|
||||||
{
|
{
|
||||||
NumberType number{};
|
NumberType number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(number)))
|
return get_number(number) && emit_cbor_negative_integer(number);
|
||||||
{
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// pass the CBOR negative integer -1 - @a number to the SAX parser
|
||||||
|
template<typename NumberType>
|
||||||
|
bool emit_cbor_negative_integer(const NumberType number)
|
||||||
|
{
|
||||||
// the value is -1 - number, which fits into number_integer_t
|
// the value is -1 - number, which fits into number_integer_t
|
||||||
// whenever number does; the outer cast undoes the integral promotion
|
// whenever number does; the outer cast undoes the integral promotion
|
||||||
// for number_integer_t types narrower than int
|
// for number_integer_t types narrower than int
|
||||||
@@ -18746,7 +18750,7 @@ class binary_reader
|
|||||||
case 0x95:
|
case 0x95:
|
||||||
case 0x96:
|
case 0x96:
|
||||||
case 0x97:
|
case 0x97:
|
||||||
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_cbor_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
||||||
|
|
||||||
case 0x98: // array (one-byte uint8_t for n follows)
|
case 0x98: // array (one-byte uint8_t for n follows)
|
||||||
case 0x99: // array (two-byte uint16_t for n follow)
|
case 0x99: // array (two-byte uint16_t for n follow)
|
||||||
@@ -18755,7 +18759,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -18909,16 +18913,10 @@ class binary_reader
|
|||||||
return get_half_float(false);
|
return get_half_float(false);
|
||||||
|
|
||||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<float>();
|
||||||
float number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<double>();
|
||||||
double number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // anything else (0xFF is handled inside the other types)
|
default: // anything else (0xFF is handled inside the other types)
|
||||||
{
|
{
|
||||||
@@ -18927,6 +18925,16 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// read a single- or double-precision float (kept out of
|
||||||
|
/// parse_cbor_value, which is inlined into parse_cbor_internal)
|
||||||
|
template<typename NumberType>
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool get_cbor_float()
|
||||||
|
{
|
||||||
|
NumberType number{};
|
||||||
|
return get_number(number) && emit_float(number);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reports a nested indefinite-length CBOR string or byte array
|
@brief reports a nested indefinite-length CBOR string or byte array
|
||||||
@param[in] type_name name of the rejected string type
|
@param[in] type_name name of the rejected string type
|
||||||
@@ -19452,6 +19460,157 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read a big-endian number of type @a NumberType at @a data
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
NumberType load_big_endian(const unsigned char* data) const noexcept
|
||||||
|
{
|
||||||
|
NumberType result{};
|
||||||
|
std::memcpy(&result, data, sizeof(NumberType));
|
||||||
|
if (is_little_endian)
|
||||||
|
{
|
||||||
|
byte_swap(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the integers, floats, booleans, and nulls that follow in a
|
||||||
|
definite-length array, without the per-element bookkeeping of
|
||||||
|
@ref parse_cbor_internal
|
||||||
|
|
||||||
|
Stops at the end of the array, at any other kind of value, and when fewer
|
||||||
|
than 9 bytes (the longest of these values) are left in the input, leaving
|
||||||
|
those to @ref parse_cbor_value, which reads them the same way.
|
||||||
|
|
||||||
|
@param[in,out] remaining the number of elements of the array not yet read
|
||||||
|
@return whether the SAX parser accepted the values
|
||||||
|
*/
|
||||||
|
bool parse_cbor_scalar_run(std::size_t& remaining)
|
||||||
|
{
|
||||||
|
while (remaining != 0 && ia.bulk_remaining() >= 9)
|
||||||
|
{
|
||||||
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
|
const unsigned char head = data[0];
|
||||||
|
std::size_t size = 0;
|
||||||
|
bool accepted = true;
|
||||||
|
switch (head)
|
||||||
|
{
|
||||||
|
case 0x18:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_unsigned(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x19:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x38:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_cbor_negative_integer(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x39:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xF4:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(false);
|
||||||
|
break;
|
||||||
|
case 0xF5:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(true);
|
||||||
|
break;
|
||||||
|
case 0xF6:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->null();
|
||||||
|
break;
|
||||||
|
case 0xFA:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_float(load_big_endian<float>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xFB:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_float(load_big_endian<double>(data + 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (head <= 0x17)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_unsigned(static_cast<number_unsigned_t>(head));
|
||||||
|
}
|
||||||
|
else if (head >= 0x20 && head <= 0x37)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - head));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// any other value is read by parse_cbor_value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// as if read by get() and get_number(): current is the head byte
|
||||||
|
ia.bulk_skip(size);
|
||||||
|
chars_read += size;
|
||||||
|
current = head;
|
||||||
|
--remaining;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!accepted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief open a definite-length array and read the scalars it begins with
|
||||||
|
(see @ref parse_cbor_scalar_run)
|
||||||
|
|
||||||
|
Only the start of an array is tried, which covers arrays of numbers
|
||||||
|
(e.g. coordinates) but costs arrays of other values only one test, not one
|
||||||
|
per element.
|
||||||
|
|
||||||
|
@param[in] len the number of elements
|
||||||
|
@return whether the SAX parser accepted the array and the values read
|
||||||
|
*/
|
||||||
|
bool enter_cbor_array(const std::size_t len)
|
||||||
|
{
|
||||||
|
return enter_array(len) && (len == 0 || parse_cbor_array_start(std::integral_constant<bool, bulk_scan> {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read the scalars the array just opened begins with; out of line, so
|
||||||
|
/// that @ref parse_cbor_value stays small enough to be inlined into
|
||||||
|
/// @ref parse_cbor_internal
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool parse_cbor_array_start(std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
return parse_cbor_scalar_run(container_stack.back().remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input that is not contiguous: every value is read by parse_cbor_value
|
||||||
|
bool parse_cbor_array_start(std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a CBOR value and everything nested inside it
|
@brief read a CBOR value and everything nested inside it
|
||||||
|
|
||||||
@@ -19481,6 +19640,7 @@ class binary_reader
|
|||||||
// pop_back() below, which destroys the container_stack element
|
// pop_back() below, which destroys the container_stack element
|
||||||
// it would otherwise alias
|
// it would otherwise alias
|
||||||
const container_frame top = container_stack.back();
|
const container_frame top = container_stack.back();
|
||||||
|
const bool is_object = top.is_object;
|
||||||
bool at_end = false;
|
bool at_end = false;
|
||||||
|
|
||||||
if (top.remaining != npos)
|
if (top.remaining != npos)
|
||||||
@@ -19492,7 +19652,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
// claim the element about to be read
|
// claim the element about to be read
|
||||||
--container_stack.back().remaining;
|
--container_stack.back().remaining;
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
}
|
}
|
||||||
@@ -19505,7 +19665,7 @@ class binary_reader
|
|||||||
// Testing for it consumes a byte, which is the first byte
|
// Testing for it consumes a byte, which is the first byte
|
||||||
// of the next element when it is not one.
|
// of the next element when it is not one.
|
||||||
at_end = (get() == 0xFF);
|
at_end = (get() == 0xFF);
|
||||||
fetch = top.is_object;
|
fetch = is_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
@@ -19522,7 +19682,7 @@ class binary_reader
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||||
{
|
{
|
||||||
@@ -19747,7 +19907,7 @@ class binary_reader
|
|||||||
case 0x9D:
|
case 0x9D:
|
||||||
case 0x9E:
|
case 0x9E:
|
||||||
case 0x9F:
|
case 0x9F:
|
||||||
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
|
return enter_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
|
||||||
|
|
||||||
// fixstr
|
// fixstr
|
||||||
case 0xA0:
|
case 0xA0:
|
||||||
@@ -19878,13 +20038,13 @@ class binary_reader
|
|||||||
case 0xDC: // array 16
|
case 0xDC: // array 16
|
||||||
{
|
{
|
||||||
std::uint16_t len{};
|
std::uint16_t len{};
|
||||||
return get_number(len) && enter_array(static_cast<std::size_t>(len));
|
return get_number(len) && enter_msgpack_array(static_cast<std::size_t>(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDD: // array 32
|
case 0xDD: // array 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(len) && enter_array(conditional_static_cast<std::size_t>(len));
|
return get_number(len) && enter_msgpack_array(conditional_static_cast<std::size_t>(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDE: // map 16
|
case 0xDE: // map 16
|
||||||
@@ -20226,6 +20386,138 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the integers, floats, booleans, and nils that follow in a
|
||||||
|
MessagePack array, without the per-element bookkeeping of
|
||||||
|
@ref parse_msgpack_internal
|
||||||
|
|
||||||
|
Stops at the end of the array, at any other kind of value, and when fewer
|
||||||
|
than 9 bytes (the longest of these values) are left in the input, leaving
|
||||||
|
those to @ref parse_msgpack_value, which reads them the same way.
|
||||||
|
|
||||||
|
@param[in,out] remaining the number of elements of the array not yet read
|
||||||
|
@return whether the SAX parser accepted the values
|
||||||
|
*/
|
||||||
|
bool parse_msgpack_scalar_run(std::size_t& remaining)
|
||||||
|
{
|
||||||
|
while (remaining != 0 && ia.bulk_remaining() >= 9)
|
||||||
|
{
|
||||||
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
|
const unsigned char head = data[0];
|
||||||
|
std::size_t size = 0;
|
||||||
|
bool accepted = true;
|
||||||
|
switch (head)
|
||||||
|
{
|
||||||
|
case 0xC0: // nil
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->null();
|
||||||
|
break;
|
||||||
|
case 0xC2: // false
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(false);
|
||||||
|
break;
|
||||||
|
case 0xC3: // true
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(true);
|
||||||
|
break;
|
||||||
|
case 0xCA: // float 32
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_float(load_big_endian<float>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCB: // float 64
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_float(load_big_endian<double>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCC: // uint 8
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_unsigned(data[1]);
|
||||||
|
break;
|
||||||
|
case 0xCD: // uint 16
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCE: // uint 32
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xCF: // uint 64
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xD0: // int 8
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_signed(static_cast<std::int8_t>(data[1]));
|
||||||
|
break;
|
||||||
|
case 0xD1: // int 16
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_signed(load_big_endian<std::int16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xD2: // int 32
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_signed(load_big_endian<std::int32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xD3: // int 64
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_signed(load_big_endian<std::int64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (head <= 0x7F)
|
||||||
|
{
|
||||||
|
// positive fixint
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_unsigned(static_cast<number_unsigned_t>(head));
|
||||||
|
}
|
||||||
|
else if (head >= 0xE0)
|
||||||
|
{
|
||||||
|
// negative fixint
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_integer(static_cast<std::int8_t>(head));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// any other value is read by parse_msgpack_value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// as if read by get() and get_number(): current is the last byte
|
||||||
|
ia.bulk_skip(size);
|
||||||
|
chars_read += size;
|
||||||
|
current = data[size - 1];
|
||||||
|
--remaining;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!accepted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief open a MessagePack array and read the scalars it begins with (see
|
||||||
|
@ref parse_msgpack_scalar_run)
|
||||||
|
@param[in] len the number of elements
|
||||||
|
@return whether the SAX parser accepted the array and the values read
|
||||||
|
*/
|
||||||
|
bool enter_msgpack_array(const std::size_t len)
|
||||||
|
{
|
||||||
|
return enter_array(len) && (len == 0 || parse_msgpack_array_start(std::integral_constant<bool, bulk_scan> {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read the scalars the array just opened begins with; out of line, so
|
||||||
|
/// that @ref parse_msgpack_value stays small enough to be inlined
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool parse_msgpack_array_start(std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
return parse_msgpack_scalar_run(container_stack.back().remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input that is not contiguous: every value is read by parse_msgpack_value
|
||||||
|
bool parse_msgpack_array_start(std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a MessagePack value and everything nested inside it
|
@brief read a MessagePack value and everything nested inside it
|
||||||
|
|
||||||
@@ -21467,13 +21759,13 @@ class binary_reader
|
|||||||
return sax->null();
|
return sax->null();
|
||||||
|
|
||||||
case 0xFB:
|
case 0xFB:
|
||||||
return sax->number_float(static_cast<number_float_t>(-1.0), "");
|
return sax->number_float(static_cast<number_float_t>(-1.0), no_source);
|
||||||
|
|
||||||
case 0xFC:
|
case 0xFC:
|
||||||
return sax->number_float(static_cast<number_float_t>(0.0), "");
|
return sax->number_float(static_cast<number_float_t>(0.0), no_source);
|
||||||
|
|
||||||
case 0xFD:
|
case 0xFD:
|
||||||
return sax->number_float(static_cast<number_float_t>(1.0), "");
|
return sax->number_float(static_cast<number_float_t>(1.0), no_source);
|
||||||
|
|
||||||
case 0xFF: // empty string
|
case 0xFF: // empty string
|
||||||
{
|
{
|
||||||
@@ -21842,6 +22134,27 @@ class binary_reader
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a double with a single byteswap(): GCC does
|
||||||
|
/// not merge the byte-wise swap of the generic version into one
|
||||||
|
static void byte_swap(double& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(double) == sizeof(std::uint64_t), "double must have 64 bits");
|
||||||
|
std::uint64_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a float with a single byteswap()
|
||||||
|
static void byte_swap(float& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(float) == sizeof(std::uint32_t), "float must have 32 bits");
|
||||||
|
std::uint32_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
template<class NumberType>
|
template<class NumberType>
|
||||||
static void byte_swap(NumberType& number)
|
static void byte_swap(NumberType& number)
|
||||||
{
|
{
|
||||||
@@ -21977,7 +22290,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
||||||
}
|
}
|
||||||
return sax->number_float(result, "");
|
return sax->number_float(result, no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -22037,7 +22350,7 @@ class binary_reader
|
|||||||
}();
|
}();
|
||||||
return sax->number_float((half & 0x8000u) != 0
|
return sax->number_float((half & 0x8000u) != 0
|
||||||
? static_cast<number_float_t>(-val)
|
? static_cast<number_float_t>(-val)
|
||||||
: static_cast<number_float_t>(val), "");
|
: static_cast<number_float_t>(val), no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -22362,6 +22675,10 @@ class binary_reader
|
|||||||
/// the SAX parser
|
/// the SAX parser
|
||||||
json_sax_t* sax = nullptr;
|
json_sax_t* sax = nullptr;
|
||||||
|
|
||||||
|
/// the (empty) source text of every number_float event: binary formats
|
||||||
|
/// have none, and a string_t built for each float would cost more
|
||||||
|
const string_t no_source{};
|
||||||
|
|
||||||
/// the containers that have been opened and not closed yet; see @ref container_frame
|
/// the containers that have been opened and not closed yet; see @ref container_frame
|
||||||
std::vector<container_frame> container_stack{};
|
std::vector<container_frame> container_stack{};
|
||||||
|
|
||||||
|
|||||||
Generated
+37
-157
@@ -1311,8 +1311,7 @@ struct parse_failure
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
||||||
/// Store: false for accept(): the grammar, number, string and UTF-8 checks run as for parse(), but no node is written
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel>
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel, bool Store>
|
|
||||||
class builder
|
class builder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -1488,87 +1487,11 @@ class builder
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the string from p, the first backslash, control character, ill-formed UTF-8 byte, or the end, checked and not
|
|
||||||
/// decoded (accept() stores nothing, so the arena is not written): the checks, errors and error positions of
|
|
||||||
/// slow_string(), for everything up to the closing quote
|
|
||||||
NLOHMANN_VIEW_NOINLINE decoded check_string(const unsigned char* p)
|
|
||||||
{
|
|
||||||
const unsigned char* const end = m_end;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char c = *p;
|
|
||||||
if (c == '"')
|
|
||||||
{
|
|
||||||
++p;
|
|
||||||
return decoded{p, 0, 0};
|
|
||||||
}
|
|
||||||
if (c != '\\')
|
|
||||||
{
|
|
||||||
return failed(c < 0x20 ? error_code::string_control_character : error_code::string_utf8, p);
|
|
||||||
}
|
|
||||||
++p;
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char d = *p++;
|
|
||||||
if (d == 'u')
|
|
||||||
{
|
|
||||||
const int cp = hex4(p); // (moves p past the four digits)
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cp < 0))
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY((cp & 0xF800) == 0xD800)) // a surrogate
|
|
||||||
{
|
|
||||||
if (cp >= 0xDC00)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_low, p);
|
|
||||||
}
|
|
||||||
if (end - p < 2 || p[0] != '\\' || p[1] != 'u')
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
p += 2;
|
|
||||||
const int lo = hex4(p); // (moves p past the four digits)
|
|
||||||
if (lo < 0)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (lo < 0xDC00 || lo > 0xDFFF)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!(d == '"' || d == '\\' || d == '/' || d == 'b' || d == 'f' || d == 'n' || d == 'r' || d == 't'))
|
|
||||||
{
|
|
||||||
--p;
|
|
||||||
return failed(error_code::string_escape, p);
|
|
||||||
}
|
|
||||||
if (p != end && *p == '\\')
|
|
||||||
{
|
|
||||||
continue; // (escapes in a row: no run of text between them)
|
|
||||||
}
|
|
||||||
p = scan_string_run(p, end); // the text up to the next backslash, quote, control character, or ill-formed UTF-8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// s points to the start of the string, p to a backslash, a control
|
/// s points to the start of the string, p to a backslash, a control
|
||||||
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
||||||
/// the arena
|
/// the arena
|
||||||
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// (accept(): the string is checked, not decoded)
|
|
||||||
static_cast<void>(s);
|
|
||||||
return check_string(p);
|
|
||||||
}
|
|
||||||
// single-character escapes; 0: invalid (and 'u', handled separately)
|
// single-character escapes; 0: invalid (and 'u', handled separately)
|
||||||
static const std::array<char, 128> simple_escape =
|
static const std::array<char, 128> simple_escape =
|
||||||
{
|
{
|
||||||
@@ -1929,13 +1852,10 @@ run_found:
|
|||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
||||||
{
|
{
|
||||||
if (enabled(Store))
|
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
||||||
{
|
base = cold.m_doc.nodes;
|
||||||
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
out = base;
|
||||||
base = cold.m_doc.nodes;
|
cap = base + cold.m_doc.node_capacity;
|
||||||
out = base;
|
|
||||||
cap = base + cold.m_doc.node_capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
||||||
{
|
{
|
||||||
@@ -2119,10 +2039,7 @@ close_object:
|
|||||||
// an array pays nothing for this)
|
// an array pays nothing for this)
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
||||||
{
|
{
|
||||||
if (enabled(Store))
|
cold.note_large_object(cur_idx);
|
||||||
{
|
|
||||||
cold.note_large_object(cur_idx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close_container:
|
close_container:
|
||||||
@@ -2282,12 +2199,6 @@ indent_done:
|
|||||||
/// an integer's value); two stores on little-endian targets
|
/// an integer's value); two stores on little-endian targets
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// accept(): nothing is stored, so what computes the arguments is not run either. base and out stay
|
|
||||||
// null: their difference, which open() takes, is 0.
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
||||||
{
|
{
|
||||||
const auto n = static_cast<std::size_t>(out - base);
|
const auto n = static_cast<std::size_t>(out - base);
|
||||||
@@ -2346,12 +2257,9 @@ indent_done:
|
|||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
||||||
{
|
{
|
||||||
if (enabled(Store))
|
node& n = base[cur_idx];
|
||||||
{
|
n.len = cur_count;
|
||||||
node& n = base[cur_idx];
|
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||||
n.len = cur_count;
|
|
||||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
|
||||||
}
|
|
||||||
if (--depth != 0)
|
if (--depth != 0)
|
||||||
{
|
{
|
||||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||||
@@ -2526,44 +2434,34 @@ indent_done:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// run the builder with compile-time options
|
/// run the builder with compile-time options
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Store>
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd>
|
||||||
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
||||||
{
|
{
|
||||||
if (sentinel)
|
if (sentinel)
|
||||||
{
|
{
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
||||||
/// number_float_t of the document; validate_only: accept(), only the result is
|
/// number_float_t of the document
|
||||||
/// used
|
|
||||||
template<typename FloatType, bool NulIsEnd>
|
template<typename FloatType, bool NulIsEnd>
|
||||||
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, bool validate_only,
|
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, parse_failure& failure)
|
||||||
parse_failure& failure)
|
|
||||||
{
|
{
|
||||||
if (validate_only && !comments && !trailing_commas)
|
|
||||||
{
|
|
||||||
// accept() of plain JSON: nothing is stored. The other option sets
|
|
||||||
// could skip the stores just as well; they keep the builder that
|
|
||||||
// stores only so that there are two more instantiations of the parse
|
|
||||||
// loop, not eight.
|
|
||||||
return build_with<FloatType, false, false, NulIsEnd, false>(d, src, size, sentinel, failure);
|
|
||||||
}
|
|
||||||
if (comments)
|
if (comments)
|
||||||
{
|
{
|
||||||
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, true, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, true, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, false, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, false, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
@@ -10203,7 +10101,7 @@ class basic_json_document
|
|||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_range(first, last, error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.read_range(first, last, allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10218,7 +10116,7 @@ class basic_json_document
|
|||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
append_input(buf, std::forward<InputType>(input));
|
append_input(buf, std::forward<InputType>(input));
|
||||||
d.build_owned(std::move(buf), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.build_owned(std::move(buf), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10231,8 +10129,7 @@ class basic_json_document
|
|||||||
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_kind(std::forward<InputType>(input), on_error::validate, ignore_comments, ignore_trailing_commas,
|
d.read(std::forward<InputType>(input), false, ignore_comments, ignore_trailing_commas);
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
|
||||||
return !d.is_discarded();
|
return !d.is_discarded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10248,7 +10145,7 @@ class basic_json_document
|
|||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
read_kind(std::forward<InputType>(input), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas,
|
read_kind(std::forward<InputType>(input), allow_exceptions, ignore_comments, ignore_trailing_commas,
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10607,22 +10504,6 @@ class basic_json_document
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// what a failed parse does
|
|
||||||
enum class on_error : std::uint8_t
|
|
||||||
{
|
|
||||||
discard, ///< the document is discarded
|
|
||||||
raise, ///< parse_error is thrown
|
|
||||||
validate ///< accept(): the document is discarded, and no node is stored while parsing
|
|
||||||
};
|
|
||||||
|
|
||||||
static on_error error_mode(bool allow_exceptions) noexcept
|
|
||||||
{
|
|
||||||
return allow_exceptions ? on_error::raise : on_error::discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NulIsEnd of the builder: a null byte ends the input unless strict_nul_handling
|
|
||||||
static constexpr bool nul_is_end = !detail::abi_config::strict_nul_handling;
|
|
||||||
|
|
||||||
/// create the storage (sized for the input) on first use
|
/// create the storage (sized for the input) on first use
|
||||||
void ensure_data(const char* src, std::size_t size)
|
void ensure_data(const char* src, std::size_t size)
|
||||||
{
|
{
|
||||||
@@ -10634,7 +10515,7 @@ class basic_json_document
|
|||||||
|
|
||||||
/// parse a buffer the document takes ownership of (it replaces the
|
/// parse a buffer the document takes ownership of (it replaces the
|
||||||
/// previous owned copy)
|
/// previous owned copy)
|
||||||
void build_owned(std::string&& buf, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_owned(std::string&& buf, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
ensure_data(buf.data(), buf.size());
|
ensure_data(buf.data(), buf.size());
|
||||||
m_data->owned = std::move(buf);
|
m_data->owned = std::move(buf);
|
||||||
@@ -10644,7 +10525,7 @@ class basic_json_document
|
|||||||
/// parse a copy of the input; the copy is appended to the cleared owned
|
/// parse a copy of the input; the copy is appended to the cleared owned
|
||||||
/// buffer, so that its capacity is reused
|
/// buffer, so that its capacity is reused
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void build_copy(T&& input, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_copy(T&& input, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
if (!m_data)
|
if (!m_data)
|
||||||
{
|
{
|
||||||
@@ -10665,7 +10546,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
||||||
void build(const char* src, std::size_t size, on_error allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
void build(const char* src, std::size_t size, bool allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
||||||
{
|
{
|
||||||
ensure_data(src, size);
|
ensure_data(src, size);
|
||||||
document_data& d = *m_data;
|
document_data& d = *m_data;
|
||||||
@@ -10692,8 +10573,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ok = detail::view::build<typename BasicJsonType::number_float_t, nul_is_end>(d, src, size, comments, trailing_commas, sentinel,
|
ok = detail::view::build < typename BasicJsonType::number_float_t, !detail::abi_config::strict_nul_handling > (d, src, size, comments, trailing_commas, sentinel, failure);
|
||||||
allow_exceptions == on_error::validate, failure);
|
|
||||||
}
|
}
|
||||||
if (NLOHMANN_VIEW_LIKELY(ok))
|
if (NLOHMANN_VIEW_LIKELY(ok))
|
||||||
{
|
{
|
||||||
@@ -10706,7 +10586,7 @@ class basic_json_document
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::vector<std::uint32_t>().swap(d.large_objects);
|
std::vector<std::uint32_t>().swap(d.large_objects);
|
||||||
if (allow_exceptions == on_error::raise)
|
if (allow_exceptions)
|
||||||
{
|
{
|
||||||
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
||||||
}
|
}
|
||||||
@@ -10714,13 +10594,13 @@ class basic_json_document
|
|||||||
|
|
||||||
// --- input dispatch (see detail::view::input_kind) ---
|
// --- input dispatch (see detail::view::input_kind) ---
|
||||||
|
|
||||||
void read_kind(std::string&& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
void read_kind(std::string&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
||||||
{
|
{
|
||||||
build_owned(std::move(s), ae, c, tc);
|
build_owned(std::move(s), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -10734,7 +10614,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename Array>
|
template<typename Array>
|
||||||
void read_kind(Array& a, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
void read_kind(Array& a, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
||||||
{
|
{
|
||||||
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
||||||
const std::size_t n = std::extent<Array>::value;
|
const std::size_t n = std::extent<Array>::value;
|
||||||
@@ -10745,7 +10625,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
||||||
{
|
{
|
||||||
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
||||||
const auto size = static_cast<std::size_t>(s.size());
|
const auto size = static_cast<std::size_t>(s.size());
|
||||||
@@ -10754,13 +10634,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(s, ae, c, tc);
|
build_copy(s, ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -10772,13 +10652,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(T&& input, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
void read_kind(T&& input, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(std::forward<T>(input), ae, c, tc);
|
build_copy(std::forward<T>(input), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range(IteratorType first, IteratorType last, on_error ae, bool c, bool tc)
|
void read_range(IteratorType first, IteratorType last, bool ae, bool c, bool tc)
|
||||||
{
|
{
|
||||||
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
||||||
// borrow the range where the library's input adapter scans it as one
|
// borrow the range where the library's input adapter scans it as one
|
||||||
@@ -10789,14 +10669,14 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
||||||
{
|
{
|
||||||
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
||||||
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::false_type /*other*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::false_type /*other*/)
|
||||||
{
|
{
|
||||||
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ using nlohmann::json;
|
|||||||
#include "round_trip_corpus.hpp"
|
#include "round_trip_corpus.hpp"
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
#include "sax_countdown.hpp"
|
#include "sax_countdown.hpp"
|
||||||
|
#include "sax_event_loggers.hpp"
|
||||||
using utils::SaxCountdown;
|
using utils::SaxCountdown;
|
||||||
|
using utils::SaxEventLogger;
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("CBOR")
|
TEST_CASE("CBOR")
|
||||||
@@ -2446,6 +2448,78 @@ TEST_CASE("CBOR SAX parsing stops at every event")
|
|||||||
CHECK(count_events(std::vector<std::uint8_t>({0xBF, 0x61, 'a', 0x9F, 0x01, 0xFF, 0xFF})) == 6);
|
CHECK(count_events(std::vector<std::uint8_t>({0xBF, 0x61, 'a', 0x9F, 0x01, 0xFF, 0xFF})) == 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CBOR arrays of scalars from contiguous and non-contiguous input")
|
||||||
|
{
|
||||||
|
// contiguous input reads the integers, floats, booleans, and nulls an
|
||||||
|
// array begins with in one loop, other input value by value; both must
|
||||||
|
// give the same events, value, or error for the complete document and for
|
||||||
|
// every truncation of it, and stop at every event a SAX handler rejects
|
||||||
|
const std::vector<std::uint8_t> cbor =
|
||||||
|
{
|
||||||
|
0xA3, // map(3)
|
||||||
|
0x61, 'a', // "a"
|
||||||
|
0x98, 0x16, // array(22)
|
||||||
|
0x00, 0x17, 0x18, 0xFF, 0x19, 0x01, 0x00, // 0, 23, 255, 256
|
||||||
|
0x1A, 0x00, 0x01, 0x00, 0x00, // 65536
|
||||||
|
0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 18446744073709551615
|
||||||
|
0x20, 0x37, 0x38, 0xFF, 0x39, 0x01, 0x00, // -1, -24, -256, -257
|
||||||
|
0x3A, 0x00, 0x01, 0x00, 0x00, // -65537
|
||||||
|
0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // -18446744073709551616 (a float)
|
||||||
|
0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // -9223372036854775808
|
||||||
|
0xF4, 0xF5, 0xF6, // false, true, null
|
||||||
|
0xFA, 0x3F, 0xC0, 0x00, 0x00, // 1.5 (float32)
|
||||||
|
0xFB, 0xBF, 0xB9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, // -0.1
|
||||||
|
0xF9, 0x3C, 0x00, // 1.0 (float16, ends the loop)
|
||||||
|
0x01, 0x61, 'a', // 1, "a"
|
||||||
|
0xFB, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2.5
|
||||||
|
0x61, 'b', // "b"
|
||||||
|
0x83, // array(3)
|
||||||
|
0x82, 0xFB, 0x3F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [1.25,
|
||||||
|
0xFB, 0xC0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // -3.75]
|
||||||
|
0x82, 0xFB, 0x3F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [1.25,
|
||||||
|
0xFB, 0xC0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // -3.75]
|
||||||
|
0x80, // []
|
||||||
|
0x61, 'c', // "c"
|
||||||
|
0x84, 0xA0, 0x01, 0xC1, 0x02, 0x81, 0x03 // [{}, 1, tag(1) 2, [3]]
|
||||||
|
};
|
||||||
|
const auto tags = json::cbor_tag_handler_t::ignore;
|
||||||
|
|
||||||
|
const json j = json::from_cbor(cbor, true, true, tags);
|
||||||
|
CHECK(j["a"].size() == 22);
|
||||||
|
CHECK(j["a"][5] == 18446744073709551615u);
|
||||||
|
CHECK(j["a"][11].is_number_float());
|
||||||
|
CHECK(j["a"][11] == -18446744073709551616.0);
|
||||||
|
CHECK(j["a"][12] == (std::numeric_limits<std::int64_t>::min)());
|
||||||
|
CHECK(j["a"][17] == -0.1);
|
||||||
|
CHECK(j["b"] == json::parse("[[1.25, -3.75], [1.25, -3.75], []]"));
|
||||||
|
CHECK(j["c"] == json::parse("[{}, 1, 2, [3]]"));
|
||||||
|
|
||||||
|
for (std::size_t length = 0; length <= cbor.size(); ++length)
|
||||||
|
{
|
||||||
|
CAPTURE(length)
|
||||||
|
const std::vector<std::uint8_t> contiguous(cbor.begin(), cbor.begin() + static_cast<std::ptrdiff_t>(length));
|
||||||
|
const std::list<std::uint8_t> bytewise(contiguous.begin(), contiguous.end());
|
||||||
|
|
||||||
|
CHECK(json::from_cbor(contiguous, true, false, tags).dump() == json::from_cbor(bytewise.begin(), bytewise.end(), true, false, tags).dump());
|
||||||
|
|
||||||
|
SaxEventLogger from_contiguous;
|
||||||
|
SaxEventLogger from_bytewise;
|
||||||
|
CHECK(json::sax_parse(contiguous, &from_contiguous, json::input_format_t::cbor, true, false, false, tags)
|
||||||
|
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::cbor, true, false, false, tags));
|
||||||
|
CHECK(from_contiguous.events == from_bytewise.events);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int events = 0; events <= 60; ++events)
|
||||||
|
{
|
||||||
|
CAPTURE(events)
|
||||||
|
const std::list<std::uint8_t> bytewise(cbor.begin(), cbor.end());
|
||||||
|
SaxCountdown from_contiguous(events);
|
||||||
|
SaxCountdown from_bytewise(events);
|
||||||
|
CHECK(json::sax_parse(cbor, &from_contiguous, json::input_format_t::cbor, true, false, false, tags)
|
||||||
|
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::cbor, true, false, false, tags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
||||||
{
|
{
|
||||||
// Reading an indefinite-length string or byte array used to call itself
|
// Reading an indefinite-length string or byte array used to call itself
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ built build(const std::string& text, bool comments, bool trailing_commas, bool s
|
|||||||
r.copy.assign(text.begin(), text.end());
|
r.copy.assign(text.begin(), text.end());
|
||||||
src = r.copy.data();
|
src = r.copy.data();
|
||||||
}
|
}
|
||||||
constexpr bool nul_is_end = !nlohmann::detail::abi_config::strict_nul_handling;
|
r.ok = nlohmann::detail::view::build < FloatType, !nlohmann::detail::abi_config::strict_nul_handling > (*r.data, src, text.size(), comments, trailing_commas, sentinel, r.failure);
|
||||||
r.ok = nlohmann::detail::view::build<FloatType, nul_is_end>(*r.data, src, text.size(), comments, trailing_commas, sentinel, false, r.failure);
|
|
||||||
r.data->src = src;
|
r.data->src = src;
|
||||||
r.data->base[0] = src;
|
r.data->base[0] = src;
|
||||||
r.data->base[1] = r.data->arena.data();
|
r.data->base[1] = r.data->arena.data();
|
||||||
@@ -173,22 +172,6 @@ void check_string(const std::string& content)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// accept() stores no nodes: it must accept and reject what json::accept does, for all options and for inputs with
|
|
||||||
// and without a terminating NUL
|
|
||||||
void check_accept(const std::string& text)
|
|
||||||
{
|
|
||||||
CAPTURE(text)
|
|
||||||
const std::vector<char> bytes(text.begin(), text.end());
|
|
||||||
for (int options = 0; options < 4; ++options)
|
|
||||||
{
|
|
||||||
const bool comments = (options & 1) != 0;
|
|
||||||
const bool trailing_commas = (options & 2) != 0;
|
|
||||||
const bool accepted = json::accept(text, comments, trailing_commas);
|
|
||||||
CHECK(nlohmann::json_document::accept(text, comments, trailing_commas) == accepted);
|
|
||||||
CHECK(nlohmann::json_document::accept(bytes, comments, trailing_commas) == accepted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a small deterministic generator of documents
|
// a small deterministic generator of documents
|
||||||
struct generator
|
struct generator
|
||||||
{
|
{
|
||||||
@@ -440,133 +423,6 @@ TEST_CASE("json_view builder")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("accept() without an index")
|
|
||||||
{
|
|
||||||
for (const char* text :
|
|
||||||
{
|
|
||||||
"", " ", "\n", "null", "nul", "true", "[", "]", "[]", "{}", "[1,2,3]", "[1,2,3", "[1,2,3,]", "{\"a\":1,}", "{\"a\"}", "{\"a\":}",
|
|
||||||
"[1] x", "[1][2]", "\xEF\xBB\xBF[1]", "[\"a\\", "[\"\\u12\"]", "[\"\\ud800\"]", "[\"\x01\"]", "[\"\xff\"]", "[01]", "[1.]",
|
|
||||||
"[.5]", "[-]", "[1e]", "// c\n[1]", "/* c */ {\"a\" /* x */ : 1}", "/* c", "[1] // c", "1e400", "-1e400", "123456789012345678901234567890",
|
|
||||||
"{\"a\":1,\"a\":2}", "[[[]]]", "\"\\u00e4\\n\\ud83d\\ude00\""
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(text);
|
|
||||||
}
|
|
||||||
// a null byte inside the text, and after it
|
|
||||||
check_accept(std::string("[1,\0]", 5));
|
|
||||||
check_accept(std::string("[1]\0", 4));
|
|
||||||
check_accept(std::string("\0", 1));
|
|
||||||
|
|
||||||
// generated documents and damaged copies, as for the builder above
|
|
||||||
generator g;
|
|
||||||
for (int i = 0; i < 2000; ++i)
|
|
||||||
{
|
|
||||||
std::string text;
|
|
||||||
g.value(text, 0);
|
|
||||||
check_accept(text);
|
|
||||||
std::string damaged = text;
|
|
||||||
const auto at = static_cast<std::size_t>(g.r(static_cast<int>(damaged.size())));
|
|
||||||
static const char replacements[] = {'x', '"', '\\', ',', ':', ']', '}', '[', '{', '1', '-', '.', 'e', '\0', '\n', '/'}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
damaged[at] = replacements[g.r(16)];
|
|
||||||
check_accept(damaged);
|
|
||||||
check_accept(text.substr(0, at));
|
|
||||||
}
|
|
||||||
|
|
||||||
// nesting beyond the 64 levels that are kept on the builder's own stack, closed and not closed
|
|
||||||
for (const std::size_t depth :
|
|
||||||
{
|
|
||||||
63, 64, 65, 66, 200, 1000
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(std::string(depth, '[') + std::string(depth, ']'));
|
|
||||||
check_accept(std::string(depth, '[') + std::string(depth - 1, ']'));
|
|
||||||
std::string objects;
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
objects += "{\"k\":";
|
|
||||||
}
|
|
||||||
objects += "1" + std::string(depth, '}');
|
|
||||||
check_accept(objects);
|
|
||||||
check_accept(objects.substr(0, objects.size() - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// objects with enough members to get a hash index when they are indexed (and duplicate keys)
|
|
||||||
std::string large = "{";
|
|
||||||
for (int i = 0; i < 300; ++i)
|
|
||||||
{
|
|
||||||
large += (i == 0 ? "\"k" : ",\"k") + std::to_string(i % 200) + "\":" + std::to_string(i);
|
|
||||||
}
|
|
||||||
check_accept(large + "}");
|
|
||||||
check_accept(large);
|
|
||||||
check_accept("[" + large + "},{" + large.substr(1) + "}]");
|
|
||||||
|
|
||||||
// the overflow check uses the floating-point type of the document
|
|
||||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
|
||||||
for (const char* text :
|
|
||||||
{
|
|
||||||
"1e39", "-1e39", "3.4028235e38", "3.4028236e38", "[1.5e38, 3.5e38]", "{\"a\": 1e-50, \"b\": 1e39}", "340282356779733661637539395458142568448"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(text)
|
|
||||||
CHECK(nlohmann::basic_json_document<float_json>::accept(text) == float_json::accept(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
// strings are checked and not decoded: runs of text of every length from 0 to 40 between valid and invalid
|
|
||||||
// escapes, UTF-8 sequences and control characters, in values and in keys, cut near the end of the text
|
|
||||||
const std::array<const char*, 9> valid = {{"\\n", "\\\"", "\\\\", "\\/", "\\t", "\\u00e4", "\\u20ac", "\\ud83d\\ude00", "\\u0000"}};
|
|
||||||
const std::array<const char*, 12> invalid = {{"\\x", "\\ud800", "\\udc00", "\\u12G4", "\\u12", "\\ud83dx", "\\ud83d\\u0041", "\\ud83d\\ud83d", "\\", "\x01", "\x1f", "\x7f"}};
|
|
||||||
const std::array<const char*, 6> utf8 = {{"\xc3\xa4", "\xe2\x82\xac", "\xf0\x9f\x98\x80", "\xc3", "\xff", "\x80"}};
|
|
||||||
std::mt19937 rng(2029); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
|
||||||
for (std::size_t run = 0; run <= 40; ++run)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 30; ++i)
|
|
||||||
{
|
|
||||||
std::string content = std::string(rng() % 20, 'a');
|
|
||||||
const auto pieces = 1 + (rng() % 6);
|
|
||||||
for (unsigned k = 0; k < pieces; ++k)
|
|
||||||
{
|
|
||||||
content += valid[rng() % valid.size()];
|
|
||||||
content += std::string(k == 0 ? run : rng() % 41, static_cast<char>('b' + (rng() % 20)));
|
|
||||||
switch (rng() % 8)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
content += utf8[rng() % 3];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
content += utf8[rng() % utf8.size()];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
content += invalid[rng() % invalid.size()];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const std::string& text :
|
|
||||||
{
|
|
||||||
"[\"" + content + "\"]", "{\"" + content + "\":1}", "[\"" + content, "[\"" + content.substr(0, content.size() - (rng() % 4)) + "\"]"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("escapes in a row")
|
|
||||||
{
|
|
||||||
for (const char* content :
|
|
||||||
{
|
|
||||||
"\\n", "\\n\\t\\\\", "\\u00e9\\u00e9\\u4e2d", "\\u00e9\\u00e", "\\u00e9\\", "\\n\\q", "\\n\\ud83d\\ude00\\n",
|
|
||||||
"\\ud83d\\ude00\\ud83d", "\\n\\n\\n abc \\n\\n", "\\n\x01", "\\n\xC3\x28", "\\n\\"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(std::string("[\"") + content + "\"]");
|
|
||||||
check_accept(std::string("[\"") + content);
|
|
||||||
check_accept(std::string("{\"") + content + "\":1}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("test files")
|
SECTION("test files")
|
||||||
{
|
{
|
||||||
for (const char* name :
|
for (const char* name :
|
||||||
|
|||||||
@@ -27,12 +27,15 @@ using nlohmann::json;
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "make_test_data_available.hpp"
|
#include "make_test_data_available.hpp"
|
||||||
#include "round_trip_corpus.hpp"
|
#include "round_trip_corpus.hpp"
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
#include "sax_countdown.hpp"
|
#include "sax_countdown.hpp"
|
||||||
|
#include "sax_event_loggers.hpp"
|
||||||
using utils::SaxCountdown;
|
using utils::SaxCountdown;
|
||||||
|
using utils::SaxEventLogger;
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("MessagePack")
|
TEST_CASE("MessagePack")
|
||||||
@@ -2212,3 +2215,97 @@ TEST_CASE("MessagePack large strings and binaries (chunked reader)")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("MessagePack arrays of scalars from contiguous and non-contiguous input")
|
||||||
|
{
|
||||||
|
// contiguous input reads the integers, floats, booleans, and nils an
|
||||||
|
// array begins with in one loop, other input value by value; both must
|
||||||
|
// give the same events, value, or error for the complete document and for
|
||||||
|
// every truncation of it, and stop at every event a SAX handler rejects
|
||||||
|
const std::vector<std::uint8_t> msgpack =
|
||||||
|
{
|
||||||
|
0x83, // map(3)
|
||||||
|
0xA1, 'a', // "a"
|
||||||
|
0xDC, 0x00, 0x16, // array 16 (22)
|
||||||
|
0x00, 0x7F, 0xCC, 0xFF, 0xCD, 0x01, 0x00, // 0, 127, 255, 256
|
||||||
|
0xCE, 0x00, 0x01, 0x00, 0x00, // 65536
|
||||||
|
0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 18446744073709551615
|
||||||
|
0xFF, 0xE0, 0xD0, 0x80, 0xD1, 0xFF, 0x00, // -1, -32, -128, -256
|
||||||
|
0xD2, 0xFF, 0xFE, 0xFF, 0xFF, // -65537
|
||||||
|
0xD3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // -9223372036854775808
|
||||||
|
0xC2, 0xC3, 0xC0, // false, true, nil
|
||||||
|
0xCA, 0x3F, 0xC0, 0x00, 0x00, // 1.5 (float 32)
|
||||||
|
0xCB, 0xBF, 0xB9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, // -0.1
|
||||||
|
0xA1, 'x', // "x" (ends the loop)
|
||||||
|
0x01, // 1
|
||||||
|
0xCB, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2.5
|
||||||
|
0xD9, 0x01, 'y', // "y" (str 8)
|
||||||
|
0xC0, // nil
|
||||||
|
0xA1, 'b', // "b"
|
||||||
|
0x93, // fixarray(3)
|
||||||
|
0x92, 0xCB, 0x3F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [1.25,
|
||||||
|
0xCB, 0xC0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // -3.75]
|
||||||
|
0x92, 0xCB, 0x3F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [1.25,
|
||||||
|
0xCB, 0xC0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // -3.75]
|
||||||
|
0x90, // []
|
||||||
|
0xA1, 'c', // "c"
|
||||||
|
0x94, 0x80, 0x01, 0xC4, 0x01, 0x05, 0x91, 0x03 // [{}, 1, bin 8, [3]]
|
||||||
|
};
|
||||||
|
|
||||||
|
const json j = json::from_msgpack(msgpack);
|
||||||
|
CHECK(j["a"].size() == 22);
|
||||||
|
CHECK(j["a"][5] == 18446744073709551615u);
|
||||||
|
CHECK(j["a"][7] == -32);
|
||||||
|
CHECK(j["a"][11] == (std::numeric_limits<std::int64_t>::min)());
|
||||||
|
CHECK(j["a"][15] == 1.5);
|
||||||
|
CHECK(j["a"][16] == -0.1);
|
||||||
|
CHECK(j["a"][17] == "x");
|
||||||
|
CHECK(j["b"] == json::parse("[[1.25, -3.75], [1.25, -3.75], []]"));
|
||||||
|
CHECK(j["c"][2].is_binary());
|
||||||
|
|
||||||
|
for (std::size_t length = 0; length <= msgpack.size(); ++length)
|
||||||
|
{
|
||||||
|
CAPTURE(length)
|
||||||
|
const std::vector<std::uint8_t> contiguous(msgpack.begin(), msgpack.begin() + static_cast<std::ptrdiff_t>(length));
|
||||||
|
const std::list<std::uint8_t> bytewise(contiguous.begin(), contiguous.end());
|
||||||
|
|
||||||
|
CHECK(json::from_msgpack(contiguous, true, false).dump() == json::from_msgpack(bytewise.begin(), bytewise.end(), true, false).dump());
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
std::string contiguous_error;
|
||||||
|
std::string bytewise_error;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(json::from_msgpack(contiguous));
|
||||||
|
}
|
||||||
|
catch (const json::parse_error& e)
|
||||||
|
{
|
||||||
|
contiguous_error = e.what();
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(json::from_msgpack(bytewise.begin(), bytewise.end()));
|
||||||
|
}
|
||||||
|
catch (const json::parse_error& e)
|
||||||
|
{
|
||||||
|
bytewise_error = e.what();
|
||||||
|
}
|
||||||
|
CHECK(contiguous_error == bytewise_error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SaxEventLogger from_contiguous;
|
||||||
|
SaxEventLogger from_bytewise;
|
||||||
|
CHECK(json::sax_parse(contiguous, &from_contiguous, json::input_format_t::msgpack)
|
||||||
|
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::msgpack));
|
||||||
|
CHECK(from_contiguous.events == from_bytewise.events);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int events = 0; events <= 60; ++events)
|
||||||
|
{
|
||||||
|
CAPTURE(events)
|
||||||
|
const std::list<std::uint8_t> bytewise(msgpack.begin(), msgpack.end());
|
||||||
|
SaxCountdown from_contiguous(events);
|
||||||
|
SaxCountdown from_bytewise(events);
|
||||||
|
CHECK(json::sax_parse(msgpack, &from_contiguous, json::input_format_t::msgpack)
|
||||||
|
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::msgpack));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user