mirror of
https://github.com/nlohmann/json.git
synced 2026-09-07 00:37:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fb73ee33e |
@@ -996,21 +996,23 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR string
|
||||
@brief reads a CBOR string
|
||||
|
||||
Reads everything @ref get_cbor_string accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length string can be read
|
||||
into the same string.
|
||||
This function first reads starting bytes to determine the expected
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
|
||||
@param[out] result string the bytes are appended to
|
||||
@param[out] result created string
|
||||
|
||||
@return whether string creation completed
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_string_chunk(string_t& result)
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (current)
|
||||
{
|
||||
// UTF-8 string (0x00..0x17 bytes follow)
|
||||
@@ -1066,6 +1068,20 @@ class binary_reader
|
||||
return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x7F: // UTF-8 string (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
string_t chunk;
|
||||
if (!get_cbor_string(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(chunk);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -1076,82 +1092,23 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR string
|
||||
@brief reads a CBOR byte array
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created string
|
||||
|
||||
@return whether string creation completed
|
||||
*/
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
// number of indefinite-length strings that have been opened and not
|
||||
// closed yet. RFC 8949, Section 3.2.3 does not permit nesting them,
|
||||
// but this reader has always accepted it, so the open levels are
|
||||
// counted instead of recursed through, which overflowed the stack for
|
||||
// an input of repeated 0x7F bytes (see #5104). Every chunk is appended
|
||||
// to the same result, so no per-level state is needed.
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x7F) // UTF-8 string (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length string;
|
||||
// outside of one it is not a string and falls through to the error
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR byte array
|
||||
|
||||
Reads everything @ref get_cbor_binary accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length byte array can be
|
||||
read into the same byte array.
|
||||
|
||||
@param[out] result byte array the bytes are appended to
|
||||
@param[out] result created byte array
|
||||
|
||||
@return whether byte array creation completed
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_binary_chunk(binary_t& result)
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (current)
|
||||
{
|
||||
// Binary data (0x00..0x17 bytes follow)
|
||||
@@ -1211,6 +1168,20 @@ class binary_reader
|
||||
get_binary(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x5F: // Binary data (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
binary_t chunk;
|
||||
if (!get_cbor_binary(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.insert(result.end(), chunk.begin(), chunk.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -1220,63 +1191,6 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR byte array
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created byte array
|
||||
|
||||
@return whether byte array creation completed
|
||||
*/
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
// the open indefinite-length byte arrays are counted rather than
|
||||
// recursed through, for the reason given in @ref get_cbor_string
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x5F) // Binary data (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length byte
|
||||
// array; outside of one it falls through to the error below
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_binary_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief narrow a definite CBOR array/map length to std::size_t
|
||||
|
||||
|
||||
+14
-14
@@ -4474,7 +4474,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -4491,7 +4491,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -4517,7 +4517,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format
|
||||
@@ -4532,7 +4532,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -4548,7 +4548,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -4572,7 +4572,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format
|
||||
@@ -4587,7 +4587,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -4603,7 +4603,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -4627,7 +4627,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format
|
||||
@@ -4642,7 +4642,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -4658,7 +4658,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format
|
||||
@@ -4673,7 +4673,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -4689,7 +4689,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -4713,7 +4713,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
/// @}
|
||||
|
||||
|
||||
@@ -11683,21 +11683,23 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR string
|
||||
@brief reads a CBOR string
|
||||
|
||||
Reads everything @ref get_cbor_string accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length string can be read
|
||||
into the same string.
|
||||
This function first reads starting bytes to determine the expected
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
|
||||
@param[out] result string the bytes are appended to
|
||||
@param[out] result created string
|
||||
|
||||
@return whether string creation completed
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_string_chunk(string_t& result)
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (current)
|
||||
{
|
||||
// UTF-8 string (0x00..0x17 bytes follow)
|
||||
@@ -11753,6 +11755,20 @@ class binary_reader
|
||||
return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x7F: // UTF-8 string (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
string_t chunk;
|
||||
if (!get_cbor_string(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(chunk);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -11763,82 +11779,23 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR string
|
||||
@brief reads a CBOR byte array
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created string
|
||||
|
||||
@return whether string creation completed
|
||||
*/
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
// number of indefinite-length strings that have been opened and not
|
||||
// closed yet. RFC 8949, Section 3.2.3 does not permit nesting them,
|
||||
// but this reader has always accepted it, so the open levels are
|
||||
// counted instead of recursed through, which overflowed the stack for
|
||||
// an input of repeated 0x7F bytes (see #5104). Every chunk is appended
|
||||
// to the same result, so no per-level state is needed.
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x7F) // UTF-8 string (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length string;
|
||||
// outside of one it is not a string and falls through to the error
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR byte array
|
||||
|
||||
Reads everything @ref get_cbor_binary accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length byte array can be
|
||||
read into the same byte array.
|
||||
|
||||
@param[out] result byte array the bytes are appended to
|
||||
@param[out] result created byte array
|
||||
|
||||
@return whether byte array creation completed
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_binary_chunk(binary_t& result)
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (current)
|
||||
{
|
||||
// Binary data (0x00..0x17 bytes follow)
|
||||
@@ -11898,6 +11855,20 @@ class binary_reader
|
||||
get_binary(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x5F: // Binary data (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
binary_t chunk;
|
||||
if (!get_cbor_binary(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.insert(result.end(), chunk.begin(), chunk.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -11907,63 +11878,6 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR byte array
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created byte array
|
||||
|
||||
@return whether byte array creation completed
|
||||
*/
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
// the open indefinite-length byte arrays are counted rather than
|
||||
// recursed through, for the reason given in @ref get_cbor_string
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x5F) // Binary data (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length byte
|
||||
// array; outside of one it falls through to the error below
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_binary_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief narrow a definite CBOR array/map length to std::size_t
|
||||
|
||||
@@ -25988,7 +25902,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -26005,7 +25919,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -26031,7 +25945,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format
|
||||
@@ -26046,7 +25960,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -26062,7 +25976,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -26086,7 +26000,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format
|
||||
@@ -26101,7 +26015,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -26117,7 +26031,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -26141,7 +26055,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format
|
||||
@@ -26156,7 +26070,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -26172,7 +26086,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format
|
||||
@@ -26187,7 +26101,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -26203,7 +26117,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -26227,7 +26141,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
|
||||
return res ? std::move(result) : basic_json(value_t::discarded);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
/// @}
|
||||
|
||||
|
||||
@@ -177,6 +177,24 @@ json_test_add_test_for(src/unit-comparison.cpp
|
||||
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
||||
)
|
||||
|
||||
# test the parser again with JSON_DIAGNOSTIC_POSITIONS enabled
|
||||
json_test_set_test_options(test-class_parser_diagnostic_positions
|
||||
COMPILE_DEFINITIONS JSON_DIAGNOSTIC_POSITIONS=1
|
||||
)
|
||||
json_test_add_test_for(src/unit-class_parser.cpp
|
||||
NAME test-class_parser_diagnostic_positions
|
||||
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
||||
)
|
||||
|
||||
# test diagnostic positions again without regular diagnostics (JSON pointer paths)
|
||||
json_test_set_test_options(test-diagnostic-positions_only
|
||||
COMPILE_DEFINITIONS JSON_DIAGNOSTICS=0
|
||||
)
|
||||
json_test_add_test_for(src/unit-diagnostic-positions.cpp
|
||||
NAME test-diagnostic-positions_only
|
||||
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
||||
)
|
||||
|
||||
# *DO NOT* use json_test_set_test_options() below this line
|
||||
|
||||
#############################################################################
|
||||
|
||||
@@ -2035,58 +2035,6 @@ TEST_CASE("CBOR definite length equal to the indefinite-length sentinel")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
||||
{
|
||||
// Reading an indefinite-length string or byte array used to call itself
|
||||
// once per chunk, so a payload of repeated 0x7F (or 0x5F) bytes exhausted
|
||||
// the call stack before any of the input was rejected. The open levels are
|
||||
// counted now, and the levels below prove the reader still reads the same
|
||||
// values and reports the same errors at the same byte offsets.
|
||||
json _;
|
||||
|
||||
SECTION("many open levels are reported, not crashed on")
|
||||
{
|
||||
const std::vector<uint8_t> input(200000, 0x7F);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 200001: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
||||
CHECK(json::from_cbor(input, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("many open levels are reported, not crashed on (binary)")
|
||||
{
|
||||
const std::vector<uint8_t> input(200000, 0x5F);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 200001: syntax error while parsing CBOR binary: unexpected end of input", json::parse_error&);
|
||||
CHECK(json::from_cbor(input, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("chunks are still concatenated")
|
||||
{
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0xFF})) == json(""));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x61, 0x61, 0xFF})) == json("a"));
|
||||
// nested indefinite-length strings are concatenated across levels
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x61, 0x61, 0xFF, 0x61, 0x62, 0xFF})) == json("ab"));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x7F, 0x61, 0x7A, 0xFF, 0xFF, 0xFF})) == json("z"));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xA1, 0x7F, 0x61, 0x61, 0xFF, 0x01})) == json({{"a", 1}}));
|
||||
}
|
||||
|
||||
SECTION("chunks are still concatenated (binary)")
|
||||
{
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x5F, 0x41, 0x61, 0xFF})) == json::binary({0x61}));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x5F, 0x5F, 0x41, 0x61, 0xFF, 0x41, 0x62, 0xFF})) == json::binary({0x61, 0x62}));
|
||||
}
|
||||
|
||||
SECTION("a chunk that is not a string is still rejected")
|
||||
{
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x00})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x00", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x5F, 0x5F, 0x00})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR binary: expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x00", json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("a break marker outside an indefinite-length string is not a string")
|
||||
{
|
||||
// 0xFF only closes a string that was opened; on its own it is not one
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR roundtrips" * doctest::skip())
|
||||
{
|
||||
SECTION("input from flynn")
|
||||
|
||||
@@ -344,6 +344,50 @@ void trailing_comma_helper(const std::string& s)
|
||||
}
|
||||
}
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
/**
|
||||
* Validates that the generated JSON object is the same as expected
|
||||
* Validates that the start position and end position match the start and end of the string
|
||||
*
|
||||
* This check assumes that there is no whitespace around the json object in the original string.
|
||||
*/
|
||||
void validate_generated_json_and_start_end_pos_helper(const std::string& original_string, const json& j, const json& check)
|
||||
{
|
||||
CHECK(j == check);
|
||||
CHECK(j.start_pos() == 0);
|
||||
CHECK(j.end_pos() == original_string.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the root object from the given root string and validates that the start and end positions for the nested object are correct.
|
||||
*
|
||||
* This checks that whitespace around the nested object is included in the start and end positions of the root object.
|
||||
*/
|
||||
void validate_start_end_pos_for_nested_obj_helper(const std::string& nested_type_json_str, const std::string& root_type_json_str, const json& expected_json, const json::parser_callback_t& cb = nullptr)
|
||||
{
|
||||
json j;
|
||||
|
||||
// 1. If callback is provided, use callback version of parse()
|
||||
if (cb)
|
||||
{
|
||||
j = json::parse(root_type_json_str, cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
j = json::parse(root_type_json_str);
|
||||
}
|
||||
|
||||
// 2. Check if the generated JSON is as expected
|
||||
// Assumptions: The root_type_json_str does not have any whitespace around the json object
|
||||
validate_generated_json_and_start_end_pos_helper(root_type_json_str, j, expected_json);
|
||||
|
||||
// 3. Get the nested object
|
||||
const auto& nested = j["nested"];
|
||||
// 4. Check if the start and end positions are generated correctly for nested objects and arrays
|
||||
CHECK(nested_type_json_str == root_type_json_str.substr(nested.start_pos(), nested.end_pos() - nested.start_pos()));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("parser class")
|
||||
@@ -1779,6 +1823,228 @@ TEST_CASE("parser class")
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error);
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*<U+0000>'", json::parse_error);
|
||||
}
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
// Macro for all test cases for start_pos and end_pos
|
||||
#define SETUP_TESTCASES() \
|
||||
SECTION("with callback") \
|
||||
{ \
|
||||
SECTION("filter nothing") \
|
||||
{ \
|
||||
json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept \
|
||||
{ \
|
||||
return true; \
|
||||
}; \
|
||||
validate_start_end_pos_for_nested_obj_helper(nested_type_json_str, root_type_json_str, expected, cb); \
|
||||
} \
|
||||
SECTION("filter element") \
|
||||
{ \
|
||||
json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t event, json& j) noexcept \
|
||||
{ \
|
||||
return (event != json::parse_event_t::key && event != json::parse_event_t::value) || j != json("a"); \
|
||||
}; \
|
||||
validate_start_end_pos_for_nested_obj_helper(nested_type_json_str, root_type_json_str, filteredExpected, cb); \
|
||||
} \
|
||||
} \
|
||||
SECTION("without callback") \
|
||||
{ \
|
||||
validate_start_end_pos_for_nested_obj_helper(nested_type_json_str, root_type_json_str, expected); \
|
||||
}
|
||||
|
||||
SECTION("retrieve start position and end position")
|
||||
{
|
||||
SECTION("for object")
|
||||
{
|
||||
// Create an object with spaces to test the start and end positions. Spaces will not be included in the
|
||||
// JSON object, however, the start and end positions should include the spaces from the input JSON string.
|
||||
const std::string nested_type_json_str = R"({ "a": 1,"b" : "test1"})";
|
||||
const std::string root_type_json_str = R"({ "nested": )" + nested_type_json_str + R"(, "anotherValue": "test2"})";
|
||||
auto expected = json({{"nested", {{"a", 1}, {"b", "test1"}}}, {"anotherValue", "test2"}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected["nested"].erase("a");
|
||||
|
||||
SETUP_TESTCASES()
|
||||
}
|
||||
|
||||
SECTION("for array")
|
||||
{
|
||||
const std::string nested_type_json_str = R"(["a", "test", 45])";
|
||||
const std::string root_type_json_str = R"({ "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })";
|
||||
auto expected = json({{"nested", {"a", "test", 45}}, {"anotherValue", "test"}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected["nested"] = json({"test", 45});
|
||||
SETUP_TESTCASES()
|
||||
}
|
||||
|
||||
SECTION("for array with objects")
|
||||
{
|
||||
const std::string nested_type_json_str = R"([{"a": 1, "b": "test"}, {"c": 2, "d": "test2"}])";
|
||||
const std::string root_type_json_str = R"({ "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })";
|
||||
auto expected = json({{"nested", {{{"a", 1}, {"b", "test"}}, {{"c", 2}, {"d", "test2"}}}}, {"anotherValue", "test"}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected["nested"][0].erase("a");
|
||||
SETUP_TESTCASES()
|
||||
|
||||
auto j = json::parse(root_type_json_str);
|
||||
auto nested_array = j["nested"];
|
||||
const auto& nested_obj = nested_array[0];
|
||||
CHECK(nested_type_json_str.substr(1, 21) == root_type_json_str.substr(nested_obj.start_pos(), nested_obj.end_pos() - nested_obj.start_pos()));
|
||||
CHECK(nested_type_json_str.substr(24, 22) == root_type_json_str.substr(nested_array[1].start_pos(), nested_array[1].end_pos() - nested_array[1].start_pos()));
|
||||
}
|
||||
|
||||
SECTION("for two levels of nesting objects")
|
||||
{
|
||||
const std::string nested_type_json_str = R"({"nested2": {"b": "test"}})";
|
||||
const std::string root_type_json_str = R"({ "a": 2, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })";
|
||||
auto expected = json({{"a", 2}, {"nested", {{"nested2", {{"b", "test"}}}}}, {"anotherValue", "test"}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected.erase("a");
|
||||
SETUP_TESTCASES()
|
||||
|
||||
auto j = json::parse(root_type_json_str);
|
||||
auto nested_obj = j["nested"]["nested2"];
|
||||
CHECK(nested_type_json_str.substr(12, 13) == root_type_json_str.substr(nested_obj.start_pos(), nested_obj.end_pos() - nested_obj.start_pos()));
|
||||
}
|
||||
|
||||
SECTION("for simple types")
|
||||
{
|
||||
SECTION("no nested")
|
||||
{
|
||||
SECTION("with callback")
|
||||
{
|
||||
json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
// 1. string type
|
||||
std::string json_str = R"("test")";
|
||||
auto j = json::parse(json_str, cb);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, "test");
|
||||
|
||||
// 2. number type
|
||||
json_str = R"(1)";
|
||||
j = json::parse(json_str, cb);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, 1);
|
||||
|
||||
// 3. boolean type
|
||||
json_str = R"(true)";
|
||||
j = json::parse(json_str, cb);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, true);
|
||||
|
||||
// 4. null type
|
||||
json_str = R"(null)";
|
||||
j = json::parse(json_str, cb);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, nullptr);
|
||||
}
|
||||
|
||||
SECTION("without callback")
|
||||
{
|
||||
// 1. string type
|
||||
std::string json_str = R"("test")";
|
||||
auto j = json::parse(json_str);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, "test");
|
||||
|
||||
// 2. number type
|
||||
json_str = R"(1)";
|
||||
j = json::parse(json_str);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, 1);
|
||||
|
||||
json_str = R"(1.001239923)";
|
||||
j = json::parse(json_str);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, 1.001239923);
|
||||
|
||||
json_str = R"(1.123812389000000)";
|
||||
j = json::parse(json_str);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, 1.123812389);
|
||||
|
||||
// 3. boolean type
|
||||
json_str = R"(true)";
|
||||
j = json::parse(json_str);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, true);
|
||||
|
||||
json_str = R"(false)";
|
||||
j = json::parse(json_str);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, false);
|
||||
|
||||
// 4. null type
|
||||
json_str = R"(null)";
|
||||
j = json::parse(json_str);
|
||||
validate_generated_json_and_start_end_pos_helper(json_str, j, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("string type")
|
||||
{
|
||||
const std::string nested_type_json_str = R"("test")";
|
||||
const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })";
|
||||
auto expected = json({{"nested", "test"}, {"anotherValue", "test"}, {"a", 1}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected.erase("a");
|
||||
SETUP_TESTCASES()
|
||||
}
|
||||
|
||||
SECTION("number type")
|
||||
{
|
||||
const std::string nested_type_json_str = R"(2)";
|
||||
const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })";
|
||||
auto expected = json({{"nested", 2}, {"anotherValue", "test"}, {"a", 1}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected.erase("a");
|
||||
SETUP_TESTCASES()
|
||||
}
|
||||
|
||||
SECTION("boolean type")
|
||||
{
|
||||
const std::string nested_type_json_str = R"(true)";
|
||||
const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })";
|
||||
auto expected = json({{"nested", true}, {"anotherValue", "test"}, {"a", 1}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected.erase("a");
|
||||
SETUP_TESTCASES()
|
||||
}
|
||||
|
||||
SECTION("null type")
|
||||
{
|
||||
const std::string nested_type_json_str = R"(null)";
|
||||
const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })";
|
||||
auto expected = json({{"nested", nullptr}, {"anotherValue", "test"}, {"a", 1}});
|
||||
auto filteredExpected = expected;
|
||||
filteredExpected.erase("a");
|
||||
SETUP_TESTCASES()
|
||||
}
|
||||
}
|
||||
SECTION("with leading whitespace and newlines around root JSON")
|
||||
{
|
||||
const std::string initial_whitespace = R"(
|
||||
|
||||
)";
|
||||
const std::string nested_type_json_str = R"({
|
||||
"a": 1,
|
||||
"nested": {
|
||||
"b": "test"
|
||||
},
|
||||
"anotherValue": "test"
|
||||
})";
|
||||
const std::string end_whitespace = R"(
|
||||
|
||||
)";
|
||||
const std::string root_type_json_str = initial_whitespace + nested_type_json_str + end_whitespace;
|
||||
|
||||
auto expected = json({{"a", 1}, {"nested", {{"b", "test"}}}, {"anotherValue", "test"}});
|
||||
|
||||
auto j = json::parse(root_type_json_str);
|
||||
|
||||
// 2. Check if the generated JSON is as expected
|
||||
CHECK(j == expected);
|
||||
|
||||
// 3. Check if the start and end positions do not include the surrounding whitespace
|
||||
CHECK(j.start_pos() == initial_whitespace.size());
|
||||
CHECK(j.end_pos() == root_type_json_str.size() - end_whitespace.size());
|
||||
}
|
||||
}
|
||||
#undef SETUP_TESTCASES
|
||||
#endif
|
||||
}
|
||||
|
||||
// this test relies on parse errors being thrown, so it is skipped when
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#ifdef JSON_DIAGNOSTICS
|
||||
#undef JSON_DIAGNOSTICS
|
||||
#endif
|
||||
|
||||
#define JSON_DIAGNOSTICS 0
|
||||
#define JSON_DIAGNOSTIC_POSITIONS 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
TEST_CASE("Better diagnostics with positions only")
|
||||
{
|
||||
SECTION("invalid type")
|
||||
{
|
||||
const std::string json_invalid_string = R"(
|
||||
{
|
||||
"address": {
|
||||
"street": "Fake Street",
|
||||
"housenumber": "1"
|
||||
}
|
||||
}
|
||||
)";
|
||||
json j = json::parse(json_invalid_string);
|
||||
CHECK_THROWS_WITH_AS(j.at("address").at("housenumber").get<int>(),
|
||||
"[json.exception.type_error.302] (bytes 108-111) type must be number, but is string", json::type_error);
|
||||
}
|
||||
|
||||
SECTION("invalid type without positions")
|
||||
{
|
||||
const json j = "foo";
|
||||
CHECK_THROWS_WITH_AS(j.get<int>(),
|
||||
"[json.exception.type_error.302] type must be number, but is string", json::type_error);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#define JSON_DIAGNOSTICS 1
|
||||
#ifndef JSON_DIAGNOSTICS
|
||||
#define JSON_DIAGNOSTICS 1
|
||||
#endif
|
||||
#define JSON_DIAGNOSTIC_POSITIONS 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -27,8 +29,13 @@ TEST_CASE("Better diagnostics with positions")
|
||||
}
|
||||
)";
|
||||
json j = json::parse(json_invalid_string);
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(j.at("address").at("housenumber").get<int>(),
|
||||
"[json.exception.type_error.302] (/address/housenumber) (bytes 108-111) type must be number, but is string", json::type_error);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(j.at("address").at("housenumber").get<int>(),
|
||||
"[json.exception.type_error.302] (bytes 108-111) type must be number, but is string", json::type_error);
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("invalid type without positions")
|
||||
@@ -74,7 +81,12 @@ TEST_CASE("Better diagnostics with positions")
|
||||
// (/foo/bar); the position of that parent is reported in the message
|
||||
const json doc = json::parse(R"({"foo":{"bar":"a string"}})");
|
||||
const json patch = json::parse(R"([{"op":"add","path":"/foo/bar/baz","value":1}])");
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(doc.patch(patch),
|
||||
"[json.exception.out_of_range.411] (/foo/bar) (bytes 14-24) cannot add value: the JSON Patch 'add' target's parent is of type string, but must be an object or array", json::out_of_range);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(doc.patch(patch),
|
||||
"[json.exception.out_of_range.411] (bytes 14-24) cannot add value: the JSON Patch 'add' target's parent is of type string, but must be an object or array", json::out_of_range);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user