Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12fc67313e | ||
|
|
43a2d24f6f |
No files matched your search
@@ -10,6 +10,13 @@ at its root, a [Natvis](https://learn.microsoft.com/en-us/visualstudio/debugger/
|
|||||||
file that gives `json`/`ordered_json` values a friendly, key/value debugger view instead of showing raw internal
|
file that gives `json`/`ordered_json` values a friendly, key/value debugger view instead of showing raw internal
|
||||||
fields, when debugging with the MSVC debug engine (`cppvsdbg`) in Visual Studio or VS Code.
|
fields, when debugging with the MSVC debug engine (`cppvsdbg`) in Visual Studio or VS Code.
|
||||||
|
|
||||||
|
The file also has visualizers for `json_document` and `json_view` (and their `ordered_json`/editable variants). They
|
||||||
|
show the value of the document or view, and its elements as an expandable list. Because a document is only a flat index
|
||||||
|
into the source text, the strings of the direct elements are shown, but the elements of nested arrays and objects are
|
||||||
|
shown as index nodes (their kind, size, and numbers; strings only by length). Arrays and objects that have been edited
|
||||||
|
in a `json_editable_document` are not expanded. The visualizers read the internal layout of the MSVC STL (`std::array`,
|
||||||
|
`std::unique_ptr`).
|
||||||
|
|
||||||
The checked-in visualizers cover namespace variants with zero, one, or two ABI tags. Configurations with three or more
|
The checked-in visualizers cover namespace variants with zero, one, or two ABI tags. Configurations with three or more
|
||||||
ABI tags are not covered and may show raw internal fields instead. To generate a Natvis file for such a configuration:
|
ABI tags are not covered and may show raw internal fields instead. To generate a Natvis file for such a configuration:
|
||||||
|
|
||||||
@@ -29,7 +36,7 @@ ABI tags are not covered and may show raw internal fields instead. To generate a
|
|||||||
|
|
||||||
4. Use the generated file instead of `nlohmann_json.natvis`.
|
4. Use the generated file instead of `nlohmann_json.natvis`.
|
||||||
|
|
||||||
`--abi-tags` can be repeated for several configurations, and `--all` generates every combination (about 60 MB for 12
|
`--abi-tags` can be repeated for several configurations, and `--all` generates every combination (about 60 MB for 11
|
||||||
ABI tags, doubling with each further tag). See
|
ABI tags, doubling with each further tag). See
|
||||||
[`tools/generate_natvis`](https://github.com/nlohmann/json/tree/develop/tools/generate_natvis) for all options.
|
[`tools/generate_natvis`](https://github.com/nlohmann/json/tree/develop/tools/generate_natvis) for all options.
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#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>
|
||||||
@@ -742,13 +741,11 @@ class binary_reader
|
|||||||
bool get_cbor_negative_integer()
|
bool get_cbor_negative_integer()
|
||||||
{
|
{
|
||||||
NumberType number{};
|
NumberType number{};
|
||||||
return get_number(number) && emit_cbor_negative_integer(number);
|
if (JSON_HEDLEY_UNLIKELY(!get_number(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
|
||||||
@@ -973,7 +970,7 @@ class binary_reader
|
|||||||
case 0x95:
|
case 0x95:
|
||||||
case 0x96:
|
case 0x96:
|
||||||
case 0x97:
|
case 0x97:
|
||||||
return enter_cbor_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_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)
|
||||||
@@ -982,7 +979,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_cbor_array(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -1136,10 +1133,16 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -1148,14 +1151,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read a single- or double-precision float
|
|
||||||
template<typename NumberType>
|
|
||||||
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
|
||||||
@@ -1681,183 +1676,6 @@ 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.
|
|
||||||
|
|
||||||
The caller has checked that an element remains, that at least 9 bytes are
|
|
||||||
left, and that the next byte begins a scalar.
|
|
||||||
|
|
||||||
@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)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
|
||||||
const unsigned char head = data[0];
|
|
||||||
std::size_t size = 0;
|
|
||||||
// false positive: every case assigns accepted before it is read
|
|
||||||
// @infer-ignore DEAD_STORE
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (remaining != 0 && ia.bulk_remaining() >= 9);
|
|
||||||
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 || !cbor_scalar_follows(std::integral_constant<bool, bulk_scan> {})
|
|
||||||
|| parse_cbor_array_start(std::integral_constant<bool, bulk_scan> {}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// whether a run of scalars can start at the next byte: at least 9 bytes
|
|
||||||
/// are left and the byte begins an integer, a float, a boolean, or null;
|
|
||||||
/// one test, so that an array of containers or strings makes no call
|
|
||||||
bool cbor_scalar_follows(std::true_type /*bulk*/) const
|
|
||||||
{
|
|
||||||
if (ia.bulk_remaining() < 9)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto head = static_cast<unsigned char>(*ia.bulk_data());
|
|
||||||
return head < 0x40 || head >= 0xF4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// input that is not contiguous: every value is read by parse_cbor_value
|
|
||||||
bool cbor_scalar_follows(std::false_type /*bulk*/) const noexcept
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
||||||
@@ -1887,7 +1705,6 @@ 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)
|
||||||
@@ -1899,7 +1716,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 (is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
}
|
}
|
||||||
@@ -1912,7 +1729,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 = is_object;
|
fetch = top.is_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
@@ -1929,7 +1746,7 @@ class binary_reader
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||||
{
|
{
|
||||||
@@ -3874,13 +3691,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), no_source);
|
return sax->number_float(static_cast<number_float_t>(-1.0), "");
|
||||||
|
|
||||||
case 0xFC:
|
case 0xFC:
|
||||||
return sax->number_float(static_cast<number_float_t>(0.0), no_source);
|
return sax->number_float(static_cast<number_float_t>(0.0), "");
|
||||||
|
|
||||||
case 0xFD:
|
case 0xFD:
|
||||||
return sax->number_float(static_cast<number_float_t>(1.0), no_source);
|
return sax->number_float(static_cast<number_float_t>(1.0), "");
|
||||||
|
|
||||||
case 0xFF: // empty string
|
case 0xFF: // empty string
|
||||||
{
|
{
|
||||||
@@ -4203,12 +4020,7 @@ class binary_reader
|
|||||||
`char_traits<char_type>::eof()` in that case.
|
`char_traits<char_type>::eof()` in that case.
|
||||||
|
|
||||||
@return character read from the input
|
@return character read from the input
|
||||||
|
|
||||||
@note always inlined: GCC otherwise stops inlining the stream adapter's
|
|
||||||
get_character() into parse_cbor_internal once that function grows
|
|
||||||
(+0.45 % instructions for CBOR from a std::istringstream with GCC 13)
|
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_ALWAYS_INLINE
|
|
||||||
char_int_type get()
|
char_int_type get()
|
||||||
{
|
{
|
||||||
++chars_read;
|
++chars_read;
|
||||||
@@ -4254,27 +4066,6 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -4410,7 +4201,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, no_source);
|
return sax->number_float(result, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4470,7 +4261,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), no_source);
|
: static_cast<number_float_t>(val), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4795,10 +4586,6 @@ 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
+11880
File diff suppressed because it is too large.
Load diff
Generated
+247
-461
@@ -8852,233 +8852,6 @@ 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>
|
||||||
@@ -10543,6 +10316,231 @@ 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>
|
||||||
|
|
||||||
@@ -18519,13 +18517,11 @@ class binary_reader
|
|||||||
bool get_cbor_negative_integer()
|
bool get_cbor_negative_integer()
|
||||||
{
|
{
|
||||||
NumberType number{};
|
NumberType number{};
|
||||||
return get_number(number) && emit_cbor_negative_integer(number);
|
if (JSON_HEDLEY_UNLIKELY(!get_number(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
|
||||||
@@ -18750,7 +18746,7 @@ class binary_reader
|
|||||||
case 0x95:
|
case 0x95:
|
||||||
case 0x96:
|
case 0x96:
|
||||||
case 0x97:
|
case 0x97:
|
||||||
return enter_cbor_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_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)
|
||||||
@@ -18759,7 +18755,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_cbor_array(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -18913,10 +18909,16 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -18925,14 +18927,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read a single- or double-precision float
|
|
||||||
template<typename NumberType>
|
|
||||||
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
|
||||||
@@ -19458,183 +19452,6 @@ 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.
|
|
||||||
|
|
||||||
The caller has checked that an element remains, that at least 9 bytes are
|
|
||||||
left, and that the next byte begins a scalar.
|
|
||||||
|
|
||||||
@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)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
|
||||||
const unsigned char head = data[0];
|
|
||||||
std::size_t size = 0;
|
|
||||||
// false positive: every case assigns accepted before it is read
|
|
||||||
// @infer-ignore DEAD_STORE
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (remaining != 0 && ia.bulk_remaining() >= 9);
|
|
||||||
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 || !cbor_scalar_follows(std::integral_constant<bool, bulk_scan> {})
|
|
||||||
|| parse_cbor_array_start(std::integral_constant<bool, bulk_scan> {}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// whether a run of scalars can start at the next byte: at least 9 bytes
|
|
||||||
/// are left and the byte begins an integer, a float, a boolean, or null;
|
|
||||||
/// one test, so that an array of containers or strings makes no call
|
|
||||||
bool cbor_scalar_follows(std::true_type /*bulk*/) const
|
|
||||||
{
|
|
||||||
if (ia.bulk_remaining() < 9)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto head = static_cast<unsigned char>(*ia.bulk_data());
|
|
||||||
return head < 0x40 || head >= 0xF4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// input that is not contiguous: every value is read by parse_cbor_value
|
|
||||||
bool cbor_scalar_follows(std::false_type /*bulk*/) const noexcept
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
||||||
@@ -19664,7 +19481,6 @@ 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)
|
||||||
@@ -19676,7 +19492,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 (is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
}
|
}
|
||||||
@@ -19689,7 +19505,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 = is_object;
|
fetch = top.is_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
@@ -19706,7 +19522,7 @@ class binary_reader
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||||
{
|
{
|
||||||
@@ -21651,13 +21467,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), no_source);
|
return sax->number_float(static_cast<number_float_t>(-1.0), "");
|
||||||
|
|
||||||
case 0xFC:
|
case 0xFC:
|
||||||
return sax->number_float(static_cast<number_float_t>(0.0), no_source);
|
return sax->number_float(static_cast<number_float_t>(0.0), "");
|
||||||
|
|
||||||
case 0xFD:
|
case 0xFD:
|
||||||
return sax->number_float(static_cast<number_float_t>(1.0), no_source);
|
return sax->number_float(static_cast<number_float_t>(1.0), "");
|
||||||
|
|
||||||
case 0xFF: // empty string
|
case 0xFF: // empty string
|
||||||
{
|
{
|
||||||
@@ -21980,12 +21796,7 @@ class binary_reader
|
|||||||
`char_traits<char_type>::eof()` in that case.
|
`char_traits<char_type>::eof()` in that case.
|
||||||
|
|
||||||
@return character read from the input
|
@return character read from the input
|
||||||
|
|
||||||
@note always inlined: GCC otherwise stops inlining the stream adapter's
|
|
||||||
get_character() into parse_cbor_internal once that function grows
|
|
||||||
(+0.45 % instructions for CBOR from a std::istringstream with GCC 13)
|
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_ALWAYS_INLINE
|
|
||||||
char_int_type get()
|
char_int_type get()
|
||||||
{
|
{
|
||||||
++chars_read;
|
++chars_read;
|
||||||
@@ -22031,27 +21842,6 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -22187,7 +21977,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, no_source);
|
return sax->number_float(result, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -22247,7 +22037,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), no_source);
|
: static_cast<number_float_t>(val), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -22572,10 +22362,6 @@ 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{};
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,7 @@ 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")
|
||||||
@@ -2448,78 +2446,6 @@ 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
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ The checked-in `nlohmann_json.natvis` covers the default namespace (no ABI tags)
|
|||||||
of ABI tags, with both versioned and unversioned namespace variants. Configurations that enable three or more ABI tags
|
of ABI tags, with both versioned and unversioned namespace variants. Configurations that enable three or more ABI tags
|
||||||
are not covered by this reduced file; use `--abi-tags` or `--all` (see below) to generate a file for them.
|
are not covered by this reduced file; use `--abi-tags` or `--all` (see below) to generate a file for them.
|
||||||
|
|
||||||
|
The visualizers of `basic_json_document` and `basic_json_view` live in the same inline namespaces as `basic_json`, so the
|
||||||
|
ABI tags apply to them in the same way, and the `view_mode` template argument is covered by a wildcard: it does not
|
||||||
|
multiply the namespaces. Each namespace gets three more types (`basic_json_view<*,*>`, `basic_json_document<*,*>`, and
|
||||||
|
`detail::view::node`), which roughly doubles the size of the file.
|
||||||
|
|
||||||
The ABI tag list and the library version are parsed from
|
The ABI tag list and the library version are parsed from
|
||||||
`include/nlohmann/detail/abi_macros.hpp`, so this script must be re-run (via
|
`include/nlohmann/detail/abi_macros.hpp`, so this script must be re-run (via
|
||||||
`make natvis`) whenever an `NLOHMANN_JSON_ABI_TAG_*` macro is added to that
|
`make natvis`) whenever an `NLOHMANN_JSON_ABI_TAG_*` macro is added to that
|
||||||
@@ -35,7 +40,7 @@ version other than the one in `abi_macros.hpp`.
|
|||||||
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `--list-tags` | Print every ABI tag (in namespace order) with the macro that enables it and the `NLOHMANN_JSON_ABI_TAG_*` identifier, then exit. |
|
| `--list-tags` | Print every ABI tag (in namespace order) with the macro that enables it and the `NLOHMANN_JSON_ABI_TAG_*` identifier, then exit. |
|
||||||
| `--abi-tags TAGS` | Add the namespaces of one configuration, given as a comma-separated list of ABI tags (`_diag,_dp,_ns`; the leading underscore is optional, and the order does not matter). Can be repeated for several configurations. Unknown tags are an error. |
|
| `--abi-tags TAGS` | Add the namespaces of one configuration, given as a comma-separated list of ABI tags (`_diag,_dp,_ns`; the leading underscore is optional, and the order does not matter). Can be repeated for several configurations. Unknown tags are an error. |
|
||||||
| `--all` | Generate the namespaces for every combination of ABI tags. The file size doubles with each tag (about 60 MB for 12 tags). Cannot be combined with `--abi-tags`. |
|
| `--all` | Generate the namespaces for every combination of ABI tags. The file size doubles with each tag (about 60 MB for 11 tags). Cannot be combined with `--abi-tags`. |
|
||||||
| `--output FILE` | Write the Natvis file to `FILE` instead of `nlohmann_json.natvis` in the repository root. |
|
| `--output FILE` | Write the Natvis file to `FILE` instead of `nlohmann_json.natvis` in the repository root. |
|
||||||
|
|
||||||
Each configuration gets both the versioned (`json_abi_diag_dp_ns_v3_12_0`) and the unversioned (`json_abi_diag_dp_ns`,
|
Each configuration gets both the versioned (`json_abi_diag_dp_ns_v3_12_0`) and the unversioned (`json_abi_diag_dp_ns`,
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ if __name__ == '__main__':
|
|||||||
'optional) of one configuration to add to the default namespaces; can be repeated')
|
'optional) of one configuration to add to the default namespaces; can be repeated')
|
||||||
selection.add_argument('--all', action='store_true',
|
selection.add_argument('--all', action='store_true',
|
||||||
help='Generate the namespaces for every combination of ABI tags; the file size doubles '
|
help='Generate the namespaces for every combination of ABI tags; the file size doubles '
|
||||||
'with each ABI tag (about 60 MB for 12 tags)')
|
'with each ABI tag (about 60 MB for 11 tags)')
|
||||||
parser.add_argument('--list-tags', action='store_true',
|
parser.add_argument('--list-tags', action='store_true',
|
||||||
help='List the ABI tags with the macros that enable them, and exit')
|
help='List the ABI tags with the macros that enable them, and exit')
|
||||||
parser.add_argument('--output', metavar='FILE',
|
parser.add_argument('--output', metavar='FILE',
|
||||||
|
|||||||
@@ -5,6 +5,39 @@
|
|||||||
<!-- * * * * * * * * AUTO-GENERATED FILE * * * * * * * * -->
|
<!-- * * * * * * * * AUTO-GENERATED FILE * * * * * * * * -->
|
||||||
|
|
||||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
{# The children of the container node `node` (a pointer expression) of the document `doc` (a document_data pointer
|
||||||
|
expression). A node is only an index into the source text: strings are read from `doc`, so the strings of the
|
||||||
|
direct children are readable here, but the children of a nested container are shown as nodes (see the node
|
||||||
|
visualizer below). Containers with edits (flag 8, "moved") are not expanded. The node kinds are the numbers of
|
||||||
|
node_kind: 1 object, 2 array, 3 string; the float token is shown only by the node visualizer. #}
|
||||||
|
{% macro children(node, doc=none) %}
|
||||||
|
<CustomListItems MaxItemsPerView="1000">
|
||||||
|
<Variable Name="nd" InitialValue="{{ node }}" />
|
||||||
|
<Variable Name="obj" InitialValue="nd != 0 && (int)nd->kind == 1" />
|
||||||
|
<Variable Name="n" InitialValue="(nd != 0 && ((int)nd->kind == 1 || (int)nd->kind == 2) && (nd->flags & 8) == 0) ? (int)nd->len : 0" />
|
||||||
|
<Variable Name="p" InitialValue="nd + 1" />
|
||||||
|
<Variable Name="i" InitialValue="0" />
|
||||||
|
<Size>obj ? 2 * n : n</Size>
|
||||||
|
<Loop Condition="i < n">
|
||||||
|
{% if doc %}
|
||||||
|
<Item Name="key" Condition="obj">{{ doc }}->base._Elems[p->flags & 3] + p->off,[p->len]s8</Item>
|
||||||
|
{% else %}
|
||||||
|
<Item Name="key" Condition="obj">*p</Item>
|
||||||
|
{% endif %}
|
||||||
|
<Exec Condition="obj">p = p + 1</Exec>
|
||||||
|
{% if doc %}
|
||||||
|
<Item Name="value" Condition="obj && (int)p->kind == 3">{{ doc }}->base._Elems[p->flags & 3] + p->off,[p->len]s8</Item>
|
||||||
|
{% endif %}
|
||||||
|
<Item Name="value" Condition="obj{% if doc %} && (int)p->kind != 3{% endif %}">*p</Item>
|
||||||
|
{% if doc %}
|
||||||
|
<Item Condition="!obj && (int)p->kind == 3">{{ doc }}->base._Elems[p->flags & 3] + p->off,[p->len]s8</Item>
|
||||||
|
{% endif %}
|
||||||
|
<Item Condition="!obj{% if doc %} && (int)p->kind != 3{% endif %}">*p</Item>
|
||||||
|
<Exec>p = p + (((int)p->kind == 1 || (int)p->kind == 2) ? p->next : 1)</Exec>
|
||||||
|
<Exec>i = i + 1</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
{%- endmacro %}
|
||||||
{% for ns in namespaces %}
|
{% for ns in namespaces %}
|
||||||
<!-- Namespace {{ ns }} -->
|
<!-- Namespace {{ ns }} -->
|
||||||
<Type Name="{{ ns }}::basic_json<*>">
|
<Type Name="{{ ns }}::basic_json<*>">
|
||||||
@@ -62,5 +95,44 @@
|
|||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- json_view / json_document (include/nlohmann/json_view.hpp). They live in the same inline namespaces as
|
||||||
|
basic_json, so the ABI tags need no extra entries; the view_mode is a template argument that the wildcard
|
||||||
|
covers. A view is a pointer to the document's data and a pointer to a node of its flat index; the
|
||||||
|
strings of the source are found through document_data::base (std::array of the MSVC STL: _Elems). -->
|
||||||
|
<Type Name="{{ ns }}::detail::view::node">
|
||||||
|
<DisplayString Condition="(int)kind == 0">null</DisplayString>
|
||||||
|
<DisplayString Condition="(int)kind == 1">object, size={len}</DisplayString>
|
||||||
|
<DisplayString Condition="(int)kind == 2">array, size={len}</DisplayString>
|
||||||
|
<DisplayString Condition="(int)kind == 3">string, length={len}</DisplayString>
|
||||||
|
<DisplayString Condition="(int)kind == 4">boolean: {(flags & 4) != 0}</DisplayString>
|
||||||
|
<DisplayString Condition="(int)kind == 5">int64: {(__int64)((unsigned __int64)len | ((unsigned __int64)next << 32))}</DisplayString>
|
||||||
|
<DisplayString Condition="(int)kind == 6">uint64: {(unsigned __int64)len | ((unsigned __int64)next << 32)}</DisplayString>
|
||||||
|
<DisplayString Condition="(int)kind == 7">float, length={len}</DisplayString>
|
||||||
|
<DisplayString>link</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
{{ children('this') }}
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="{{ ns }}::basic_json_view<*,*>">
|
||||||
|
<DisplayString Condition="m_node == 0">discarded</DisplayString>
|
||||||
|
<DisplayString Condition="(int)m_node->kind == 3">{m_doc->base._Elems[m_node->flags & 3] + m_node->off,[m_node->len]s8}</DisplayString>
|
||||||
|
<DisplayString Condition="(int)m_node->kind == 7">{m_doc->base._Elems[m_node->flags & 3] + m_node->off,[m_node->len]s8b}</DisplayString>
|
||||||
|
<DisplayString>{*m_node}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
{{ children('m_node', 'm_doc') }}
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="{{ ns }}::basic_json_document<*,*>">
|
||||||
|
<DisplayString Condition="m_data._Mypair._Myval2 == 0 || m_data._Mypair._Myval2->discarded">discarded</DisplayString>
|
||||||
|
<DisplayString>{*m_data._Mypair._Myval2->nodes}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[source]" Condition="m_data._Mypair._Myval2 != 0">m_data._Mypair._Myval2->src,[m_data._Mypair._Myval2->size]s8b</Item>
|
||||||
|
<Item Name="[nodes]" Condition="m_data._Mypair._Myval2 != 0">m_data._Mypair._Myval2->node_count</Item>
|
||||||
|
{{ children('m_data._Mypair._Myval2->nodes', 'm_data._Mypair._Myval2') }}
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</AutoVisualizer>
|
</AutoVisualizer>
|
||||||
Reference in new issue
Block a user