mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 19:50:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0b82e5af7 | ||
|
|
6a073dbae4 | ||
|
|
fdcc569eee | ||
|
|
2610d176ef | ||
|
|
c9a2143c78 | ||
|
|
a8b078ea37 | ||
|
|
391bc271b2 | ||
|
|
afef548ea1 | ||
|
|
70380a8de6 | ||
|
|
1841b4f671 | ||
|
|
edd18d071e | ||
|
|
35a3b0f3de | ||
|
|
e072480872 | ||
|
|
a3a94bb7eb | ||
|
|
0ffe9ab4a8 | ||
|
|
e158b080bd |
@@ -18,6 +18,10 @@ Deserializes an input stream to a JSON value.
|
||||
|
||||
the stream `i`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: if an exception is thrown, there are no changes in `j`.
|
||||
|
||||
## Exceptions
|
||||
|
||||
- Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or if
|
||||
@@ -125,3 +129,5 @@ being read.
|
||||
the stream; planned to become the default in version 4.0.0.
|
||||
- Fixed a null pointer dereference for an `std::istream` without a stream buffer (now throws `parse_error.101`), and a
|
||||
crash (`std::terminate`) when `i` has `eofbit` in its exception mask, in version 3.13.0.
|
||||
- Changed to the strong exception safety guarantee in version 3.13.0: `j` is no longer left with a partially parsed
|
||||
value if parsing throws.
|
||||
|
||||
@@ -389,6 +389,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
| Functionality | Additional requirement |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`diff`](../../api/basic_json/diff.md), [`items`](../../api/basic_json/items.md), [`std::hash`](../../api/basic_json/std_hash.md) | conversion of a `#!cpp std::size_t` to `StringType`: either assignability from the result of `#!cpp std::to_string`, or an ADL overload `#!cpp void int_to_string(StringType&, std::size_t)` |
|
||||
| [`operator/(std::size_t)`](../../api/json_pointer/operator_slash.md) | the same conversion of a `#!cpp std::size_t` to `StringType` as `diff`, `items`, and `std::hash` above |
|
||||
| [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) | additionally a specialization of `#!cpp std::hash<StringType>` |
|
||||
| [`to_bson`](../../api/basic_json/to_bson.md) | `find(value_type)` and `npos` |
|
||||
| [`parse`](../../api/basic_json/parse.md) from a `string_t` | the input adapters must accept it; otherwise pass a character range |
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // generate_n
|
||||
#include <array> // array
|
||||
#include <cmath> // ldexp
|
||||
#include <cstddef> // size_t
|
||||
@@ -123,7 +122,26 @@ class binary_reader
|
||||
~binary_reader() = default;
|
||||
|
||||
/*!
|
||||
@param[in] format the binary format to parse
|
||||
@brief parse in the format the constructor was given
|
||||
|
||||
@param[in] sax_ a SAX event processor
|
||||
@param[in] strict whether to expect the input to be consumed completed
|
||||
@param[in] tag_handler how to treat CBOR tags
|
||||
|
||||
@return whether parsing was successful
|
||||
*/
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
bool sax_parse(json_sax_t* sax_,
|
||||
const bool strict = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
{
|
||||
return sax_parse(input_format, sax_, strict, tag_handler);
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[in] format the binary format to parse; must equal the format the
|
||||
constructor was given, since that format is what
|
||||
every reader function below actually dispatches on
|
||||
@param[in] sax_ a SAX event processor
|
||||
@param[in] strict whether to expect the input to be consumed completed
|
||||
@param[in] tag_handler how to treat CBOR tags
|
||||
@@ -136,6 +154,7 @@ class binary_reader
|
||||
const bool strict = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
{
|
||||
JSON_ASSERT(format == input_format);
|
||||
sax = sax_;
|
||||
container_stack.clear();
|
||||
bon8_pushback_size = 0;
|
||||
@@ -148,7 +167,7 @@ class binary_reader
|
||||
break;
|
||||
|
||||
case input_format_t::cbor:
|
||||
result = parse_cbor_internal(true, tag_handler);
|
||||
result = parse_cbor_internal(tag_handler);
|
||||
break;
|
||||
|
||||
case input_format_t::msgpack:
|
||||
@@ -271,6 +290,22 @@ class binary_reader
|
||||
return enter_container(/*is_object*/true, len, type_marker);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief close the innermost open array or object
|
||||
|
||||
Pops the container opened by the matching @ref enter_container call and
|
||||
emits the SAX end event. Every format-specific driver otherwise repeated
|
||||
the same pop-then-dispatch sequence at its own close site.
|
||||
|
||||
@return whether the SAX parser accepted the end event
|
||||
*/
|
||||
bool leave_container()
|
||||
{
|
||||
const bool is_object = container_stack.back().is_object;
|
||||
container_stack.pop_back();
|
||||
return is_object ? sax->end_object() : sax->end_array();
|
||||
}
|
||||
|
||||
//////////
|
||||
// BSON //
|
||||
//////////
|
||||
@@ -355,8 +390,8 @@ class binary_reader
|
||||
if (element_type == 0) // end of the innermost document
|
||||
{
|
||||
// a copy, not a reference: it must stay valid across the
|
||||
// pop_back() below, which destroys the container_stack
|
||||
// element it would otherwise alias
|
||||
// pop_back() inside leave_container() below, which destroys
|
||||
// the container_stack element it would otherwise alias
|
||||
const container_frame top = container_stack.back();
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!check_bson_document_size(top.start_position, top.declared_size)))
|
||||
@@ -364,8 +399,7 @@ class binary_reader
|
||||
return false;
|
||||
}
|
||||
|
||||
container_stack.pop_back();
|
||||
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -860,29 +894,13 @@ class binary_reader
|
||||
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)
|
||||
{
|
||||
std::uint8_t len{};
|
||||
return get_number(input_format_t::cbor, len) && enter_array(static_cast<std::size_t>(len));
|
||||
}
|
||||
|
||||
case 0x99: // array (two-byte uint16_t for n follow)
|
||||
{
|
||||
std::uint16_t len{};
|
||||
return get_number(input_format_t::cbor, len) && enter_array(static_cast<std::size_t>(len));
|
||||
}
|
||||
|
||||
case 0x9A: // array (four-byte uint32_t for n follow)
|
||||
{
|
||||
std::uint32_t len{};
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
||||
}
|
||||
|
||||
case 0x9B: // array (eight-byte uint64_t for n follow)
|
||||
{
|
||||
std::uint64_t len{};
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
||||
}
|
||||
|
||||
case 0x9F: // array (indefinite length)
|
||||
@@ -916,35 +934,19 @@ class binary_reader
|
||||
return enter_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
||||
|
||||
case 0xB8: // map (one-byte uint8_t for n follows)
|
||||
{
|
||||
std::uint8_t len{};
|
||||
return get_number(input_format_t::cbor, len) && enter_object(static_cast<std::size_t>(len));
|
||||
}
|
||||
|
||||
case 0xB9: // map (two-byte uint16_t for n follow)
|
||||
{
|
||||
std::uint16_t len{};
|
||||
return get_number(input_format_t::cbor, len) && enter_object(static_cast<std::size_t>(len));
|
||||
}
|
||||
|
||||
case 0xBA: // map (four-byte uint32_t for n follow)
|
||||
{
|
||||
std::uint32_t len{};
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
||||
}
|
||||
|
||||
case 0xBB: // map (eight-byte uint64_t for n follow)
|
||||
{
|
||||
std::uint64_t len{};
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
||||
}
|
||||
|
||||
case 0xBF: // map (indefinite length)
|
||||
return enter_object(detail::unknown_size());
|
||||
|
||||
case 0xC0: // tagged item
|
||||
case 0xC0: // tagged item (tag value 0-23, in the head itself)
|
||||
case 0xC1:
|
||||
case 0xC2:
|
||||
case 0xC3:
|
||||
@@ -968,6 +970,22 @@ class binary_reader
|
||||
case 0xD5:
|
||||
case 0xD6:
|
||||
case 0xD7:
|
||||
{
|
||||
if (tag_handler == cbor_tag_handler_t::error)
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||
exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
|
||||
}
|
||||
|
||||
// ignore and store: the tag value is already in the head, so
|
||||
// there is nothing left to read here; the tagged value that
|
||||
// follows is read by the loop in parse_cbor_internal() rather
|
||||
// than by recursing here
|
||||
tag_pending = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0xD8: // tagged item (1 byte follows)
|
||||
case 0xD9: // tagged item (2 bytes follow)
|
||||
case 0xDA: // tagged item (4 bytes follow)
|
||||
@@ -984,47 +1002,11 @@ class binary_reader
|
||||
|
||||
case cbor_tag_handler_t::ignore:
|
||||
{
|
||||
// ignore binary subtype
|
||||
switch (current)
|
||||
// ignore the tag's binary subtype argument
|
||||
std::uint64_t subtype_to_ignore{};
|
||||
if (!get_cbor_argument(subtype_to_ignore))
|
||||
{
|
||||
case 0xD8:
|
||||
{
|
||||
std::uint8_t subtype_to_ignore{};
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xD9:
|
||||
{
|
||||
std::uint16_t subtype_to_ignore{};
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xDA:
|
||||
{
|
||||
std::uint32_t subtype_to_ignore{};
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xDB:
|
||||
{
|
||||
std::uint64_t subtype_to_ignore{};
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
// the tagged value follows; it is read by the loop in
|
||||
// parse_cbor_internal() rather than by recursing here
|
||||
@@ -1034,57 +1016,15 @@ class binary_reader
|
||||
|
||||
case cbor_tag_handler_t::store:
|
||||
{
|
||||
binary_t b;
|
||||
// use binary subtype and store in a binary container
|
||||
switch (current)
|
||||
std::uint64_t subtype{};
|
||||
if (!get_cbor_argument(subtype))
|
||||
{
|
||||
case 0xD8:
|
||||
{
|
||||
std::uint8_t subtype{};
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
case 0xD9:
|
||||
{
|
||||
std::uint16_t subtype{};
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
case 0xDA:
|
||||
{
|
||||
std::uint32_t subtype{};
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
case 0xDB:
|
||||
{
|
||||
std::uint64_t subtype{};
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// as above, the tagged value is read by the caller
|
||||
tag_pending = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
binary_t b;
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
|
||||
get();
|
||||
// a byte string (the heads accepted by get_cbor_binary) keeps the tag as subtype
|
||||
if ((current >= 0x40 && current <= 0x5B) || current == 0x5F)
|
||||
@@ -1115,52 +1055,7 @@ class binary_reader
|
||||
return sax->null();
|
||||
|
||||
case 0xF9: // Half-Precision Float (two-byte IEEE 754)
|
||||
{
|
||||
const auto byte1_raw = get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto byte2_raw = get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
||||
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
||||
|
||||
// Code from RFC 8949, Appendix D, Figure 3:
|
||||
// As half-precision floating-point numbers were only added
|
||||
// to IEEE 754 in 2008, today's programming platforms often
|
||||
// still only have limited support for them. It is very
|
||||
// easy to include at least decoding support for them even
|
||||
// without such support. An example of a small decoder for
|
||||
// half-precision floating-point numbers in the C language
|
||||
// is shown in Fig. 3.
|
||||
const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);
|
||||
const double val = [&half]
|
||||
{
|
||||
const int exp = (half >> 10u) & 0x1Fu;
|
||||
const unsigned int mant = half & 0x3FFu;
|
||||
JSON_ASSERT(exp <= 31);
|
||||
JSON_ASSERT(mant <= 1023);
|
||||
switch (exp)
|
||||
{
|
||||
case 0:
|
||||
return std::ldexp(mant, -24);
|
||||
case 31:
|
||||
return (mant == 0)
|
||||
? std::numeric_limits<double>::infinity()
|
||||
: std::numeric_limits<double>::quiet_NaN();
|
||||
default:
|
||||
return std::ldexp(mant + 1024, exp - 25);
|
||||
}
|
||||
}();
|
||||
return sax->number_float((half & 0x8000u) != 0
|
||||
? static_cast<number_float_t>(-val)
|
||||
: static_cast<number_float_t>(val), "");
|
||||
}
|
||||
return get_half_float(input_format_t::cbor, false);
|
||||
|
||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||
{
|
||||
@@ -1539,6 +1434,73 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read a CBOR argument (additional information 24-27) of the width
|
||||
@ref current announces
|
||||
|
||||
The lower 5 bits of @a current (0x18-0x1B) select a 1/2/4/8-byte
|
||||
big-endian unsigned integer that follows the head byte; this is shared by
|
||||
every major type that uses this encoding (unsigned/negative integers,
|
||||
strings, arrays, maps, tags). Reading always goes through @ref get_number,
|
||||
so EOF is reported the same way as before this helper existed.
|
||||
|
||||
@param[out] value the decoded argument
|
||||
@return whether reading succeeded
|
||||
*/
|
||||
bool get_cbor_argument(std::uint64_t& value)
|
||||
{
|
||||
switch (current & 0x1F)
|
||||
{
|
||||
case 0x18: // 1 byte
|
||||
{
|
||||
std::uint8_t n{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
value = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x19: // 2 bytes
|
||||
{
|
||||
std::uint16_t n{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
value = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x1A: // 4 bytes
|
||||
{
|
||||
std::uint32_t n{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
value = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x1B: // 8 bytes
|
||||
{
|
||||
std::uint64_t n{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
value = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
return false; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief narrow a definite CBOR array/map length to std::size_t
|
||||
|
||||
@@ -1571,19 +1533,15 @@ class binary_reader
|
||||
enclosing container after each element, so that the nesting depth of the
|
||||
input costs heap rather than native stack (see #5104).
|
||||
|
||||
@param[in] get_char whether a new character should be retrieved from the
|
||||
input (true) or whether the last read character
|
||||
@a current should be considered instead
|
||||
@param[in] tag_handler how CBOR tags should be treated
|
||||
|
||||
@return whether reading the value succeeded
|
||||
*/
|
||||
bool parse_cbor_internal(const bool get_char,
|
||||
const cbor_tag_handler_t tag_handler)
|
||||
bool parse_cbor_internal(const cbor_tag_handler_t tag_handler)
|
||||
{
|
||||
// whether the next value starts at a fresh byte or at the one already
|
||||
// read into `current`
|
||||
bool fetch = get_char;
|
||||
bool fetch = true;
|
||||
|
||||
// the key currently being read; hoisted out of the loop so that its
|
||||
// capacity is reused across elements and across nesting levels
|
||||
@@ -1626,8 +1584,7 @@ class binary_reader
|
||||
|
||||
if (at_end)
|
||||
{
|
||||
container_stack.pop_back();
|
||||
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1676,9 +1633,6 @@ class binary_reader
|
||||
// MsgPack //
|
||||
/////////////
|
||||
|
||||
/*!
|
||||
@return whether a valid MessagePack value was passed to the SAX parser
|
||||
*/
|
||||
/*!
|
||||
@brief read one MessagePack value
|
||||
|
||||
@@ -2377,8 +2331,7 @@ class binary_reader
|
||||
|
||||
if (container_stack.back().remaining == 0)
|
||||
{
|
||||
container_stack.pop_back();
|
||||
if (JSON_HEDLEY_UNLIKELY(is_object ? !sax->end_object() : !sax->end_array()))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -2423,20 +2376,16 @@ class binary_reader
|
||||
////////////
|
||||
|
||||
/*!
|
||||
@param[in] get_char whether a new character should be retrieved from the
|
||||
input (true, default) or whether the last read
|
||||
character should be considered instead
|
||||
|
||||
@return whether a valid UBJSON value was passed to the SAX parser
|
||||
*/
|
||||
bool parse_ubjson_internal(const bool get_char = true)
|
||||
bool parse_ubjson_internal()
|
||||
{
|
||||
// the key currently being read; hoisted out of the loop so that its
|
||||
// capacity is reused across elements and across nesting levels
|
||||
string_t key;
|
||||
|
||||
// the type marker of the value to read next
|
||||
char_int_type prefix = get_char ? get_ignore_noop() : current;
|
||||
char_int_type prefix = get_ignore_noop();
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -2511,8 +2460,7 @@ class binary_reader
|
||||
break;
|
||||
}
|
||||
|
||||
container_stack.pop_back();
|
||||
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -2720,6 +2668,42 @@ class binary_reader
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
||||
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
||||
|
||||
Every signed count marker rejects a negative value the same way (error
|
||||
113); the value_in_range_of check additionally needed for 'L' is only
|
||||
ever live when @a SignedType is std::int64_t on a target where
|
||||
std::size_t is narrower (e.g. 32-bit), since 'i'/'I'/'l' can never exceed
|
||||
std::size_t there.
|
||||
|
||||
@tparam SignedType std::int8_t, std::int16_t, std::int32_t or std::int64_t
|
||||
@param[out] result the count narrowed to std::size_t
|
||||
@return whether reading and validating succeeded
|
||||
*/
|
||||
template<typename SignedType>
|
||||
bool get_ubjson_signed_count(std::size_t& result)
|
||||
{
|
||||
SignedType number{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(number < 0))
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||
exception_message(input_format, "integer value overflow", "size"), nullptr));
|
||||
}
|
||||
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[out] result determined size
|
||||
@param[in,out] is_ndarray for input, `true` means already inside an ndarray vector
|
||||
@@ -2752,73 +2736,16 @@ class binary_reader
|
||||
}
|
||||
|
||||
case 'i':
|
||||
{
|
||||
std::int8_t number{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (number < 0)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||
}
|
||||
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||
return true;
|
||||
}
|
||||
return get_ubjson_signed_count<std::int8_t>(result);
|
||||
|
||||
case 'I':
|
||||
{
|
||||
std::int16_t number{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (number < 0)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||
}
|
||||
result = static_cast<std::size_t>(number);
|
||||
return true;
|
||||
}
|
||||
return get_ubjson_signed_count<std::int16_t>(result);
|
||||
|
||||
case 'l':
|
||||
{
|
||||
std::int32_t number{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (number < 0)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||
}
|
||||
result = static_cast<std::size_t>(number);
|
||||
return true;
|
||||
}
|
||||
return get_ubjson_signed_count<std::int32_t>(result);
|
||||
|
||||
case 'L':
|
||||
{
|
||||
std::int64_t number{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (number < 0)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||
}
|
||||
if (!value_in_range_of<std::size_t>(number))
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||
exception_message(input_format, "integer value overflow", "size"), nullptr));
|
||||
}
|
||||
result = static_cast<std::size_t>(number);
|
||||
return true;
|
||||
}
|
||||
return get_ubjson_signed_count<std::int64_t>(result);
|
||||
|
||||
case 'u':
|
||||
{
|
||||
@@ -2909,16 +2836,23 @@ class binary_reader
|
||||
result = 1;
|
||||
for (auto i : dim)
|
||||
{
|
||||
// Pre-multiplication overflow check: if i > 0 and result > SIZE_MAX/i, then result*i would overflow.
|
||||
// This check must happen before multiplication since overflow detection after the fact is unreliable
|
||||
// as modular arithmetic can produce any value, not just 0 or SIZE_MAX.
|
||||
if (JSON_HEDLEY_UNLIKELY(i > 0 && result > (std::numeric_limits<std::size_t>::max)() / i))
|
||||
// Pre-multiplication overflow check: since the loop above
|
||||
// already rejected any zero dimension, i is always > 0
|
||||
// here, so result > SIZE_MAX/i means result*i would
|
||||
// overflow. This check must happen before multiplication
|
||||
// since overflow detection after the fact is unreliable,
|
||||
// as modular arithmetic can produce any value, not just 0
|
||||
// or SIZE_MAX.
|
||||
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
||||
}
|
||||
result *= i;
|
||||
// Additional post-multiplication check to catch any edge cases the pre-check might miss
|
||||
if (result == 0 || result == npos)
|
||||
// the pre-check above already rules out result becoming 0
|
||||
// by overflow; the only value it cannot rule out is an
|
||||
// exact match with npos, the sentinel reserved for an
|
||||
// unknown-size container (see get_ubjson_size_type())
|
||||
if (result == npos)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
||||
}
|
||||
@@ -2979,7 +2913,7 @@ class binary_reader
|
||||
{
|
||||
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
||||
if (input_format == input_format_t::bjdata
|
||||
&& JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))
|
||||
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||
@@ -3123,50 +3057,7 @@ class binary_reader
|
||||
{
|
||||
break;
|
||||
}
|
||||
const auto byte1_raw = get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto byte2_raw = get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
||||
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
||||
|
||||
// Code from RFC 8949, Appendix D, Figure 3:
|
||||
// As half-precision floating-point numbers were only added
|
||||
// to IEEE 754 in 2008, today's programming platforms often
|
||||
// still only have limited support for them. It is very
|
||||
// easy to include at least decoding support for them even
|
||||
// without such support. An example of a small decoder for
|
||||
// half-precision floating-point numbers in the C language
|
||||
// is shown in Fig. 3.
|
||||
const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);
|
||||
const double val = [&half]
|
||||
{
|
||||
const int exp = (half >> 10u) & 0x1Fu;
|
||||
const unsigned int mant = half & 0x3FFu;
|
||||
JSON_ASSERT(exp <= 31);
|
||||
JSON_ASSERT(mant <= 1023);
|
||||
switch (exp)
|
||||
{
|
||||
case 0:
|
||||
return std::ldexp(mant, -24);
|
||||
case 31:
|
||||
return (mant == 0)
|
||||
? std::numeric_limits<double>::infinity()
|
||||
: std::numeric_limits<double>::quiet_NaN();
|
||||
default:
|
||||
return std::ldexp(mant + 1024, exp - 25);
|
||||
}
|
||||
}();
|
||||
return sax->number_float((half & 0x8000u) != 0
|
||||
? static_cast<number_float_t>(-val)
|
||||
: static_cast<number_float_t>(val), "");
|
||||
return get_half_float(input_format, true);
|
||||
}
|
||||
|
||||
case 'd':
|
||||
@@ -3239,19 +3130,16 @@ class binary_reader
|
||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||
{
|
||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||
auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)
|
||||
{
|
||||
return p.first < t;
|
||||
});
|
||||
const char* type_name = bjd_type_name(size_and_type.second);
|
||||
string_t key = "_ArrayType_";
|
||||
if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))
|
||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
||||
}
|
||||
|
||||
string_t type = it->second; // sax->string() takes a reference
|
||||
string_t type = type_name; // sax->string() takes a reference
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
||||
{
|
||||
return false;
|
||||
@@ -3543,8 +3431,7 @@ class binary_reader
|
||||
|
||||
if (at_end)
|
||||
{
|
||||
container_stack.pop_back();
|
||||
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -4083,6 +3970,68 @@ class binary_reader
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read and decode an IEEE 754 half-precision (16-bit) float
|
||||
|
||||
Used by CBOR (big endian) and BJData (little endian); the two formats
|
||||
only differ in the byte order of the two bytes that make up the half.
|
||||
|
||||
@param[in] format the current format (for diagnostics)
|
||||
@param[in] little_endian whether the two bytes are little endian (BJData)
|
||||
or big endian (CBOR)
|
||||
|
||||
@return whether reading and decoding succeeded
|
||||
*/
|
||||
bool get_half_float(const input_format_t format, const bool little_endian)
|
||||
{
|
||||
const auto byte1_raw = get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto byte2_raw = get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
||||
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
||||
|
||||
// Code from RFC 8949, Appendix D, Figure 3:
|
||||
// As half-precision floating-point numbers were only added
|
||||
// to IEEE 754 in 2008, today's programming platforms often
|
||||
// still only have limited support for them. It is very
|
||||
// easy to include at least decoding support for them even
|
||||
// without such support. An example of a small decoder for
|
||||
// half-precision floating-point numbers in the C language
|
||||
// is shown in Fig. 3.
|
||||
const auto half = little_endian
|
||||
? static_cast<unsigned int>((byte2 << 8u) + byte1)
|
||||
: static_cast<unsigned int>((byte1 << 8u) + byte2);
|
||||
const double val = [&half]
|
||||
{
|
||||
const int exp = (half >> 10u) & 0x1Fu;
|
||||
const unsigned int mant = half & 0x3FFu;
|
||||
JSON_ASSERT(exp <= 31);
|
||||
JSON_ASSERT(mant <= 1023);
|
||||
switch (exp)
|
||||
{
|
||||
case 0:
|
||||
return std::ldexp(mant, -24);
|
||||
case 31:
|
||||
return (mant == 0)
|
||||
? std::numeric_limits<double>::infinity()
|
||||
: std::numeric_limits<double>::quiet_NaN();
|
||||
default:
|
||||
return std::ldexp(mant + 1024, exp - 25);
|
||||
}
|
||||
}();
|
||||
return sax->number_float((half & 0x8000u) != 0
|
||||
? static_cast<number_float_t>(-val)
|
||||
: static_cast<number_float_t>(val), "");
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a string by reading characters from the input
|
||||
|
||||
@@ -4308,38 +4257,61 @@ class binary_reader
|
||||
/// BON8: number of bytes in @ref bon8_pushback
|
||||
std::size_t bon8_pushback_size = 0;
|
||||
|
||||
// excluded markers in bjdata optimized type
|
||||
#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
|
||||
make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
|
||||
|
||||
#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \
|
||||
make_array<bjd_type>( \
|
||||
bjd_type{'B', "byte"}, \
|
||||
bjd_type{'C', "char"}, \
|
||||
bjd_type{'D', "double"}, \
|
||||
bjd_type{'I', "int16"}, \
|
||||
bjd_type{'L', "int64"}, \
|
||||
bjd_type{'M', "uint64"}, \
|
||||
bjd_type{'U', "uint8"}, \
|
||||
bjd_type{'d', "single"}, \
|
||||
bjd_type{'i', "int8"}, \
|
||||
bjd_type{'l', "int32"}, \
|
||||
bjd_type{'m', "uint32"}, \
|
||||
bjd_type{'u', "uint16"})
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
// lookup tables
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
|
||||
const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =
|
||||
JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;
|
||||
/*!
|
||||
@brief whether @a marker is excluded from BJData's optimized ND-array types
|
||||
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
|
||||
|
||||
using bjd_type = std::pair<char_int_type, string_t>;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
|
||||
const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =
|
||||
JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;
|
||||
Mirrors binary_writer's @ref binary_writer::is_bjdata_excluded_type_marker
|
||||
"is_bjdata_excluded_type_marker()`, which encodes the same list the other
|
||||
way; keep the two in sync.
|
||||
*/
|
||||
static constexpr bool is_bjd_excluded_optimized_type(const char_int_type marker) noexcept
|
||||
{
|
||||
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
||||
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
||||
}
|
||||
|
||||
#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_
|
||||
#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_
|
||||
/*!
|
||||
@brief look up the ND-array element type name for a BJData dtype marker
|
||||
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
||||
not name a known dtype
|
||||
|
||||
A C++11 `constexpr` function cannot contain a `switch`, so this is a
|
||||
plain (non-constexpr) switch instead.
|
||||
*/
|
||||
static const char* bjd_type_name(const char_int_type marker)
|
||||
{
|
||||
switch (marker)
|
||||
{
|
||||
case 'B':
|
||||
return "byte";
|
||||
case 'C':
|
||||
return "char";
|
||||
case 'D':
|
||||
return "double";
|
||||
case 'I':
|
||||
return "int16";
|
||||
case 'L':
|
||||
return "int64";
|
||||
case 'M':
|
||||
return "uint64";
|
||||
case 'U':
|
||||
return "uint8";
|
||||
case 'd':
|
||||
return "single";
|
||||
case 'i':
|
||||
return "int8";
|
||||
case 'l':
|
||||
return "int32";
|
||||
case 'm':
|
||||
return "uint32";
|
||||
case 'u':
|
||||
return "uint16";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef JSON_HAS_CPP_17
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/string_escape.hpp>
|
||||
#include <nlohmann/detail/string_utils.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
@@ -116,7 +117,7 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||
json_pointer& operator/=(std::size_t array_idx)
|
||||
{
|
||||
return *this /= std::to_string(array_idx);
|
||||
return *this /= detail::to_string<string_t>(array_idx);
|
||||
}
|
||||
|
||||
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||
@@ -752,7 +753,7 @@ class json_pointer
|
||||
// would throw out_of_range.404 -- contains() must not throw (see #5395)
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !('0' <= reference_token[0] && reference_token[0] <= '9')))
|
||||
{
|
||||
// invalid char
|
||||
return false;
|
||||
@@ -780,7 +781,7 @@ class json_pointer
|
||||
// not throw (see #5395), so such a reference token is treated as "not found"
|
||||
errno = 0; // strtoull() does not reset errno on success
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX
|
||||
|| magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
||||
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
|
||||
@@ -161,11 +160,5 @@ struct static_const
|
||||
constexpr T static_const<T>::value;
|
||||
#endif
|
||||
|
||||
template<typename T, typename... Args>
|
||||
constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
|
||||
{
|
||||
return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+23
-20
@@ -5034,7 +5034,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
return format == input_format_t::json
|
||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
||||
}
|
||||
|
||||
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -5051,7 +5051,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
return format == input_format_t::json
|
||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
||||
}
|
||||
|
||||
/// @brief generate SAX events
|
||||
@@ -5073,7 +5073,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
||||
}
|
||||
#ifndef JSON_NO_IO
|
||||
/// @brief deserialize from stream
|
||||
@@ -5092,7 +5092,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
|
||||
friend std::istream& operator>>(std::istream& i, basic_json& j)
|
||||
{
|
||||
parser(detail::input_adapter(i)).parse(false, j);
|
||||
// parse into a temporary so that j is left unchanged if parsing fails
|
||||
basic_json result;
|
||||
parser(detail::input_adapter(i)).parse(false, result);
|
||||
j = std::move(result);
|
||||
return i;
|
||||
}
|
||||
#endif // JSON_NO_IO
|
||||
@@ -5368,7 +5371,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5388,7 +5391,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5417,7 +5420,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = i.get();
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5435,7 +5438,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5454,7 +5457,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5481,7 +5484,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = i.get();
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5499,7 +5502,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5518,7 +5521,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5545,7 +5548,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = i.get();
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5563,7 +5566,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5582,7 +5585,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5600,7 +5603,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5619,7 +5622,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5637,7 +5640,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5656,7 +5659,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
basic_json result;
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
@@ -5683,7 +5686,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = i.get();
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||
{
|
||||
result = value_t::discarded;
|
||||
}
|
||||
|
||||
+709
-891
File diff suppressed because it is too large
Load Diff
@@ -352,6 +352,41 @@ TEST_CASE("alternative string type")
|
||||
CHECK(j2.flatten().unflatten() == j2);
|
||||
}
|
||||
|
||||
SECTION("contains(json_pointer)")
|
||||
{
|
||||
// contains(json_pointer) must compile and work with a string_t that has
|
||||
// no c_str() and no comparison with const char* (see #5666)
|
||||
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
|
||||
|
||||
// present: object key and array indices
|
||||
CHECK(j.contains(alt_json::json_pointer("/foo")));
|
||||
CHECK(j.contains(alt_json::json_pointer("/foo/0")));
|
||||
CHECK(j.contains(alt_json::json_pointer("/foo/1")));
|
||||
|
||||
// missing: absent object key and out-of-range array index
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/bar")));
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/2")));
|
||||
|
||||
// "-" always fails the range check
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/-")));
|
||||
|
||||
// an array index must not have a leading zero
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/01")));
|
||||
|
||||
// a reference token that is not a number
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/bar")));
|
||||
}
|
||||
|
||||
SECTION("operator/(std::size_t)")
|
||||
{
|
||||
// json_pointer::operator/=(std::size_t) must compile without string_t
|
||||
// being constructible from std::string (see #5666)
|
||||
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
|
||||
|
||||
CHECK(j.at(alt_json::json_pointer("/foo") / std::size_t(0)) == j["foo"][0]);
|
||||
CHECK(j.at(alt_json::json_pointer("/foo") / std::size_t(1)) == j["foo"][1]);
|
||||
}
|
||||
|
||||
SECTION("patch")
|
||||
{
|
||||
alt_json const patch1 = alt_json::parse(R"([{ "op": "add", "path": "/a/b", "value": [ "foo", "bar" ] }])");
|
||||
|
||||
@@ -210,15 +210,42 @@ TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \
|
||||
|
||||
TEST_CASE("BJData")
|
||||
{
|
||||
SECTION("binary_reader BJData LUT arrays are sorted")
|
||||
SECTION("binary_reader BJData lookup tables")
|
||||
{
|
||||
std::vector<std::uint8_t> const data;
|
||||
auto ia = nlohmann::detail::input_adapter(data);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
nlohmann::detail::binary_reader<json, decltype(ia)> const br{std::move(ia), json::input_format_t::bjdata};
|
||||
|
||||
CHECK(std::is_sorted(br.bjd_optimized_type_markers.begin(), br.bjd_optimized_type_markers.end()));
|
||||
CHECK(std::is_sorted(br.bjd_types_map.begin(), br.bjd_types_map.end()));
|
||||
// the excluded optimized-type markers must match binary_writer's
|
||||
// is_bjdata_excluded_type_marker(), which encodes the same 8 markers
|
||||
for (const char marker :
|
||||
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'
|
||||
})
|
||||
{
|
||||
CHECK(br.is_bjd_excluded_optimized_type(marker));
|
||||
}
|
||||
for (const char marker :
|
||||
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'x'
|
||||
})
|
||||
{
|
||||
CHECK(!br.is_bjd_excluded_optimized_type(marker));
|
||||
}
|
||||
|
||||
// every dtype marker must round-trip to its ND-array type name
|
||||
const std::vector<std::pair<char, std::string>> types
|
||||
{
|
||||
{'B', "byte"}, {'C', "char"}, {'D', "double"}, {'I', "int16"},
|
||||
{'L', "int64"}, {'M', "uint64"}, {'U', "uint8"}, {'d', "single"},
|
||||
{'i', "int8"}, {'l', "int32"}, {'m', "uint32"}, {'u', "uint16"}
|
||||
};
|
||||
for (const auto& type : types)
|
||||
{
|
||||
const char* name = br.bjd_type_name(type.first);
|
||||
REQUIRE(name != nullptr);
|
||||
CHECK(std::string(name) == type.second);
|
||||
}
|
||||
CHECK(br.bjd_type_name('x') == nullptr);
|
||||
}
|
||||
|
||||
SECTION("individual values")
|
||||
|
||||
@@ -19,6 +19,7 @@ using nlohmann::json;
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <sstream>
|
||||
|
||||
TEST_CASE("Better diagnostics")
|
||||
{
|
||||
@@ -492,6 +493,21 @@ TEST_CASE("Regression tests for extended diagnostics")
|
||||
CHECK(copy == j);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Regression test for issue #5652 - operator>> leaves a partial value in its target on a parse error")
|
||||
{
|
||||
json j = "old value";
|
||||
std::istringstream is("[1, x");
|
||||
CHECK_THROWS_WITH_AS(is >> j, "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: '1, x'", json::parse_error);
|
||||
|
||||
// j must be left unchanged, as json::parse() guarantees for its result
|
||||
CHECK(j == "old value");
|
||||
|
||||
// copying j must not trigger assert_invariant(): a failed parse must
|
||||
// not leave array/object elements without a parent pointer
|
||||
json const copy = j; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(copy == j);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Better diagnostics past the descent bound of update() and merge_patch()")
|
||||
|
||||
@@ -3033,3 +3033,224 @@ TEST_CASE("UBJSON optimized array of unsigned integers beyond int64")
|
||||
CHECK(json::to_ubjson(j, true, true) == expected);
|
||||
CHECK(json::from_ubjson(expected) == j);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// the bytes that follow the marker of an integer: the value in the width of
|
||||
// the marker (big endian for UBJSON, little endian for BJData), or, for a
|
||||
// high-precision number, the length and the decimal digits
|
||||
std::vector<std::uint8_t> integer_payload(const char marker, const json& value, const bool little_endian)
|
||||
{
|
||||
std::size_t width = 0;
|
||||
switch (marker)
|
||||
{
|
||||
case 'i':
|
||||
case 'U':
|
||||
width = 1;
|
||||
break;
|
||||
case 'I':
|
||||
case 'u':
|
||||
width = 2;
|
||||
break;
|
||||
case 'l':
|
||||
case 'm':
|
||||
width = 4;
|
||||
break;
|
||||
case 'L':
|
||||
case 'M':
|
||||
width = 8;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
const std::string digits = value.dump();
|
||||
std::vector<std::uint8_t> result = {'i', static_cast<std::uint8_t>(digits.size())};
|
||||
for (const char c : digits)
|
||||
{
|
||||
result.push_back(static_cast<std::uint8_t>(c));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const std::uint64_t bits = value.is_number_unsigned()
|
||||
? value.get<std::uint64_t>()
|
||||
: static_cast<std::uint64_t>(value.get<std::int64_t>());
|
||||
std::vector<std::uint8_t> result(width);
|
||||
for (std::size_t i = 0; i < width; ++i)
|
||||
{
|
||||
result[little_endian ? i : width - 1 - i] = static_cast<std::uint8_t>(bits >> (8 * i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
json i64(const std::int64_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
json u64(const std::uint64_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("UBJSON and BJData integer markers at every range edge")
|
||||
{
|
||||
// An optimized container announces the marker of its values after `$` and
|
||||
// then writes every value without a marker, so the marker the writer
|
||||
// announces and the width it writes must match for every value. This
|
||||
// checks both for the values around each edge of the integer types, as
|
||||
// scalars and as the values of optimized arrays and objects.
|
||||
struct integer_case
|
||||
{
|
||||
json value;
|
||||
char ubjson; // expected UBJSON marker
|
||||
char bjdata; // expected BJData marker
|
||||
};
|
||||
|
||||
const std::int64_t int64_min = (std::numeric_limits<std::int64_t>::min)();
|
||||
const std::int64_t int64_max = (std::numeric_limits<std::int64_t>::max)();
|
||||
const std::uint64_t uint64_max = (std::numeric_limits<std::uint64_t>::max)();
|
||||
|
||||
const std::vector<integer_case> cases =
|
||||
{
|
||||
// int8
|
||||
{i64(-129), 'I', 'I'},
|
||||
{i64(-128), 'i', 'i'},
|
||||
{i64(-127), 'i', 'i'},
|
||||
{i64(-1), 'i', 'i'},
|
||||
{i64(0), 'i', 'i'},
|
||||
{u64(0), 'i', 'i'},
|
||||
{i64(126), 'i', 'i'},
|
||||
{i64(127), 'i', 'i'},
|
||||
{u64(127), 'i', 'i'},
|
||||
{i64(128), 'U', 'U'},
|
||||
{u64(128), 'U', 'U'},
|
||||
// uint8
|
||||
{i64(254), 'U', 'U'},
|
||||
{i64(255), 'U', 'U'},
|
||||
{u64(255), 'U', 'U'},
|
||||
{i64(256), 'I', 'I'},
|
||||
{u64(256), 'I', 'I'},
|
||||
// int16
|
||||
{i64(-32769), 'l', 'l'},
|
||||
{i64(-32768), 'I', 'I'},
|
||||
{i64(-32767), 'I', 'I'},
|
||||
{i64(32766), 'I', 'I'},
|
||||
{i64(32767), 'I', 'I'},
|
||||
{u64(32767), 'I', 'I'},
|
||||
{i64(32768), 'l', 'u'},
|
||||
{u64(32768), 'l', 'u'},
|
||||
// uint16 (BJData only)
|
||||
{i64(65534), 'l', 'u'},
|
||||
{i64(65535), 'l', 'u'},
|
||||
{u64(65535), 'l', 'u'},
|
||||
{i64(65536), 'l', 'l'},
|
||||
{u64(65536), 'l', 'l'},
|
||||
// int32
|
||||
{i64(-2147483649LL), 'L', 'L'},
|
||||
{i64(-2147483648LL), 'l', 'l'},
|
||||
{i64(-2147483647LL), 'l', 'l'},
|
||||
{i64(2147483646LL), 'l', 'l'},
|
||||
{i64(2147483647LL), 'l', 'l'},
|
||||
{u64(2147483647ULL), 'l', 'l'},
|
||||
{i64(2147483648LL), 'L', 'm'},
|
||||
{u64(2147483648ULL), 'L', 'm'},
|
||||
// uint32 (BJData only)
|
||||
{i64(4294967294LL), 'L', 'm'},
|
||||
{i64(4294967295LL), 'L', 'm'},
|
||||
{u64(4294967295ULL), 'L', 'm'},
|
||||
{i64(4294967296LL), 'L', 'L'},
|
||||
{u64(4294967296ULL), 'L', 'L'},
|
||||
// int64
|
||||
{i64(int64_min), 'L', 'L'},
|
||||
{i64(int64_min + 1), 'L', 'L'},
|
||||
{i64(int64_max - 1), 'L', 'L'},
|
||||
{i64(int64_max), 'L', 'L'},
|
||||
{u64(static_cast<std::uint64_t>(int64_max)), 'L', 'L'},
|
||||
// uint64 (BJData only; UBJSON writes a high-precision number)
|
||||
{u64(static_cast<std::uint64_t>(int64_max) + 1), 'H', 'M'},
|
||||
{u64(uint64_max - 1), 'H', 'M'},
|
||||
{u64(uint64_max), 'H', 'M'},
|
||||
};
|
||||
|
||||
for (const auto& c : cases)
|
||||
{
|
||||
for (const bool bjdata :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
const char marker = bjdata ? c.bjdata : c.ubjson;
|
||||
const std::vector<std::uint8_t> payload = integer_payload(marker, c.value, bjdata);
|
||||
const auto to_binary = [bjdata](const json & j, const bool use_size, const bool use_type)
|
||||
{
|
||||
return bjdata ? json::to_bjdata(j, use_size, use_type) : json::to_ubjson(j, use_size, use_type);
|
||||
};
|
||||
const auto from_binary = [bjdata](const std::vector<std::uint8_t>& v)
|
||||
{
|
||||
return bjdata ? json::from_bjdata(v) : json::from_ubjson(v);
|
||||
};
|
||||
INFO("value = " << c.value.dump() << (c.value.is_number_unsigned() ? " (unsigned)" : "") << ", format = " << (bjdata ? "BJData" : "UBJSON"));
|
||||
|
||||
// scalar
|
||||
std::vector<std::uint8_t> expected = {static_cast<std::uint8_t>(marker)};
|
||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
||||
for (const bool use_size :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
CHECK(to_binary(c.value, use_size, false) == expected);
|
||||
}
|
||||
CHECK(from_binary(expected) == c.value);
|
||||
|
||||
const json arr = {c.value, c.value, c.value};
|
||||
|
||||
// array without count or type: every value has its marker
|
||||
expected = {'['};
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
expected.push_back(static_cast<std::uint8_t>(marker));
|
||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
||||
}
|
||||
expected.push_back(']');
|
||||
CHECK(to_binary(arr, false, false) == expected);
|
||||
CHECK(from_binary(expected) == arr);
|
||||
|
||||
// array with count: every value has its marker
|
||||
expected = {'[', '#', 'i', 3};
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
expected.push_back(static_cast<std::uint8_t>(marker));
|
||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
||||
}
|
||||
CHECK(to_binary(arr, true, false) == expected);
|
||||
CHECK(from_binary(expected) == arr);
|
||||
|
||||
// array with type and count: the marker once, then the payloads
|
||||
expected = {'[', '$', static_cast<std::uint8_t>(marker), '#', 'i', 3};
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
||||
}
|
||||
CHECK(to_binary(arr, true, true) == expected);
|
||||
CHECK(from_binary(expected) == arr);
|
||||
|
||||
// object with type and count: the marker once, then key and payload
|
||||
const json obj = {{"a", c.value}, {"b", c.value}};
|
||||
expected = {'{', '$', static_cast<std::uint8_t>(marker), '#', 'i', 2};
|
||||
for (const char key :
|
||||
{'a', 'b'
|
||||
})
|
||||
{
|
||||
expected.push_back('i');
|
||||
expected.push_back(1);
|
||||
expected.push_back(static_cast<std::uint8_t>(key));
|
||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
||||
}
|
||||
CHECK(to_binary(obj, true, true) == expected);
|
||||
CHECK(from_binary(expected) == obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user