mirror of
https://github.com/nlohmann/json.git
synced 2026-09-27 10:10:29 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91c06cae20 | ||
|
|
67bb400b3c | ||
|
|
431a7864b4 | ||
|
|
35173b9d2d |
@@ -34,6 +34,15 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a string, binary
|
||||||
|
value, array, or object exceeds 4294967295, the maximum MessagePack can store; example:
|
||||||
|
`"MessagePack length 4294967296 exceeds maximum of 4294967295"`
|
||||||
|
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
||||||
|
exceeds 255, the maximum of the MessagePack ext type; example:
|
||||||
|
`"subtype 70000 is too large for the MessagePack ext type (max 255)"`
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the size of the JSON value `j`.
|
Linear in the size of the JSON value `j`.
|
||||||
@@ -65,3 +74,4 @@ Linear in the size of the JSON value `j`.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 2.0.9.
|
- Added in version 2.0.9.
|
||||||
|
- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0.
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ specification:
|
|||||||
- arrays with more than 4294967295 elements
|
- arrays with more than 4294967295 elements
|
||||||
- objects with more than 4294967295 elements
|
- objects with more than 4294967295 elements
|
||||||
|
|
||||||
|
Serializing such a value throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412).
|
||||||
|
|
||||||
!!! info "NaN/infinity handling"
|
!!! info "NaN/infinity handling"
|
||||||
|
|
||||||
`NaN`, `Infinity`, and `-Infinity` are serialized as a MessagePack float 32 (type 0xCA, 5 bytes total),
|
`NaN`, `Infinity`, and `-Infinity` are serialized as a MessagePack float 32 (type 0xCA, 5 bytes total),
|
||||||
|
|||||||
@@ -932,19 +932,25 @@ A JSON Patch `add` operation cannot be applied because the target location's par
|
|||||||
|
|
||||||
### json.exception.out_of_range.412
|
### json.exception.out_of_range.412
|
||||||
|
|
||||||
BSON stores the length of documents, arrays, strings, and binary values in a signed 32-bit integer. This exception is thrown when a value is too large to be described by such a length field.
|
BSON stores the length of documents, arrays, strings, and binary values in a signed 32-bit integer, and MessagePack
|
||||||
|
stores the length of strings, binary values, arrays, and objects in at most an unsigned 32-bit integer. This exception
|
||||||
|
is thrown when a value is too large to be described by such a length field.
|
||||||
|
|
||||||
!!! failure "Example message"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
```
|
```
|
||||||
BSON length 2147483661 exceeds maximum of 2147483647
|
BSON length 2147483661 exceeds maximum of 2147483647
|
||||||
```
|
```
|
||||||
|
```
|
||||||
|
MessagePack length 4294967296 exceeds maximum of 4294967295
|
||||||
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
This exception was added in version 3.13.0. Before that, the length was silently truncated, and
|
This exception was added in version 3.13.0. Before that, the BSON length was silently truncated, and
|
||||||
[`to_bson`](../api/basic_json/to_bson.md) produced documents with negative length prefixes that
|
[`to_bson`](../api/basic_json/to_bson.md) produced documents with negative length prefixes that
|
||||||
[`from_bson`](../api/basic_json/from_bson.md) rejected.
|
[`from_bson`](../api/basic_json/from_bson.md) rejected; [`to_msgpack`](../api/basic_json/to_msgpack.md) wrote such
|
||||||
|
a value without any length, producing output that could not be read back.
|
||||||
|
|
||||||
### json.exception.out_of_range.413
|
### json.exception.out_of_range.413
|
||||||
|
|
||||||
|
|||||||
@@ -466,6 +466,23 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief check that @a length fits into the 32 bits that MessagePack stores
|
||||||
|
the length of a string, binary value, array, or object in
|
||||||
|
@return the length as an unsigned 32-bit integer
|
||||||
|
@throw out_of_range.412 if @a length exceeds the range of std::uint32_t
|
||||||
|
*/
|
||||||
|
static std::uint32_t to_msgpack_length(const std::size_t length, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length)))
|
||||||
|
{
|
||||||
|
JSON_THROW(out_of_range::create(412, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j));
|
||||||
|
}
|
||||||
|
|
||||||
|
static_cast<void>(j);
|
||||||
|
return static_cast<std::uint32_t>(length);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
*/
|
*/
|
||||||
@@ -606,7 +623,7 @@ class binary_writer
|
|||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
// step 1: write control byte and the string length
|
// step 1: write control byte and the string length
|
||||||
const auto N = j.m_data.m_value.string->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.string->size(), j);
|
||||||
if (N <= 31)
|
if (N <= 31)
|
||||||
{
|
{
|
||||||
// fixstr
|
// fixstr
|
||||||
@@ -624,7 +641,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(0xDA));
|
oa.write_character(to_char_type(0xDA));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
// str 32
|
// str 32
|
||||||
oa.write_character(to_char_type(0xDB));
|
oa.write_character(to_char_type(0xDB));
|
||||||
@@ -641,7 +658,7 @@ class binary_writer
|
|||||||
case value_t::array:
|
case value_t::array:
|
||||||
{
|
{
|
||||||
// step 1: write control byte and the array size
|
// step 1: write control byte and the array size
|
||||||
const auto N = j.m_data.m_value.array->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.array->size(), j);
|
||||||
if (N <= 15)
|
if (N <= 15)
|
||||||
{
|
{
|
||||||
// fixarray
|
// fixarray
|
||||||
@@ -653,7 +670,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(0xDC));
|
oa.write_character(to_char_type(0xDC));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
// array 32
|
// array 32
|
||||||
oa.write_character(to_char_type(0xDD));
|
oa.write_character(to_char_type(0xDD));
|
||||||
@@ -675,7 +692,7 @@ class binary_writer
|
|||||||
const bool use_ext = j.m_data.m_value.binary->has_subtype();
|
const bool use_ext = j.m_data.m_value.binary->has_subtype();
|
||||||
|
|
||||||
// step 1: write control byte and the byte string length
|
// step 1: write control byte and the byte string length
|
||||||
const auto N = j.m_data.m_value.binary->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.binary->size(), j);
|
||||||
if (N <= (std::numeric_limits<std::uint8_t>::max)())
|
if (N <= (std::numeric_limits<std::uint8_t>::max)())
|
||||||
{
|
{
|
||||||
std::uint8_t output_type{};
|
std::uint8_t output_type{};
|
||||||
@@ -727,7 +744,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(output_type));
|
oa.write_character(to_char_type(output_type));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
const std::uint8_t output_type = use_ext
|
const std::uint8_t output_type = use_ext
|
||||||
? 0xC9 // ext 32
|
? 0xC9 // ext 32
|
||||||
@@ -759,7 +776,7 @@ class binary_writer
|
|||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
// step 1: write control byte and the object size
|
// step 1: write control byte and the object size
|
||||||
const auto N = j.m_data.m_value.object->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.object->size(), j);
|
||||||
if (N <= 15)
|
if (N <= 15)
|
||||||
{
|
{
|
||||||
// fixmap
|
// fixmap
|
||||||
@@ -771,7 +788,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(0xDE));
|
oa.write_character(to_char_type(0xDE));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
// map 32
|
// map 32
|
||||||
oa.write_character(to_char_type(0xDF));
|
oa.write_character(to_char_type(0xDF));
|
||||||
|
|||||||
@@ -19931,6 +19931,23 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief check that @a length fits into the 32 bits that MessagePack stores
|
||||||
|
the length of a string, binary value, array, or object in
|
||||||
|
@return the length as an unsigned 32-bit integer
|
||||||
|
@throw out_of_range.412 if @a length exceeds the range of std::uint32_t
|
||||||
|
*/
|
||||||
|
static std::uint32_t to_msgpack_length(const std::size_t length, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::uint32_t>(length)))
|
||||||
|
{
|
||||||
|
JSON_THROW(out_of_range::create(412, concat("MessagePack length ", std::to_string(length), " exceeds maximum of ", std::to_string((std::numeric_limits<std::uint32_t>::max)())), &j));
|
||||||
|
}
|
||||||
|
|
||||||
|
static_cast<void>(j);
|
||||||
|
return static_cast<std::uint32_t>(length);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
*/
|
*/
|
||||||
@@ -20071,7 +20088,7 @@ class binary_writer
|
|||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
// step 1: write control byte and the string length
|
// step 1: write control byte and the string length
|
||||||
const auto N = j.m_data.m_value.string->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.string->size(), j);
|
||||||
if (N <= 31)
|
if (N <= 31)
|
||||||
{
|
{
|
||||||
// fixstr
|
// fixstr
|
||||||
@@ -20089,7 +20106,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(0xDA));
|
oa.write_character(to_char_type(0xDA));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
// str 32
|
// str 32
|
||||||
oa.write_character(to_char_type(0xDB));
|
oa.write_character(to_char_type(0xDB));
|
||||||
@@ -20106,7 +20123,7 @@ class binary_writer
|
|||||||
case value_t::array:
|
case value_t::array:
|
||||||
{
|
{
|
||||||
// step 1: write control byte and the array size
|
// step 1: write control byte and the array size
|
||||||
const auto N = j.m_data.m_value.array->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.array->size(), j);
|
||||||
if (N <= 15)
|
if (N <= 15)
|
||||||
{
|
{
|
||||||
// fixarray
|
// fixarray
|
||||||
@@ -20118,7 +20135,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(0xDC));
|
oa.write_character(to_char_type(0xDC));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
// array 32
|
// array 32
|
||||||
oa.write_character(to_char_type(0xDD));
|
oa.write_character(to_char_type(0xDD));
|
||||||
@@ -20140,7 +20157,7 @@ class binary_writer
|
|||||||
const bool use_ext = j.m_data.m_value.binary->has_subtype();
|
const bool use_ext = j.m_data.m_value.binary->has_subtype();
|
||||||
|
|
||||||
// step 1: write control byte and the byte string length
|
// step 1: write control byte and the byte string length
|
||||||
const auto N = j.m_data.m_value.binary->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.binary->size(), j);
|
||||||
if (N <= (std::numeric_limits<std::uint8_t>::max)())
|
if (N <= (std::numeric_limits<std::uint8_t>::max)())
|
||||||
{
|
{
|
||||||
std::uint8_t output_type{};
|
std::uint8_t output_type{};
|
||||||
@@ -20192,7 +20209,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(output_type));
|
oa.write_character(to_char_type(output_type));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
const std::uint8_t output_type = use_ext
|
const std::uint8_t output_type = use_ext
|
||||||
? 0xC9 // ext 32
|
? 0xC9 // ext 32
|
||||||
@@ -20224,7 +20241,7 @@ class binary_writer
|
|||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
// step 1: write control byte and the object size
|
// step 1: write control byte and the object size
|
||||||
const auto N = j.m_data.m_value.object->size();
|
const auto N = to_msgpack_length(j.m_data.m_value.object->size(), j);
|
||||||
if (N <= 15)
|
if (N <= 15)
|
||||||
{
|
{
|
||||||
// fixmap
|
// fixmap
|
||||||
@@ -20236,7 +20253,7 @@ class binary_writer
|
|||||||
oa.write_character(to_char_type(0xDE));
|
oa.write_character(to_char_type(0xDE));
|
||||||
write_number(static_cast<std::uint16_t>(N));
|
write_number(static_cast<std::uint16_t>(N));
|
||||||
}
|
}
|
||||||
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
|
else
|
||||||
{
|
{
|
||||||
// map 32
|
// map 32
|
||||||
oa.write_character(to_char_type(0xDF));
|
oa.write_character(to_char_type(0xDF));
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using nlohmann::json;
|
|||||||
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <cstdint> // SIZE_MAX, UINT32_MAX
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@@ -2150,3 +2151,79 @@ TEST_CASE("MessagePack with std::byte")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// types that report a size beyond UINT32_MAX without allocating that much
|
||||||
|
// memory, so the MessagePack length limit can be tested cheaply; see the
|
||||||
|
// similar types in unit-bson.cpp
|
||||||
|
std::size_t beyond_uint32_size()
|
||||||
|
{
|
||||||
|
return static_cast<std::size_t>((std::numeric_limits<std::uint32_t>::max)()) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class beyond_uint32_binary_t : public std::vector<std::uint8_t>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using std::vector<std::uint8_t>::vector;
|
||||||
|
|
||||||
|
size_type size() const noexcept // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
{
|
||||||
|
return beyond_uint32_size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// with clang and libstdc++ 10, the std::filesystem::path conversion that
|
||||||
|
// C++17 builds consider for every string type is ambiguous for a class
|
||||||
|
// derived from std::string, so the string case is not tested there
|
||||||
|
#if !(defined(__clang__) && defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 11)
|
||||||
|
#define JSON_TEST_BEYOND_UINT32_STRING 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JSON_TEST_BEYOND_UINT32_STRING
|
||||||
|
class beyond_uint32_string_t : public std::string
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using std::string::string;
|
||||||
|
|
||||||
|
size_type size() const noexcept // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
{
|
||||||
|
return beyond_uint32_size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using beyond_uint32_string_json = nlohmann::basic_json <
|
||||||
|
std::map, std::vector, beyond_uint32_string_t, bool, std::int64_t, std::uint64_t,
|
||||||
|
double, std::allocator, nlohmann::adl_serializer, std::vector<std::uint8_t>, void >;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using beyond_uint32_binary_json = nlohmann::basic_json <
|
||||||
|
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||||
|
double, std::allocator, nlohmann::adl_serializer, beyond_uint32_binary_t, void >;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("MessagePack lengths beyond UINT32_MAX cannot be serialized")
|
||||||
|
{
|
||||||
|
// MessagePack stores the length of a string, binary value, array, or
|
||||||
|
// object in at most 32 bits; a larger one used to be written without any
|
||||||
|
// length at all
|
||||||
|
#if SIZE_MAX > UINT32_MAX
|
||||||
|
{
|
||||||
|
const char* const expected = "[json.exception.out_of_range.412] MessagePack length 4294967296 exceeds maximum of 4294967295";
|
||||||
|
|
||||||
|
const beyond_uint32_binary_json binary = beyond_uint32_binary_json::binary(beyond_uint32_binary_t{});
|
||||||
|
CHECK_THROWS_WITH_AS(beyond_uint32_binary_json::to_msgpack(binary), expected, beyond_uint32_binary_json::out_of_range&);
|
||||||
|
|
||||||
|
const beyond_uint32_binary_json ext = beyond_uint32_binary_json::binary(beyond_uint32_binary_t{}, 42);
|
||||||
|
CHECK_THROWS_WITH_AS(beyond_uint32_binary_json::to_msgpack(ext), expected, beyond_uint32_binary_json::out_of_range&);
|
||||||
|
|
||||||
|
#ifdef JSON_TEST_BEYOND_UINT32_STRING
|
||||||
|
// created from its type rather than from a beyond_uint32_string_t:
|
||||||
|
// that would consider the std::filesystem::path conversion, which
|
||||||
|
// libstdc++ 10 cannot decide for a class derived from std::string
|
||||||
|
const beyond_uint32_string_json string(beyond_uint32_string_json::value_t::string);
|
||||||
|
CHECK_THROWS_WITH_AS(beyond_uint32_string_json::to_msgpack(string), expected, beyond_uint32_string_json::out_of_range&);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user