Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5cd9db7b3 | ||
|
|
cc31370a78 | ||
|
|
ce7498b54c | ||
|
|
e3e46253ed |
No files matched your search
@@ -22,6 +22,7 @@
|
||||
#include <bit> //byteswap
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||
#include <nlohmann/detail/input/json_sax.hpp>
|
||||
@@ -741,11 +742,13 @@ class binary_reader
|
||||
bool get_cbor_negative_integer()
|
||||
{
|
||||
NumberType number{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(number)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return get_number(number) && emit_cbor_negative_integer(number);
|
||||
}
|
||||
|
||||
/// 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
|
||||
// whenever number does; the outer cast undoes the integral promotion
|
||||
// for number_integer_t types narrower than int
|
||||
@@ -970,7 +973,7 @@ class binary_reader
|
||||
case 0x95:
|
||||
case 0x96:
|
||||
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 0x99: // array (two-byte uint16_t for n follow)
|
||||
@@ -979,7 +982,7 @@ class binary_reader
|
||||
{
|
||||
std::uint64_t len{};
|
||||
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)
|
||||
@@ -1133,16 +1136,10 @@ class binary_reader
|
||||
return get_half_float(false);
|
||||
|
||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||
{
|
||||
float number{};
|
||||
return get_number(number) && emit_float(number);
|
||||
}
|
||||
return get_cbor_float<float>();
|
||||
|
||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||
{
|
||||
double number{};
|
||||
return get_number(number) && emit_float(number);
|
||||
}
|
||||
return get_cbor_float<double>();
|
||||
|
||||
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
|
||||
@param[in] type_name name of the rejected string type
|
||||
@@ -1676,6 +1683,157 @@ class binary_reader
|
||||
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
|
||||
|
||||
@@ -1705,6 +1863,7 @@ class binary_reader
|
||||
// pop_back() below, which destroys the container_stack element
|
||||
// it would otherwise alias
|
||||
const container_frame top = container_stack.back();
|
||||
const bool is_object = top.is_object;
|
||||
bool at_end = false;
|
||||
|
||||
if (top.remaining != npos)
|
||||
@@ -1716,7 +1875,7 @@ class binary_reader
|
||||
{
|
||||
// claim the element about to be read
|
||||
--container_stack.back().remaining;
|
||||
if (top.is_object)
|
||||
if (is_object)
|
||||
{
|
||||
get();
|
||||
}
|
||||
@@ -1729,7 +1888,7 @@ class binary_reader
|
||||
// Testing for it consumes a byte, which is the first byte
|
||||
// of the next element when it is not one.
|
||||
at_end = (get() == 0xFF);
|
||||
fetch = top.is_object;
|
||||
fetch = is_object;
|
||||
}
|
||||
|
||||
if (at_end)
|
||||
@@ -1746,7 +1905,7 @@ class binary_reader
|
||||
continue;
|
||||
}
|
||||
|
||||
if (top.is_object)
|
||||
if (is_object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||
{
|
||||
@@ -1971,7 +2130,7 @@ class binary_reader
|
||||
case 0x9D:
|
||||
case 0x9E:
|
||||
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
|
||||
case 0xA0:
|
||||
@@ -2102,13 +2261,13 @@ class binary_reader
|
||||
case 0xDC: // array 16
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -3691,13 +3982,13 @@ class binary_reader
|
||||
return sax->null();
|
||||
|
||||
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:
|
||||
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:
|
||||
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
|
||||
{
|
||||
@@ -4066,6 +4357,27 @@ class binary_reader
|
||||
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>
|
||||
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 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
|
||||
? 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
|
||||
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
|
||||
std::vector<container_frame> container_stack{};
|
||||
|
||||
|
||||
Generated
+567
-250
@@ -8852,6 +8852,233 @@ NLOHMANN_JSON_NAMESPACE_END
|
||||
#include <bit> //byteswap
|
||||
#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/input/input_adapters.hpp>
|
||||
@@ -10316,231 +10543,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
||||
#include <vector> // vector
|
||||
|
||||
// #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>
|
||||
|
||||
@@ -18517,11 +18519,13 @@ class binary_reader
|
||||
bool get_cbor_negative_integer()
|
||||
{
|
||||
NumberType number{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(number)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return get_number(number) && emit_cbor_negative_integer(number);
|
||||
}
|
||||
|
||||
/// 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
|
||||
// whenever number does; the outer cast undoes the integral promotion
|
||||
// for number_integer_t types narrower than int
|
||||
@@ -18746,7 +18750,7 @@ class binary_reader
|
||||
case 0x95:
|
||||
case 0x96:
|
||||
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 0x99: // array (two-byte uint16_t for n follow)
|
||||
@@ -18755,7 +18759,7 @@ class binary_reader
|
||||
{
|
||||
std::uint64_t len{};
|
||||
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)
|
||||
@@ -18909,16 +18913,10 @@ class binary_reader
|
||||
return get_half_float(false);
|
||||
|
||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||
{
|
||||
float number{};
|
||||
return get_number(number) && emit_float(number);
|
||||
}
|
||||
return get_cbor_float<float>();
|
||||
|
||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||
{
|
||||
double number{};
|
||||
return get_number(number) && emit_float(number);
|
||||
}
|
||||
return get_cbor_float<double>();
|
||||
|
||||
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
|
||||
@param[in] type_name name of the rejected string type
|
||||
@@ -19452,6 +19460,157 @@ class binary_reader
|
||||
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
|
||||
|
||||
@@ -19481,6 +19640,7 @@ class binary_reader
|
||||
// pop_back() below, which destroys the container_stack element
|
||||
// it would otherwise alias
|
||||
const container_frame top = container_stack.back();
|
||||
const bool is_object = top.is_object;
|
||||
bool at_end = false;
|
||||
|
||||
if (top.remaining != npos)
|
||||
@@ -19492,7 +19652,7 @@ class binary_reader
|
||||
{
|
||||
// claim the element about to be read
|
||||
--container_stack.back().remaining;
|
||||
if (top.is_object)
|
||||
if (is_object)
|
||||
{
|
||||
get();
|
||||
}
|
||||
@@ -19505,7 +19665,7 @@ class binary_reader
|
||||
// Testing for it consumes a byte, which is the first byte
|
||||
// of the next element when it is not one.
|
||||
at_end = (get() == 0xFF);
|
||||
fetch = top.is_object;
|
||||
fetch = is_object;
|
||||
}
|
||||
|
||||
if (at_end)
|
||||
@@ -19522,7 +19682,7 @@ class binary_reader
|
||||
continue;
|
||||
}
|
||||
|
||||
if (top.is_object)
|
||||
if (is_object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||
{
|
||||
@@ -19747,7 +19907,7 @@ class binary_reader
|
||||
case 0x9D:
|
||||
case 0x9E:
|
||||
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
|
||||
case 0xA0:
|
||||
@@ -19878,13 +20038,13 @@ class binary_reader
|
||||
case 0xDC: // array 16
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -21467,13 +21759,13 @@ class binary_reader
|
||||
return sax->null();
|
||||
|
||||
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:
|
||||
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:
|
||||
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
|
||||
{
|
||||
@@ -21842,6 +22134,27 @@ class binary_reader
|
||||
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>
|
||||
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 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
|
||||
? 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
|
||||
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
|
||||
std::vector<container_frame> container_stack{};
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ using nlohmann::json;
|
||||
#include "round_trip_corpus.hpp"
|
||||
#include "test_utils.hpp"
|
||||
#include "sax_countdown.hpp"
|
||||
#include "sax_event_loggers.hpp"
|
||||
using utils::SaxCountdown;
|
||||
using utils::SaxEventLogger;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
// Reading an indefinite-length string or byte array used to call itself
|
||||
|
||||
@@ -27,12 +27,15 @@ using nlohmann::json;
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include "make_test_data_available.hpp"
|
||||
#include "round_trip_corpus.hpp"
|
||||
#include "test_utils.hpp"
|
||||
#include "sax_countdown.hpp"
|
||||
#include "sax_event_loggers.hpp"
|
||||
using utils::SaxCountdown;
|
||||
using utils::SaxEventLogger;
|
||||
|
||||
|
||||
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