mirror of
https://github.com/nlohmann/json.git
synced 2026-10-31 18:05:44 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c588321c5 | ||
|
|
cff8548496 |
No files matched your search
@@ -465,15 +465,17 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t element_type_parse_position = chars_read;
|
const std::size_t element_type_parse_position = chars_read;
|
||||||
|
// an array's elements are named "0", "1", ... in the wire format,
|
||||||
|
// and those names are not passed on: they are skipped unless
|
||||||
|
// error_handler_t::strict must reject ill-formed UTF-8 in them
|
||||||
|
const bool is_object = container_stack.back().is_object;
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key, is_object || error_handler == error_handler_t::strict)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// an array's elements are named "0", "1", ... in the wire format,
|
if (is_object && !sax->key(key))
|
||||||
// and those names are not passed on
|
|
||||||
if (container_stack.back().is_object && !sax->key(key))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -489,14 +491,18 @@ class binary_reader
|
|||||||
@brief Parses a C-style string from the BSON input.
|
@brief Parses a C-style string from the BSON input.
|
||||||
@param[in,out] result A reference to the string variable where the read
|
@param[in,out] result A reference to the string variable where the read
|
||||||
string is to be stored.
|
string is to be stored.
|
||||||
|
@param[in] store whether the characters are needed; if not (the names of
|
||||||
|
an array's elements), they are skipped and @a result may
|
||||||
|
be left unchanged, and they are not checked against
|
||||||
|
@ref error_handler
|
||||||
@return `true` if the \\x00-byte indicating the end of the string was
|
@return `true` if the \\x00-byte indicating the end of the string was
|
||||||
encountered before the EOF; false` indicates an unexpected EOF.
|
encountered before the EOF; false` indicates an unexpected EOF.
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr(string_t& result)
|
bool get_bson_cstr(string_t& result, const bool store = true)
|
||||||
{
|
{
|
||||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
if (get_bson_cstr_bulk(result, store, std::integral_constant<bool, bulk_scan> {}))
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = std::back_inserter(result);
|
auto out = std::back_inserter(result);
|
||||||
@@ -509,7 +515,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (current == 0x00)
|
if (current == 0x00)
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
*out++ = static_cast<typename string_t::value_type>(current);
|
*out++ = static_cast<typename string_t::value_type>(current);
|
||||||
}
|
}
|
||||||
@@ -519,10 +525,11 @@ class binary_reader
|
|||||||
@brief read a C-style string from contiguous input in one step
|
@brief read a C-style string from contiguous input in one step
|
||||||
|
|
||||||
@param[in,out] result the string to append to
|
@param[in,out] result the string to append to
|
||||||
|
@param[in] store whether to append the characters to @a result
|
||||||
@return whether the string was read; if the input has no \\x00-byte, nothing
|
@return whether the string was read; if the input has no \\x00-byte, nothing
|
||||||
is read, and @ref get_bson_cstr reports the end of the input
|
is read, and @ref get_bson_cstr reports the end of the input
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
bool get_bson_cstr_bulk(string_t& result, const bool store, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
const std::size_t remaining = ia.bulk_remaining();
|
const std::size_t remaining = ia.bulk_remaining();
|
||||||
if (remaining == 0)
|
if (remaining == 0)
|
||||||
@@ -530,18 +537,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
// a plain loop rather than std::memchr: most keys are short (array
|
const std::size_t length = find_zero_byte(data, remaining);
|
||||||
// indices are keys, too), and the call would cost more than it saves
|
|
||||||
std::size_t length = 0;
|
|
||||||
while (length < remaining && data[length] != 0x00)
|
|
||||||
{
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (length == remaining)
|
if (length == remaining)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (store)
|
||||||
|
{
|
||||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
||||||
|
}
|
||||||
// consume the string and its \x00-byte, as the byte-wise path does
|
// consume the string and its \x00-byte, as the byte-wise path does
|
||||||
ia.bulk_skip(length + 1);
|
ia.bulk_skip(length + 1);
|
||||||
chars_read += length + 1;
|
chars_read += length + 1;
|
||||||
@@ -550,7 +554,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// input that is not contiguous: C-style strings are read byte by byte
|
/// input that is not contiguous: C-style strings are read byte by byte
|
||||||
bool get_bson_cstr_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept
|
bool get_bson_cstr_bulk(string_t& /*result*/, const bool /*store*/, std::false_type /*bulk*/) const noexcept
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,34 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the index of the first 0x00 byte in [data, data+n), or n if there is
|
||||||
|
// none; scans 8 bytes at a time (BSON's C-style strings)
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::size_t find_zero_byte(const unsigned char* data, std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||||
|
constexpr std::uint64_t high = 0x8080808080808080ull;
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i + 8 <= n; i += 8)
|
||||||
|
{
|
||||||
|
const std::uint64_t v = read_eight_bytes(data + i);
|
||||||
|
const std::uint64_t zero = (v - ones) & ~v & high;
|
||||||
|
if (zero != 0)
|
||||||
|
{
|
||||||
|
// the lowest flagged byte is the first zero byte (see
|
||||||
|
// find_string_special())
|
||||||
|
return i + (static_cast<std::size_t>(count_trailing_zeros(zero)) / 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < n; ++i)
|
||||||
|
{
|
||||||
|
if (data[i] == 0x00)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// classify a byte as one the serializer must NOT copy verbatim when
|
// classify a byte as one the serializer must NOT copy verbatim when
|
||||||
// ensure_ascii is requested: the closing quote, an escape, a control character
|
// ensure_ascii is requested: the closing quote, an escape, a control character
|
||||||
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
||||||
|
|||||||
@@ -1977,23 +1977,6 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 17 to 20 digits: the first one to four, then twice eight
|
|
||||||
if (magnitude_fits_64_bits(abs_value, std::integral_constant<bool, magnitude_has_at_most_64_bits>()))
|
|
||||||
{
|
|
||||||
const auto value = static_cast<std::uint64_t>(abs_value);
|
|
||||||
const std::uint64_t top = value / 10000000000000000u; // 1 to 1844
|
|
||||||
const std::uint64_t low16 = value - (top * 10000000000000000u);
|
|
||||||
const std::uint64_t middle = low16 / 100000000u;
|
|
||||||
const std::uint64_t first = dtoa_impl::eight_digit_bytes(top);
|
|
||||||
const auto leading = static_cast<unsigned>(count_leading_zeros(first) / 8); // (first is not 0)
|
|
||||||
char* const p = buffer_ptr + n_chars;
|
|
||||||
dtoa_impl::store_msb_first(p, (first << (8 * leading)) + 0x3030303030303030u);
|
|
||||||
dtoa_impl::store_msb_first(p + 8 - leading, dtoa_impl::eight_digit_bytes(middle) + 0x3030303030303030u);
|
|
||||||
dtoa_impl::store_msb_first(p + 16 - leading, dtoa_impl::eight_digit_bytes(low16 - (middle * 100000000u)) + 0x3030303030303030u);
|
|
||||||
write_buffer_pos += n_chars + 24 - leading;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
n_chars += count_digits(abs_value);
|
n_chars += count_digits(abs_value);
|
||||||
|
|
||||||
// spare 1 byte for '\0'
|
// spare 1 byte for '\0'
|
||||||
@@ -2139,23 +2122,6 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// whether every number_magnitude_t fits 64 bits
|
|
||||||
static constexpr bool magnitude_has_at_most_64_bits = std::numeric_limits<number_magnitude_t>::is_specialized
|
|
||||||
&& std::numeric_limits<number_magnitude_t>::digits <= 64;
|
|
||||||
|
|
||||||
/// whether @a x fits 64 bits: always if number_magnitude_t has at most
|
|
||||||
/// 64 bits (selected by type, as the comparison would be reported as
|
|
||||||
/// always true), else compared
|
|
||||||
static bool magnitude_fits_64_bits(const number_magnitude_t& /*unused*/, std::true_type /*unused*/) noexcept
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool magnitude_fits_64_bits(const number_magnitude_t& x, std::false_type /*unused*/) noexcept
|
|
||||||
{
|
|
||||||
return x <= 18446744073709551615u;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function for dump_integer
|
* Helper function for dump_integer
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -343,21 +343,6 @@ class builder
|
|||||||
}
|
}
|
||||||
arena_ensure(w, w_end, 4);
|
arena_ensure(w, w_end, 4);
|
||||||
w = put_utf8(w, cp);
|
w = put_utf8(w, cp);
|
||||||
// A chain of unicode escapes ("\u3042\u3044\u3046", as Python's json.dumps writes non-ASCII text) is
|
|
||||||
// decoded here, without going round the loop above for each of them. Anything else (a surrogate, a
|
|
||||||
// bad digit, fewer than six bytes) leaves p at the backslash: the loop above handles it, and reports
|
|
||||||
// the same error at the same position as it would without this loop.
|
|
||||||
while (end - p >= 6 && p[0] == '\\' && p[1] == 'u')
|
|
||||||
{
|
|
||||||
const int next = hex_codepoint(p + 2);
|
|
||||||
if (next < 0 || (next & 0xF800) == 0xD800)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p += 6;
|
|
||||||
arena_ensure(w, w_end, 4);
|
|
||||||
w = put_utf8(w, next);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
||||||
{
|
{
|
||||||
|
|||||||
Generated
+49
-51
@@ -12458,6 +12458,34 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the index of the first 0x00 byte in [data, data+n), or n if there is
|
||||||
|
// none; scans 8 bytes at a time (BSON's C-style strings)
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::size_t find_zero_byte(const unsigned char* data, std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||||
|
constexpr std::uint64_t high = 0x8080808080808080ull;
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i + 8 <= n; i += 8)
|
||||||
|
{
|
||||||
|
const std::uint64_t v = read_eight_bytes(data + i);
|
||||||
|
const std::uint64_t zero = (v - ones) & ~v & high;
|
||||||
|
if (zero != 0)
|
||||||
|
{
|
||||||
|
// the lowest flagged byte is the first zero byte (see
|
||||||
|
// find_string_special())
|
||||||
|
return i + (static_cast<std::size_t>(count_trailing_zeros(zero)) / 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < n; ++i)
|
||||||
|
{
|
||||||
|
if (data[i] == 0x00)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// classify a byte as one the serializer must NOT copy verbatim when
|
// classify a byte as one the serializer must NOT copy verbatim when
|
||||||
// ensure_ascii is requested: the closing quote, an escape, a control character
|
// ensure_ascii is requested: the closing quote, an escape, a control character
|
||||||
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
||||||
@@ -18241,15 +18269,17 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t element_type_parse_position = chars_read;
|
const std::size_t element_type_parse_position = chars_read;
|
||||||
|
// an array's elements are named "0", "1", ... in the wire format,
|
||||||
|
// and those names are not passed on: they are skipped unless
|
||||||
|
// error_handler_t::strict must reject ill-formed UTF-8 in them
|
||||||
|
const bool is_object = container_stack.back().is_object;
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key, is_object || error_handler == error_handler_t::strict)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// an array's elements are named "0", "1", ... in the wire format,
|
if (is_object && !sax->key(key))
|
||||||
// and those names are not passed on
|
|
||||||
if (container_stack.back().is_object && !sax->key(key))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -18265,14 +18295,18 @@ class binary_reader
|
|||||||
@brief Parses a C-style string from the BSON input.
|
@brief Parses a C-style string from the BSON input.
|
||||||
@param[in,out] result A reference to the string variable where the read
|
@param[in,out] result A reference to the string variable where the read
|
||||||
string is to be stored.
|
string is to be stored.
|
||||||
|
@param[in] store whether the characters are needed; if not (the names of
|
||||||
|
an array's elements), they are skipped and @a result may
|
||||||
|
be left unchanged, and they are not checked against
|
||||||
|
@ref error_handler
|
||||||
@return `true` if the \\x00-byte indicating the end of the string was
|
@return `true` if the \\x00-byte indicating the end of the string was
|
||||||
encountered before the EOF; false` indicates an unexpected EOF.
|
encountered before the EOF; false` indicates an unexpected EOF.
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr(string_t& result)
|
bool get_bson_cstr(string_t& result, const bool store = true)
|
||||||
{
|
{
|
||||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
if (get_bson_cstr_bulk(result, store, std::integral_constant<bool, bulk_scan> {}))
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = std::back_inserter(result);
|
auto out = std::back_inserter(result);
|
||||||
@@ -18285,7 +18319,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (current == 0x00)
|
if (current == 0x00)
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
*out++ = static_cast<typename string_t::value_type>(current);
|
*out++ = static_cast<typename string_t::value_type>(current);
|
||||||
}
|
}
|
||||||
@@ -18295,10 +18329,11 @@ class binary_reader
|
|||||||
@brief read a C-style string from contiguous input in one step
|
@brief read a C-style string from contiguous input in one step
|
||||||
|
|
||||||
@param[in,out] result the string to append to
|
@param[in,out] result the string to append to
|
||||||
|
@param[in] store whether to append the characters to @a result
|
||||||
@return whether the string was read; if the input has no \\x00-byte, nothing
|
@return whether the string was read; if the input has no \\x00-byte, nothing
|
||||||
is read, and @ref get_bson_cstr reports the end of the input
|
is read, and @ref get_bson_cstr reports the end of the input
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
bool get_bson_cstr_bulk(string_t& result, const bool store, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
const std::size_t remaining = ia.bulk_remaining();
|
const std::size_t remaining = ia.bulk_remaining();
|
||||||
if (remaining == 0)
|
if (remaining == 0)
|
||||||
@@ -18306,18 +18341,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
// a plain loop rather than std::memchr: most keys are short (array
|
const std::size_t length = find_zero_byte(data, remaining);
|
||||||
// indices are keys, too), and the call would cost more than it saves
|
|
||||||
std::size_t length = 0;
|
|
||||||
while (length < remaining && data[length] != 0x00)
|
|
||||||
{
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (length == remaining)
|
if (length == remaining)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (store)
|
||||||
|
{
|
||||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
||||||
|
}
|
||||||
// consume the string and its \x00-byte, as the byte-wise path does
|
// consume the string and its \x00-byte, as the byte-wise path does
|
||||||
ia.bulk_skip(length + 1);
|
ia.bulk_skip(length + 1);
|
||||||
chars_read += length + 1;
|
chars_read += length + 1;
|
||||||
@@ -18326,7 +18358,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// input that is not contiguous: C-style strings are read byte by byte
|
/// input that is not contiguous: C-style strings are read byte by byte
|
||||||
bool get_bson_cstr_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept
|
bool get_bson_cstr_bulk(string_t& /*result*/, const bool /*store*/, std::false_type /*bulk*/) const noexcept
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -36224,23 +36256,6 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 17 to 20 digits: the first one to four, then twice eight
|
|
||||||
if (magnitude_fits_64_bits(abs_value, std::integral_constant<bool, magnitude_has_at_most_64_bits>()))
|
|
||||||
{
|
|
||||||
const auto value = static_cast<std::uint64_t>(abs_value);
|
|
||||||
const std::uint64_t top = value / 10000000000000000u; // 1 to 1844
|
|
||||||
const std::uint64_t low16 = value - (top * 10000000000000000u);
|
|
||||||
const std::uint64_t middle = low16 / 100000000u;
|
|
||||||
const std::uint64_t first = dtoa_impl::eight_digit_bytes(top);
|
|
||||||
const auto leading = static_cast<unsigned>(count_leading_zeros(first) / 8); // (first is not 0)
|
|
||||||
char* const p = buffer_ptr + n_chars;
|
|
||||||
dtoa_impl::store_msb_first(p, (first << (8 * leading)) + 0x3030303030303030u);
|
|
||||||
dtoa_impl::store_msb_first(p + 8 - leading, dtoa_impl::eight_digit_bytes(middle) + 0x3030303030303030u);
|
|
||||||
dtoa_impl::store_msb_first(p + 16 - leading, dtoa_impl::eight_digit_bytes(low16 - (middle * 100000000u)) + 0x3030303030303030u);
|
|
||||||
write_buffer_pos += n_chars + 24 - leading;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
n_chars += count_digits(abs_value);
|
n_chars += count_digits(abs_value);
|
||||||
|
|
||||||
// spare 1 byte for '\0'
|
// spare 1 byte for '\0'
|
||||||
@@ -36386,23 +36401,6 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// whether every number_magnitude_t fits 64 bits
|
|
||||||
static constexpr bool magnitude_has_at_most_64_bits = std::numeric_limits<number_magnitude_t>::is_specialized
|
|
||||||
&& std::numeric_limits<number_magnitude_t>::digits <= 64;
|
|
||||||
|
|
||||||
/// whether @a x fits 64 bits: always if number_magnitude_t has at most
|
|
||||||
/// 64 bits (selected by type, as the comparison would be reported as
|
|
||||||
/// always true), else compared
|
|
||||||
static bool magnitude_fits_64_bits(const number_magnitude_t& /*unused*/, std::true_type /*unused*/) noexcept
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool magnitude_fits_64_bits(const number_magnitude_t& x, std::false_type /*unused*/) noexcept
|
|
||||||
{
|
|
||||||
return x <= 18446744073709551615u;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function for dump_integer
|
* Helper function for dump_integer
|
||||||
*
|
*
|
||||||
|
|||||||
Generated
-15
@@ -1585,21 +1585,6 @@ class builder
|
|||||||
}
|
}
|
||||||
arena_ensure(w, w_end, 4);
|
arena_ensure(w, w_end, 4);
|
||||||
w = put_utf8(w, cp);
|
w = put_utf8(w, cp);
|
||||||
// A chain of unicode escapes ("\u3042\u3044\u3046", as Python's json.dumps writes non-ASCII text) is
|
|
||||||
// decoded here, without going round the loop above for each of them. Anything else (a surrogate, a
|
|
||||||
// bad digit, fewer than six bytes) leaves p at the backslash: the loop above handles it, and reports
|
|
||||||
// the same error at the same position as it would without this loop.
|
|
||||||
while (end - p >= 6 && p[0] == '\\' && p[1] == 'u')
|
|
||||||
{
|
|
||||||
const int next = hex_codepoint(p + 2);
|
|
||||||
if (next < 0 || (next & 0xF800) == 0xD800)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p += 6;
|
|
||||||
arena_ensure(w, w_end, 4);
|
|
||||||
w = put_utf8(w, next);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
||||||
{
|
{
|
||||||
|
|||||||
+35
-2
@@ -1164,7 +1164,13 @@ TEST_CASE("BSON keys from contiguous and stream input")
|
|||||||
// contiguous input reads a key up to its \x00-byte in one step, a stream
|
// contiguous input reads a key up to its \x00-byte in one step, a stream
|
||||||
// reads it byte by byte; both must give the same value or error for the
|
// reads it byte by byte; both must give the same value or error for the
|
||||||
// complete document and for every truncation of it
|
// complete document and for every truncation of it
|
||||||
const json j = {{"", true}, {"k", {1, 2, 3}}, {std::string(40, 'x'), {{"nested key", "value"}}}};
|
json j = {{"", true}, {"k", {1, 2, 3}}, {std::string(40, 'x'), {{"nested key", "value"}}}};
|
||||||
|
// keys and array element names ("0" to "11") that end at every position of
|
||||||
|
// an 8-byte word, which contiguous input scans at a time
|
||||||
|
for (std::size_t length = 1; length <= 17; ++length)
|
||||||
|
{
|
||||||
|
j[std::string(length, static_cast<char>('a' + length))] = json::array({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, {11}});
|
||||||
|
}
|
||||||
const std::vector<std::uint8_t> bson = json::to_bson(j);
|
const std::vector<std::uint8_t> bson = json::to_bson(j);
|
||||||
CHECK(json::from_bson(bson) == j);
|
CHECK(json::from_bson(bson) == j);
|
||||||
|
|
||||||
@@ -1184,7 +1190,7 @@ TEST_CASE("BSON keys from contiguous and stream input")
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::istringstream stream(std::string(input.begin(), input.end()));
|
std::istringstream stream(std::string(reinterpret_cast<const char*>(input.data()), input.size()));
|
||||||
from_stream = json::from_bson(stream).dump();
|
from_stream = json::from_bson(stream).dump();
|
||||||
}
|
}
|
||||||
catch (const json::parse_error& e)
|
catch (const json::parse_error& e)
|
||||||
@@ -1194,6 +1200,33 @@ TEST_CASE("BSON keys from contiguous and stream input")
|
|||||||
CHECK(from_vector == from_stream);
|
CHECK(from_vector == from_stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("BSON array element names")
|
||||||
|
{
|
||||||
|
// an array's element names are not part of the value, so they are not
|
||||||
|
// passed on; error_handler_t::strict still rejects ill-formed UTF-8 in them
|
||||||
|
const std::vector<std::uint8_t> bson =
|
||||||
|
{
|
||||||
|
0x1D, 0x00, 0x00, 0x00, // size (little endian)
|
||||||
|
0x04, 'a', 0x00, // entry: array "a"
|
||||||
|
0x15, 0x00, 0x00, 0x00, // size (little endian)
|
||||||
|
0x08, 0xFF, 0x00, 0x01, // entry: boolean named "\xFF", true
|
||||||
|
0x08, '1', '2', '3', '4', '5', '6', '7', '8', '9', 0x00, 0x00, // entry: boolean named "123456789", false
|
||||||
|
0x00, // end of the array
|
||||||
|
0x00 // end of the document
|
||||||
|
};
|
||||||
|
const json expected = {{"a", {true, false}}};
|
||||||
|
|
||||||
|
CHECK(json::from_bson(bson) == expected);
|
||||||
|
CHECK(json::from_bson(bson, true, true, json::error_handler_t::replace) == expected);
|
||||||
|
CHECK(json::from_bson(bson, true, true, json::error_handler_t::ignore) == expected);
|
||||||
|
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bson(bson, true, true, json::error_handler_t::strict), "[json.exception.parse_error.113] parse error at byte 14: syntax error while parsing BSON key: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||||
|
|
||||||
|
std::istringstream stream(std::string(reinterpret_cast<const char*>(bson.data()), bson.size()));
|
||||||
|
CHECK(json::from_bson(stream) == expected);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST_CASE("Negative size of binary value")
|
TEST_CASE("Negative size of binary value")
|
||||||
|
|||||||
@@ -575,59 +575,6 @@ TEST_CASE("json_view builder: escapes between runs of text")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("json_view builder: chains of unicode escapes")
|
|
||||||
{
|
|
||||||
// Consecutive \uXXXX escapes are decoded in a loop of their own. Chains of
|
|
||||||
// every length from 1 to 40 of escapes that give one, two, and three
|
|
||||||
// bytes of UTF-8, in upper and lower case, followed by what ends a chain:
|
|
||||||
// the end of the string, text, another escape, UTF-8, and everything that
|
|
||||||
// is not a valid escape (a bad digit at every position of the last
|
|
||||||
// escape, a lone surrogate, a surrogate pair, a cut in every place).
|
|
||||||
const std::array<const char*, 8> chain = {{"\\u0041", "\\u00e4", "\\u00E4", "\\u20ac", "\\u3042", "\\uFFFF", "\\u0000", "\\u07ff"}};
|
|
||||||
const std::array<const char*, 12> ends = {{"", "abc", "\\n", "\\u00e9", "\\ud83d\\ude00", "\\ud83d", "\\ude00", "\\ud83d\\u0041", "\\u12G4", "\\u12", "\xc3\xa4", "\\"}};
|
|
||||||
std::mt19937 rng(2027); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
|
||||||
for (std::size_t length = 1; length <= 40; ++length)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 20; ++i)
|
|
||||||
{
|
|
||||||
std::string content = std::string(rng() % 20, 'a');
|
|
||||||
for (std::size_t k = 0; k < length; ++k)
|
|
||||||
{
|
|
||||||
content += chain[rng() % chain.size()];
|
|
||||||
}
|
|
||||||
const std::string whole = content;
|
|
||||||
for (const char* end : ends)
|
|
||||||
{
|
|
||||||
check_string(whole + end);
|
|
||||||
check_string(whole + end + std::string(rng() % 19, 'z'));
|
|
||||||
}
|
|
||||||
// a bad digit at every position of every escape near the end of the chain
|
|
||||||
for (std::size_t back = 1; back <= 6 && back <= length; ++back)
|
|
||||||
{
|
|
||||||
for (std::size_t digit = 2; digit < 6; ++digit)
|
|
||||||
{
|
|
||||||
std::string broken = whole;
|
|
||||||
broken[broken.size() - (6 * back) + digit] = "gG\"\x01 z"[rng() % 6];
|
|
||||||
check_string(broken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// the text cut in every place of the last three escapes
|
|
||||||
for (std::size_t cut = 0; cut <= 18 && cut <= whole.size(); ++cut)
|
|
||||||
{
|
|
||||||
check_string(whole.substr(0, whole.size() - cut));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// a chain longer than the first growth of the arena, in a key and in a value
|
|
||||||
std::string long_chain;
|
|
||||||
for (int i = 0; i < 3000; ++i)
|
|
||||||
{
|
|
||||||
long_chain += chain[static_cast<std::size_t>(i) % chain.size()];
|
|
||||||
}
|
|
||||||
check_string(long_chain);
|
|
||||||
check_string(long_chain + "\\ud83d\\ude00" + long_chain);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("json_view node integer bits")
|
TEST_CASE("json_view node integer bits")
|
||||||
{
|
{
|
||||||
using nlohmann::detail::view::integer_bits;
|
using nlohmann::detail::view::integer_bits;
|
||||||
|
|||||||
@@ -443,30 +443,6 @@ TEST_CASE("json_view from_json")
|
|||||||
CHECK(d.root().materialize().get<point>().x == 3);
|
CHECK(d.root().materialize().get<point>().x == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("escaped keys: members are found by their decoded names")
|
|
||||||
{
|
|
||||||
// keys are decoded before they are compared, as by parse(); a lookup
|
|
||||||
// that compares the raw bytes of the source would miss these members
|
|
||||||
const json_document d = json_document::parse(R"([{"\u0078": 1, "y": 2}, {"V\u0061lue": 3, "Te\u0078t": "t"}, {"\u0062": "escaped"}, {"x": 1, "\u0078": 3, "y": 2}])");
|
|
||||||
const json j = d.root().materialize();
|
|
||||||
CHECK(d.root()[0].get<point>().x == 1);
|
|
||||||
CHECK(d.root()[0].get<point>().y == 2);
|
|
||||||
CHECK((d.root()[0].get<point>() == j[0].get<point>()));
|
|
||||||
CHECK(d.root()[1].get<with_names>().value == 3);
|
|
||||||
CHECK(d.root()[1].get<with_names>().text == "t");
|
|
||||||
CHECK(d.root()[2].get<with_defaults>().b == "escaped");
|
|
||||||
CHECK(d.root()[2].get<with_defaults>().a == 1);
|
|
||||||
// a duplicate key written once plain and once escaped: still the first member
|
|
||||||
CHECK(d.root()[3].get<point>().x == 1);
|
|
||||||
CHECK(j[3].get<point>().x == 3);
|
|
||||||
#if !defined(JSON_NOEXCEPTION) && !JSON_DIAGNOSTICS && !JSON_DIAGNOSTIC_POSITIONS
|
|
||||||
// an escaped backslash is part of the decoded key: "x\\" is not "x"
|
|
||||||
const json_document e = json_document::parse(R"({"x\\": 1, "y": 2})");
|
|
||||||
CHECK(exception_of_call([&] { static_cast<void>(e.root().get<point>()); }) == exception_of_call([&] { static_cast<void>(e.root().materialize().get<point>()); }));
|
|
||||||
CHECK(exception_of_call([&] { static_cast<void>(e.root().get<point>()); }) == "[json.exception.out_of_range.403] key 'x' not found");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("editable documents and ordered_json")
|
SECTION("editable documents and ordered_json")
|
||||||
{
|
{
|
||||||
json_editable_document e = json_editable_document::parse(R"({"x": 1, "y": 2})");
|
json_editable_document e = json_editable_document::parse(R"({"x": 1, "y": 2})");
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ using nlohmann::json;
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <limits>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -249,51 +248,6 @@ TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("integers of every digit count are written like std::to_string")
|
|
||||||
{
|
|
||||||
// dump_integer() writes up to 16 digits in two groups of eight and 17 to
|
|
||||||
// 20 digits as one to four digits and two groups of eight
|
|
||||||
std::vector<std::uint64_t> values;
|
|
||||||
std::uint64_t power = 1;
|
|
||||||
for (int digits = 1; digits <= 20; ++digits)
|
|
||||||
{
|
|
||||||
values.push_back(power); // 1 followed by zeros
|
|
||||||
values.push_back(power + 1);
|
|
||||||
values.push_back((power * 2) - 1);
|
|
||||||
if (digits < 20)
|
|
||||||
{
|
|
||||||
values.push_back((power * 10) - 1); // all 9s
|
|
||||||
values.push_back((power * 10) - power); // 9 followed by zeros
|
|
||||||
power *= 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values.push_back((std::numeric_limits<std::uint64_t>::max)());
|
|
||||||
values.push_back(static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()));
|
|
||||||
values.push_back(static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()) + 1);
|
|
||||||
std::uint64_t state = 0x9E3779B97F4A7C15u;
|
|
||||||
for (int i = 0; i < 20000; ++i)
|
|
||||||
{
|
|
||||||
state = (state * 6364136223846793005u) + 1442695040888963407u;
|
|
||||||
values.push_back(state >> (state % 61)); // every bit length
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto v : values)
|
|
||||||
{
|
|
||||||
CAPTURE(v)
|
|
||||||
CHECK(json(v).dump() == std::to_string(v));
|
|
||||||
if (v <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
|
||||||
{
|
|
||||||
const auto s = static_cast<std::int64_t>(v);
|
|
||||||
CHECK(json(s).dump() == std::to_string(s));
|
|
||||||
CHECK(json(-s).dump() == std::to_string(-s));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CHECK(json((std::numeric_limits<std::int64_t>::min)()).dump() == "-9223372036854775808");
|
|
||||||
|
|
||||||
// in a container, so the digits are followed by other output
|
|
||||||
CHECK(json::array({std::uint64_t{18446744073709551615u}, std::int64_t{-1234567890123456789}, 1}).dump() == "[18446744073709551615,-1234567890123456789,1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("dump with binary values")
|
TEST_CASE("dump with binary values")
|
||||||
{
|
{
|
||||||
auto binary = json::binary({1, 2, 3, 4});
|
auto binary = json::binary({1, 2, 3, 4});
|
||||||
|
|||||||
Reference in new issue
Block a user