mirror of
https://github.com/nlohmann/json.git
synced 2026-11-04 11:49:22 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f54a9fee6 | ||
|
|
b12b92b30c | ||
|
|
414b4e6d27 | ||
|
|
da6d21ae24 | ||
|
|
db73a6044f |
No files matched your search
@@ -1024,7 +1024,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
|
return get_cbor_argument(len) && get_container_size(len, size, "array") && enter_cbor_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -1064,7 +1064,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
return get_cbor_argument(len) && get_container_size(len, size, "map") && enter_object(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xBF: // map (indefinite length)
|
case 0xBF: // map (indefinite length)
|
||||||
@@ -1700,7 +1700,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief narrow a definite CBOR array/map length to std::size_t
|
@brief narrow a definite CBOR or MessagePack array/map length to std::size_t
|
||||||
|
|
||||||
A definite length is rejected if it does not fit in std::size_t or if it
|
A definite length is rejected if it does not fit in std::size_t or if it
|
||||||
equals detail::unknown_size(), which is reserved to mark an indefinite-
|
equals detail::unknown_size(), which is reserved to mark an indefinite-
|
||||||
@@ -1713,7 +1713,7 @@ class binary_reader
|
|||||||
@param[in] context "array" or "map", for the error message
|
@param[in] context "array" or "map", for the error message
|
||||||
@return whether the length is usable
|
@return whether the length is usable
|
||||||
*/
|
*/
|
||||||
bool get_cbor_container_size(const std::uint64_t len, std::size_t& result, const char* context)
|
bool get_container_size(const std::uint64_t len, std::size_t& result, const char* context)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
||||||
{
|
{
|
||||||
@@ -2309,7 +2309,8 @@ class binary_reader
|
|||||||
case 0xDD: // array 32
|
case 0xDD: // array 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(len) && enter_msgpack_array(conditional_static_cast<std::size_t>(len));
|
std::size_t size{};
|
||||||
|
return get_number(len) && get_container_size(len, size, "array") && enter_msgpack_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDE: // map 16
|
case 0xDE: // map 16
|
||||||
@@ -2321,7 +2322,8 @@ class binary_reader
|
|||||||
case 0xDF: // map 32
|
case 0xDF: // map 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(len) && enter_object(conditional_static_cast<std::size_t>(len));
|
std::size_t size{};
|
||||||
|
return get_number(len) && get_container_size(len, size, "map") && enter_object(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// negative fixint
|
// negative fixint
|
||||||
@@ -3131,6 +3133,27 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief store the count of an optimized container (or the length of a string)
|
||||||
|
|
||||||
|
A count equal to npos is rejected: npos marks a container whose count is
|
||||||
|
not known, so a count of that value would be read as "no count" (compare
|
||||||
|
get_container_size()). No container can hold that many elements.
|
||||||
|
|
||||||
|
@param[in] count the count read from the input
|
||||||
|
@param[out] result set to @a count unless it is rejected
|
||||||
|
@return whether @a count is usable
|
||||||
|
*/
|
||||||
|
bool store_ubjson_count(const std::size_t count, std::size_t& result)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(count == npos))
|
||||||
|
{
|
||||||
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
|
}
|
||||||
|
result = count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
||||||
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
||||||
@@ -3161,8 +3184,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
}
|
}
|
||||||
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
return store_ubjson_count(static_cast<std::size_t>(number), result); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -3239,8 +3261,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result = conditional_static_cast<std::size_t>(number);
|
return store_ubjson_count(conditional_static_cast<std::size_t>(number), result);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -3258,8 +3279,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
}
|
}
|
||||||
result = detail::conditional_static_cast<std::size_t>(number);
|
return store_ubjson_count(detail::conditional_static_cast<std::size_t>(number), result);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
|
|||||||
@@ -116,10 +116,6 @@ inline void copy_short(CharType* dst, const CharType* src, const std::size_t len
|
|||||||
/// the number of characters an empty vector is grown to by output_vector_sink
|
/// the number of characters an empty vector is grown to by output_vector_sink
|
||||||
constexpr std::size_t min_vector_sink_capacity = 64;
|
constexpr std::size_t min_vector_sink_capacity = 64;
|
||||||
|
|
||||||
/// the number of characters output_vector_sink makes writable ahead of its
|
|
||||||
/// position at a time: 64 KiB of the vector's capacity, not all of it
|
|
||||||
constexpr std::size_t vector_sink_window = 65536;
|
|
||||||
|
|
||||||
/// @brief non-virtual output sink writing into a std::vector
|
/// @brief non-virtual output sink writing into a std::vector
|
||||||
///
|
///
|
||||||
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
||||||
@@ -133,8 +129,7 @@ constexpr std::size_t vector_sink_window = 65536;
|
|||||||
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
||||||
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
||||||
/// its own write position: it grows the vector geometrically ahead of the
|
/// its own write position: it grows the vector geometrically ahead of the
|
||||||
/// position (making @ref vector_sink_window characters of it writable at a
|
/// position and copies each write to it, so a write is a capacity check and a
|
||||||
/// time) and copies each write to it, so a write is a capacity check and a
|
|
||||||
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
||||||
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
||||||
/// that trim, so it can be moved but not copied, and the vector must not be used
|
/// that trim, so it can be moved but not copied, and the vector must not be used
|
||||||
@@ -282,17 +277,9 @@ class output_vector_sink
|
|||||||
// reallocate for every few bytes
|
// reallocate for every few bytes
|
||||||
const std::size_t written = position();
|
const std::size_t written = position();
|
||||||
const std::size_t needed = written + length;
|
const std::size_t needed = written + length;
|
||||||
const std::size_t capacity = (std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
v->resize((std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
||||||
? v->capacity()
|
? v->capacity()
|
||||||
: (std::max)(needed, v->capacity() * 2));
|
: (std::max)(needed, v->capacity() * 2)));
|
||||||
v->reserve(capacity);
|
|
||||||
// resize() zeroes the characters it adds. Zeroing all of a doubled
|
|
||||||
// capacity, megabytes of a fresh allocation, at once faults in its
|
|
||||||
// pages and evicts them from the cache before the writer gets there,
|
|
||||||
// so its stores miss again; a window ahead of the position is still
|
|
||||||
// cached when they come (the binary writers on a long array of
|
|
||||||
// numbers: 4-26 % faster on x86, 1-2.5 % on Apple M1)
|
|
||||||
v->resize((std::min)(capacity, (std::max)(needed, written + vector_sink_window)));
|
|
||||||
cursor = v->data() + written;
|
cursor = v->data() + written;
|
||||||
limit = v->data() + v->size();
|
limit = v->data() + v->size();
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+35
-28
@@ -19039,7 +19039,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
|
return get_cbor_argument(len) && get_container_size(len, size, "array") && enter_cbor_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -19079,7 +19079,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
return get_cbor_argument(len) && get_container_size(len, size, "map") && enter_object(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xBF: // map (indefinite length)
|
case 0xBF: // map (indefinite length)
|
||||||
@@ -19715,7 +19715,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief narrow a definite CBOR array/map length to std::size_t
|
@brief narrow a definite CBOR or MessagePack array/map length to std::size_t
|
||||||
|
|
||||||
A definite length is rejected if it does not fit in std::size_t or if it
|
A definite length is rejected if it does not fit in std::size_t or if it
|
||||||
equals detail::unknown_size(), which is reserved to mark an indefinite-
|
equals detail::unknown_size(), which is reserved to mark an indefinite-
|
||||||
@@ -19728,7 +19728,7 @@ class binary_reader
|
|||||||
@param[in] context "array" or "map", for the error message
|
@param[in] context "array" or "map", for the error message
|
||||||
@return whether the length is usable
|
@return whether the length is usable
|
||||||
*/
|
*/
|
||||||
bool get_cbor_container_size(const std::uint64_t len, std::size_t& result, const char* context)
|
bool get_container_size(const std::uint64_t len, std::size_t& result, const char* context)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
||||||
{
|
{
|
||||||
@@ -20324,7 +20324,8 @@ class binary_reader
|
|||||||
case 0xDD: // array 32
|
case 0xDD: // array 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(len) && enter_msgpack_array(conditional_static_cast<std::size_t>(len));
|
std::size_t size{};
|
||||||
|
return get_number(len) && get_container_size(len, size, "array") && enter_msgpack_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDE: // map 16
|
case 0xDE: // map 16
|
||||||
@@ -20336,7 +20337,8 @@ class binary_reader
|
|||||||
case 0xDF: // map 32
|
case 0xDF: // map 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(len) && enter_object(conditional_static_cast<std::size_t>(len));
|
std::size_t size{};
|
||||||
|
return get_number(len) && get_container_size(len, size, "map") && enter_object(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// negative fixint
|
// negative fixint
|
||||||
@@ -21146,6 +21148,27 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief store the count of an optimized container (or the length of a string)
|
||||||
|
|
||||||
|
A count equal to npos is rejected: npos marks a container whose count is
|
||||||
|
not known, so a count of that value would be read as "no count" (compare
|
||||||
|
get_container_size()). No container can hold that many elements.
|
||||||
|
|
||||||
|
@param[in] count the count read from the input
|
||||||
|
@param[out] result set to @a count unless it is rejected
|
||||||
|
@return whether @a count is usable
|
||||||
|
*/
|
||||||
|
bool store_ubjson_count(const std::size_t count, std::size_t& result)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(count == npos))
|
||||||
|
{
|
||||||
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
|
}
|
||||||
|
result = count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
||||||
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
||||||
@@ -21176,8 +21199,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
}
|
}
|
||||||
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
return store_ubjson_count(static_cast<std::size_t>(number), result); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -21254,8 +21276,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result = conditional_static_cast<std::size_t>(number);
|
return store_ubjson_count(conditional_static_cast<std::size_t>(number), result);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -21273,8 +21294,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
}
|
}
|
||||||
result = detail::conditional_static_cast<std::size_t>(number);
|
return store_ubjson_count(detail::conditional_static_cast<std::size_t>(number), result);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
@@ -27491,10 +27511,6 @@ inline void copy_short(CharType* dst, const CharType* src, const std::size_t len
|
|||||||
/// the number of characters an empty vector is grown to by output_vector_sink
|
/// the number of characters an empty vector is grown to by output_vector_sink
|
||||||
constexpr std::size_t min_vector_sink_capacity = 64;
|
constexpr std::size_t min_vector_sink_capacity = 64;
|
||||||
|
|
||||||
/// the number of characters output_vector_sink makes writable ahead of its
|
|
||||||
/// position at a time: 64 KiB of the vector's capacity, not all of it
|
|
||||||
constexpr std::size_t vector_sink_window = 65536;
|
|
||||||
|
|
||||||
/// @brief non-virtual output sink writing into a std::vector
|
/// @brief non-virtual output sink writing into a std::vector
|
||||||
///
|
///
|
||||||
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
||||||
@@ -27508,8 +27524,7 @@ constexpr std::size_t vector_sink_window = 65536;
|
|||||||
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
||||||
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
||||||
/// its own write position: it grows the vector geometrically ahead of the
|
/// its own write position: it grows the vector geometrically ahead of the
|
||||||
/// position (making @ref vector_sink_window characters of it writable at a
|
/// position and copies each write to it, so a write is a capacity check and a
|
||||||
/// time) and copies each write to it, so a write is a capacity check and a
|
|
||||||
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
||||||
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
||||||
/// that trim, so it can be moved but not copied, and the vector must not be used
|
/// that trim, so it can be moved but not copied, and the vector must not be used
|
||||||
@@ -27657,17 +27672,9 @@ class output_vector_sink
|
|||||||
// reallocate for every few bytes
|
// reallocate for every few bytes
|
||||||
const std::size_t written = position();
|
const std::size_t written = position();
|
||||||
const std::size_t needed = written + length;
|
const std::size_t needed = written + length;
|
||||||
const std::size_t capacity = (std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
v->resize((std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
||||||
? v->capacity()
|
? v->capacity()
|
||||||
: (std::max)(needed, v->capacity() * 2));
|
: (std::max)(needed, v->capacity() * 2)));
|
||||||
v->reserve(capacity);
|
|
||||||
// resize() zeroes the characters it adds. Zeroing all of a doubled
|
|
||||||
// capacity, megabytes of a fresh allocation, at once faults in its
|
|
||||||
// pages and evicts them from the cache before the writer gets there,
|
|
||||||
// so its stores miss again; a window ahead of the position is still
|
|
||||||
// cached when they come (the binary writers on a long array of
|
|
||||||
// numbers: 4-26 % faster on x86, 1-2.5 % on Apple M1)
|
|
||||||
v->resize((std::min)(capacity, (std::max)(needed, written + vector_sink_window)));
|
|
||||||
cursor = v->data() + written;
|
cursor = v->data() + written;
|
||||||
limit = v->data() + v->size();
|
limit = v->data() + v->size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,3 +97,23 @@ TEST_CASE("CBOR")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("MessagePack")
|
||||||
|
{
|
||||||
|
SECTION("parse errors")
|
||||||
|
{
|
||||||
|
SECTION("array/map size equal to the unknown-size sentinel")
|
||||||
|
{
|
||||||
|
// on 32-bit platforms an array 32/map 32 length of 0xFFFFFFFF aliases unknown_size()
|
||||||
|
std::vector<uint8_t> const varr = {0xDD, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
std::vector<uint8_t> const vmap = {0xDF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(varr), "[json.exception.out_of_range.408] syntax error while parsing MessagePack size: excessive array size", json::out_of_range&);
|
||||||
|
CHECK(json::from_msgpack(varr, true, false).is_discarded());
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vmap), "[json.exception.out_of_range.408] syntax error while parsing MessagePack size: excessive map size", json::out_of_range&);
|
||||||
|
CHECK(json::from_msgpack(vmap, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,14 +50,6 @@ std::vector<json> test_values()
|
|||||||
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
|
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// several times vector_sink_window characters in every format: the vector
|
|
||||||
// sink makes the capacity it grows to writable a window at a time
|
|
||||||
json many_windows = json::array();
|
|
||||||
for (std::uint64_t i = 0; i < 30000; ++i)
|
|
||||||
{
|
|
||||||
many_windows.push_back((i * 0x9E3779B97F4A7C15u) >> 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
json(nullptr), json(true), json(false),
|
json(nullptr), json(true), json(false),
|
||||||
@@ -69,7 +61,7 @@ std::vector<json> test_values()
|
|||||||
json::array(), json::object(),
|
json::array(), json::object(),
|
||||||
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
|
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
|
||||||
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
|
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
|
||||||
big_array, big_object, large_payloads, many_windows,
|
big_array, big_object, large_payloads,
|
||||||
json::binary(std::vector<std::uint8_t>(5000, 0x42))
|
json::binary(std::vector<std::uint8_t>(5000, 0x42))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3237,14 +3237,34 @@ TEST_CASE("BJData")
|
|||||||
#endif
|
#endif
|
||||||
CHECK(json::from_bjdata(vM, true, false).is_discarded());
|
CHECK(json::from_bjdata(vM, true, false).is_discarded());
|
||||||
|
|
||||||
#if SIZE_MAX != 0xffffffff
|
// the first dimension is 2^64-1, which is rejected as a count (see below)
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: excessive ndarray size caused overflow", json::out_of_range&);
|
|
||||||
#else
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
||||||
#endif
|
|
||||||
CHECK(json::from_bjdata(vMX, true, false).is_discarded());
|
CHECK(json::from_bjdata(vMX, true, false).is_discarded());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("optimized array: count equal to SIZE_MAX")
|
||||||
|
{
|
||||||
|
// the reader marks a container without a count by SIZE_MAX, so
|
||||||
|
// a count of that value must not be read as "no count"
|
||||||
|
// (it used to parse as [1])
|
||||||
|
json _;
|
||||||
|
#if SIZE_MAX != 0xffffffff
|
||||||
|
std::vector<uint8_t> const vM = {'[', '#', 'M', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
||||||
|
CHECK(json::from_bjdata(vM, true, false).is_discarded());
|
||||||
|
// one less is an ordinary excessive count
|
||||||
|
std::vector<uint8_t> const vM1 = {'[', '#', 'M', 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM1), "[json.exception.out_of_range.408] excessive array size: 18446744073709551614", json::out_of_range&);
|
||||||
|
#else
|
||||||
|
std::vector<uint8_t> const vm = {'[', '#', 'm', 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vm), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
||||||
|
CHECK(json::from_bjdata(vm, true, false).is_discarded());
|
||||||
|
std::vector<uint8_t> const vL = {'[', '#', 'L', 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 'i', 0x01, ']'};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vL), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
||||||
|
CHECK(json::from_bjdata(vL, true, false).is_discarded());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("optimized array: integer value overflow")
|
SECTION("optimized array: integer value overflow")
|
||||||
{
|
{
|
||||||
#if SIZE_MAX == 0xffffffff
|
#if SIZE_MAX == 0xffffffff
|
||||||
|
|||||||
@@ -1009,7 +1009,7 @@ TEST_CASE("regression test - excessive binary container size honors allow_except
|
|||||||
// allow_exceptions=true (the default) must still throw exactly as before.
|
// allow_exceptions=true (the default) must still throw exactly as before.
|
||||||
// The exact message text is not checked here: on platforms where
|
// The exact message text is not checked here: on platforms where
|
||||||
// std::size_t is 32-bit, the CBOR reader's own length-narrowing check
|
// std::size_t is 32-bit, the CBOR reader's own length-narrowing check
|
||||||
// (get_cbor_container_size(), unrelated to this fix) intercepts a
|
// (get_container_size(), unrelated to this fix) intercepts a
|
||||||
// declared length of 2^63 before it ever reaches the check this test
|
// declared length of 2^63 before it ever reaches the check this test
|
||||||
// targets, with different (but equally valid, and already correct)
|
// targets, with different (but equally valid, and already correct)
|
||||||
// wording -- see unit-cbor.cpp for coverage of that message.
|
// wording -- see unit-cbor.cpp for coverage of that message.
|
||||||
|
|||||||
@@ -2385,6 +2385,18 @@ TEST_CASE("UBJSON optimized arrays of a valueless type are bounded")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("a count equal to SIZE_MAX is rejected")
|
||||||
|
{
|
||||||
|
// the reader marks a container without a count by SIZE_MAX, so a
|
||||||
|
// count of that value must not be read as "no count"; UBJSON's
|
||||||
|
// largest count type (int64) can only reach it where size_t is 32 bits
|
||||||
|
#if SIZE_MAX == 0xffffffff
|
||||||
|
const std::vector<uint8_t> input = {'[', '#', 'L', 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(input), "[json.exception.out_of_range.408] syntax error while parsing UBJSON size: integer value overflow", json::out_of_range&);
|
||||||
|
CHECK(json::from_ubjson(input, true, false).is_discarded());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("ordinary counts are unaffected")
|
SECTION("ordinary counts are unaffected")
|
||||||
{
|
{
|
||||||
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'Z', '#', 'i', 3})) == json({nullptr, nullptr, nullptr}));
|
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'Z', '#', 'i', 3})) == json({nullptr, nullptr, nullptr}));
|
||||||
|
|||||||
Reference in new issue
Block a user