Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22d2013b13 |
No files matched your search
@@ -4293,6 +4293,19 @@ class binary_reader
|
|||||||
return get_bytes(len, "string", result);
|
return get_bytes(len, "string", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether @a s is valid UTF-8: ASCII eight bytes at a time, the rest
|
||||||
|
/// sequence by sequence (as the binary writers check strings)
|
||||||
|
static bool is_valid_utf8_string(const string_t& s, std::true_type /*is_byte*/) noexcept
|
||||||
|
{
|
||||||
|
return valid_utf8_string(reinterpret_cast<const unsigned char*>(s.data()), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether @a s, of a character type wider than a byte, is valid UTF-8
|
||||||
|
static bool is_valid_utf8_string(const string_t& s, std::false_type /*is_byte*/) noexcept
|
||||||
|
{
|
||||||
|
return is_valid_utf8(s);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate a decoded text string (value or object key) against @ref error_handler
|
@brief validate a decoded text string (value or object key) against @ref error_handler
|
||||||
|
|
||||||
@@ -4312,7 +4325,16 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_string_utf8(string_t& result, const char* context)
|
bool check_string_utf8(string_t& result, const char* context)
|
||||||
{
|
{
|
||||||
if (error_handler == error_handler_t::keep || is_valid_utf8(result))
|
return error_handler == error_handler_t::keep || check_string_utf8_handler(result, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @ref check_string_utf8 for an error handler other than @ref
|
||||||
|
/// error_handler_t::keep; out of line, so that reading strings without a
|
||||||
|
/// check does not pay for the code of the check
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool check_string_utf8_handler(string_t& result, const char* context)
|
||||||
|
{
|
||||||
|
if (is_valid_utf8_string(result, std::integral_constant<bool, sizeof(typename string_t::value_type) == 1>()))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,6 +322,78 @@ JSON_SCAN_SSSE3_TARGET JSON_HEDLEY_NEVER_INLINE inline bool vector_utf8_run(cons
|
|||||||
block_out = block;
|
block_out = block;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief skip 16-byte blocks of well-formed UTF-8 at p (a character boundary)
|
||||||
|
|
||||||
|
Like vector_utf8_run, but quotes, backslashes, and control characters are
|
||||||
|
ordinary characters. Returns the start of the first block in which the check
|
||||||
|
found an error (which may belong to a sequence begun in the block before) or
|
||||||
|
where fewer than 16 bytes are left; a sequence may be cut at that point, so the
|
||||||
|
caller resumes at its start, one sequence at a time.
|
||||||
|
*/
|
||||||
|
JSON_SCAN_SSSE3_TARGET JSON_HEDLEY_NEVER_INLINE inline const unsigned char* vector_utf8_valid_blocks(const unsigned char* block, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
using lookup = utf8_lookup4<>;
|
||||||
|
#if JSON_SCAN_NEON
|
||||||
|
const uint8x16_t t1h = vld1q_u8(lookup::byte_1_high.data());
|
||||||
|
const uint8x16_t t1l = vld1q_u8(lookup::byte_1_low.data());
|
||||||
|
const uint8x16_t t2h = vld1q_u8(lookup::byte_2_high.data());
|
||||||
|
uint8x16_t prev = vdupq_n_u8(0);
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const uint8x16_t in = vld1q_u8(block);
|
||||||
|
if (vmaxvq_u8(vorrq_u8(in, prev)) < 0x80)
|
||||||
|
{
|
||||||
|
// ASCII here, and no sequence from the block before to finish
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uint8x16_t prev1 = vextq_u8(prev, in, 15);
|
||||||
|
const uint8x16_t sc = vandq_u8(vandq_u8(vqtbl1q_u8(t1h, vshrq_n_u8(prev1, 4)), vqtbl1q_u8(t1l, vandq_u8(prev1, vdupq_n_u8(0x0F)))), vqtbl1q_u8(t2h, vshrq_n_u8(in, 4)));
|
||||||
|
const uint8x16_t must23 = vorrq_u8(vqsubq_u8(vextq_u8(prev, in, 14), vdupq_n_u8(0xE0 - 0x80)), vqsubq_u8(vextq_u8(prev, in, 13), vdupq_n_u8(0xF0 - 0x80)));
|
||||||
|
const uint8x16_t err = veorq_u8(vandq_u8(must23, vdupq_n_u8(0x80)), sc);
|
||||||
|
if (vmaxvq_u8(err) != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const __m128i t1h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_high.data()))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
const __m128i t1l = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_low.data()))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
const __m128i t2h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_2_high.data()))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
const __m128i nibble = _mm_set1_epi8(0x0F);
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
__m128i prev = zero;
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(block))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
if (_mm_movemask_epi8(_mm_or_si128(in, prev)) == 0)
|
||||||
|
{
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const __m128i prev1 = _mm_alignr_epi8(in, prev, 15);
|
||||||
|
const __m128i sc = _mm_and_si128(_mm_and_si128(_mm_shuffle_epi8(t1h, _mm_and_si128(_mm_srli_epi16(prev1, 4), nibble)),
|
||||||
|
_mm_shuffle_epi8(t1l, _mm_and_si128(prev1, nibble))),
|
||||||
|
_mm_shuffle_epi8(t2h, _mm_and_si128(_mm_srli_epi16(in, 4), nibble)));
|
||||||
|
const __m128i must23 = _mm_or_si128(_mm_subs_epu8(_mm_alignr_epi8(in, prev, 14), _mm_set1_epi8(0xE0 - 0x80)),
|
||||||
|
_mm_subs_epu8(_mm_alignr_epi8(in, prev, 13), _mm_set1_epi8(0xF0 - 0x80)));
|
||||||
|
const __m128i err = _mm_xor_si128(_mm_and_si128(must23, _mm_set1_epi8(static_cast<char>(-128))), sc);
|
||||||
|
if (_mm_movemask_epi8(_mm_cmpeq_epi8(err, zero)) != 0xFFFF)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return block;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
@@ -236,6 +236,64 @@ inline std::size_t valid_utf8_prefix(const unsigned char* data, std::size_t n) n
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief whether [data, data+n) is valid UTF-8, for checking whole strings
|
||||||
|
|
||||||
|
The same answer as valid_utf8_prefix(data, n) == n. Short ASCII strings, the
|
||||||
|
most common keys and values, are decided in one test, and long strings are
|
||||||
|
checked 16 bytes at a time where the vector check is available.
|
||||||
|
*/
|
||||||
|
inline bool valid_utf8_string(const unsigned char* data, std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
constexpr std::uint64_t high = 0x8080808080808080ull;
|
||||||
|
if (n <= 16)
|
||||||
|
{
|
||||||
|
// two overlapping words cover 8 to 16 bytes, one partial word fewer
|
||||||
|
std::uint64_t first = 0;
|
||||||
|
std::uint64_t last = 0;
|
||||||
|
if (n >= 8)
|
||||||
|
{
|
||||||
|
std::memcpy(&first, data, sizeof(first));
|
||||||
|
std::memcpy(&last, data + n - 8, sizeof(last));
|
||||||
|
}
|
||||||
|
else if (n != 0)
|
||||||
|
{
|
||||||
|
std::memcpy(&first, data, n);
|
||||||
|
}
|
||||||
|
if (((first | last) & high) == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if JSON_SCAN_VECTOR_UTF8
|
||||||
|
else if (n >= 32
|
||||||
|
#if JSON_SCAN_SSSE3_DISPATCH
|
||||||
|
&& cpu_has_ssse3()
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const unsigned char* block = vector_utf8_valid_blocks(data, data + n);
|
||||||
|
// the rest from the start of a sequence that may be cut at block
|
||||||
|
for (int i = 1; i <= 3 && block - i >= data; ++i)
|
||||||
|
{
|
||||||
|
const unsigned char c = block[-i];
|
||||||
|
if (c < 0x80)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c >= 0xC0)
|
||||||
|
{
|
||||||
|
block -= i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto checked = static_cast<std::size_t>(block - data);
|
||||||
|
return checked + valid_utf8_prefix(block, n - checked) == n;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return valid_utf8_prefix(data, n) == n;
|
||||||
|
}
|
||||||
|
|
||||||
#if JSON_SCAN_VECTOR_UTF8
|
#if JSON_SCAN_VECTOR_UTF8
|
||||||
/// the end of scan_string_vector from block, where the vector loop stopped
|
/// the end of scan_string_vector from block, where the vector loop stopped
|
||||||
/// (ill-formed UTF-8, or fewer than 16 bytes left): one byte or sequence at a
|
/// (ill-formed UTF-8, or fewer than 16 bytes left): one byte or sequence at a
|
||||||
|
|||||||
@@ -3080,6 +3080,25 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// write the head and the characters of @a value, at most
|
||||||
|
/// max_text_in_reservation of them, in one reservation
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE void write_cbor_string_reserved(const string_t& value)
|
||||||
|
{
|
||||||
|
CharType* const out = oa.reserve(value.size() + 2);
|
||||||
|
CharType* p = out;
|
||||||
|
if (value.size() <= 0x17)
|
||||||
|
{
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(0x60 + value.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*p++ = to_char_type(0x78);
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
||||||
|
}
|
||||||
|
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||||
|
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write a CBOR text string
|
@brief write a CBOR text string
|
||||||
|
|
||||||
@@ -3101,19 +3120,7 @@ class binary_writer
|
|||||||
// the string is written as it is: head and characters in one reservation
|
// the string is written as it is: head and characters in one reservation
|
||||||
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
||||||
{
|
{
|
||||||
CharType* const out = oa.reserve(value.size() + 2);
|
write_cbor_string_reserved(value);
|
||||||
CharType* p = out;
|
|
||||||
if (value.size() <= 0x17)
|
|
||||||
{
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(0x60 + value.size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*p++ = to_char_type(0x78);
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
|
||||||
}
|
|
||||||
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
|
||||||
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3127,6 +3134,13 @@ class binary_writer
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
void write_cbor_checked_string(const string_t& value, const BasicJsonType& context)
|
void write_cbor_checked_string(const string_t& value, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
|
if (error_handler == error_handler_t::strict && value.size() <= max_text_in_reservation)
|
||||||
|
{
|
||||||
|
check_utf8(value, context);
|
||||||
|
write_cbor_string_reserved(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string_t storage;
|
string_t storage;
|
||||||
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
||||||
|
|
||||||
@@ -3141,6 +3155,27 @@ class binary_writer
|
|||||||
// MsgPack //
|
// MsgPack //
|
||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
|
/// write the head and the characters of @a value, at most
|
||||||
|
/// max_text_in_reservation of them, in one reservation
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE void write_msgpack_string_reserved(const string_t& value)
|
||||||
|
{
|
||||||
|
CharType* const out = oa.reserve(value.size() + 2);
|
||||||
|
CharType* p = out;
|
||||||
|
if (value.size() <= 31)
|
||||||
|
{
|
||||||
|
// fixstr
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(0xA0 | value.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// str 8
|
||||||
|
*p++ = to_char_type(0xD9);
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
||||||
|
}
|
||||||
|
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||||
|
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write a MessagePack str
|
@brief write a MessagePack str
|
||||||
|
|
||||||
@@ -3158,21 +3193,7 @@ class binary_writer
|
|||||||
// the string is written as it is: head and characters in one reservation
|
// the string is written as it is: head and characters in one reservation
|
||||||
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
||||||
{
|
{
|
||||||
CharType* const out = oa.reserve(value.size() + 2);
|
write_msgpack_string_reserved(value);
|
||||||
CharType* p = out;
|
|
||||||
if (value.size() <= 31)
|
|
||||||
{
|
|
||||||
// fixstr
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(0xA0 | value.size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// str 8
|
|
||||||
*p++ = to_char_type(0xD9);
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
|
||||||
}
|
|
||||||
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
|
||||||
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3185,6 +3206,13 @@ class binary_writer
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
void write_msgpack_checked_string(const string_t& value, const BasicJsonType& context)
|
void write_msgpack_checked_string(const string_t& value, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
|
if (error_handler == error_handler_t::strict && value.size() <= max_text_in_reservation)
|
||||||
|
{
|
||||||
|
check_utf8(value, context);
|
||||||
|
write_msgpack_string_reserved(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string_t storage;
|
string_t storage;
|
||||||
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
||||||
|
|
||||||
@@ -4794,10 +4822,9 @@ class binary_writer
|
|||||||
static void check_utf8(const string_t& s, const BasicJsonType& context)
|
static void check_utf8(const string_t& s, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
||||||
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
if (JSON_HEDLEY_UNLIKELY(!valid_utf8_string(data, s.size())))
|
||||||
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
|
||||||
{
|
{
|
||||||
throw_invalid_utf8(data, valid, context);
|
throw_invalid_utf8(data, valid_utf8_prefix(data, s.size()), context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
+211
-66
@@ -12377,6 +12377,78 @@ JSON_SCAN_SSSE3_TARGET JSON_HEDLEY_NEVER_INLINE inline bool vector_utf8_run(cons
|
|||||||
block_out = block;
|
block_out = block;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief skip 16-byte blocks of well-formed UTF-8 at p (a character boundary)
|
||||||
|
|
||||||
|
Like vector_utf8_run, but quotes, backslashes, and control characters are
|
||||||
|
ordinary characters. Returns the start of the first block in which the check
|
||||||
|
found an error (which may belong to a sequence begun in the block before) or
|
||||||
|
where fewer than 16 bytes are left; a sequence may be cut at that point, so the
|
||||||
|
caller resumes at its start, one sequence at a time.
|
||||||
|
*/
|
||||||
|
JSON_SCAN_SSSE3_TARGET JSON_HEDLEY_NEVER_INLINE inline const unsigned char* vector_utf8_valid_blocks(const unsigned char* block, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
using lookup = utf8_lookup4<>;
|
||||||
|
#if JSON_SCAN_NEON
|
||||||
|
const uint8x16_t t1h = vld1q_u8(lookup::byte_1_high.data());
|
||||||
|
const uint8x16_t t1l = vld1q_u8(lookup::byte_1_low.data());
|
||||||
|
const uint8x16_t t2h = vld1q_u8(lookup::byte_2_high.data());
|
||||||
|
uint8x16_t prev = vdupq_n_u8(0);
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const uint8x16_t in = vld1q_u8(block);
|
||||||
|
if (vmaxvq_u8(vorrq_u8(in, prev)) < 0x80)
|
||||||
|
{
|
||||||
|
// ASCII here, and no sequence from the block before to finish
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uint8x16_t prev1 = vextq_u8(prev, in, 15);
|
||||||
|
const uint8x16_t sc = vandq_u8(vandq_u8(vqtbl1q_u8(t1h, vshrq_n_u8(prev1, 4)), vqtbl1q_u8(t1l, vandq_u8(prev1, vdupq_n_u8(0x0F)))), vqtbl1q_u8(t2h, vshrq_n_u8(in, 4)));
|
||||||
|
const uint8x16_t must23 = vorrq_u8(vqsubq_u8(vextq_u8(prev, in, 14), vdupq_n_u8(0xE0 - 0x80)), vqsubq_u8(vextq_u8(prev, in, 13), vdupq_n_u8(0xF0 - 0x80)));
|
||||||
|
const uint8x16_t err = veorq_u8(vandq_u8(must23, vdupq_n_u8(0x80)), sc);
|
||||||
|
if (vmaxvq_u8(err) != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const __m128i t1h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_high.data()))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
const __m128i t1l = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_low.data()))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
const __m128i t2h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_2_high.data()))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
const __m128i nibble = _mm_set1_epi8(0x0F);
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
__m128i prev = zero;
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(block))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
if (_mm_movemask_epi8(_mm_or_si128(in, prev)) == 0)
|
||||||
|
{
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const __m128i prev1 = _mm_alignr_epi8(in, prev, 15);
|
||||||
|
const __m128i sc = _mm_and_si128(_mm_and_si128(_mm_shuffle_epi8(t1h, _mm_and_si128(_mm_srli_epi16(prev1, 4), nibble)),
|
||||||
|
_mm_shuffle_epi8(t1l, _mm_and_si128(prev1, nibble))),
|
||||||
|
_mm_shuffle_epi8(t2h, _mm_and_si128(_mm_srli_epi16(in, 4), nibble)));
|
||||||
|
const __m128i must23 = _mm_or_si128(_mm_subs_epu8(_mm_alignr_epi8(in, prev, 14), _mm_set1_epi8(0xE0 - 0x80)),
|
||||||
|
_mm_subs_epu8(_mm_alignr_epi8(in, prev, 13), _mm_set1_epi8(0xF0 - 0x80)));
|
||||||
|
const __m128i err = _mm_xor_si128(_mm_and_si128(must23, _mm_set1_epi8(static_cast<char>(-128))), sc);
|
||||||
|
if (_mm_movemask_epi8(_mm_cmpeq_epi8(err, zero)) != 0xFFFF)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return block;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@@ -12604,6 +12676,64 @@ inline std::size_t valid_utf8_prefix(const unsigned char* data, std::size_t n) n
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief whether [data, data+n) is valid UTF-8, for checking whole strings
|
||||||
|
|
||||||
|
The same answer as valid_utf8_prefix(data, n) == n. Short ASCII strings, the
|
||||||
|
most common keys and values, are decided in one test, and long strings are
|
||||||
|
checked 16 bytes at a time where the vector check is available.
|
||||||
|
*/
|
||||||
|
inline bool valid_utf8_string(const unsigned char* data, std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
constexpr std::uint64_t high = 0x8080808080808080ull;
|
||||||
|
if (n <= 16)
|
||||||
|
{
|
||||||
|
// two overlapping words cover 8 to 16 bytes, one partial word fewer
|
||||||
|
std::uint64_t first = 0;
|
||||||
|
std::uint64_t last = 0;
|
||||||
|
if (n >= 8)
|
||||||
|
{
|
||||||
|
std::memcpy(&first, data, sizeof(first));
|
||||||
|
std::memcpy(&last, data + n - 8, sizeof(last));
|
||||||
|
}
|
||||||
|
else if (n != 0)
|
||||||
|
{
|
||||||
|
std::memcpy(&first, data, n);
|
||||||
|
}
|
||||||
|
if (((first | last) & high) == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if JSON_SCAN_VECTOR_UTF8
|
||||||
|
else if (n >= 32
|
||||||
|
#if JSON_SCAN_SSSE3_DISPATCH
|
||||||
|
&& cpu_has_ssse3()
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const unsigned char* block = vector_utf8_valid_blocks(data, data + n);
|
||||||
|
// the rest from the start of a sequence that may be cut at block
|
||||||
|
for (int i = 1; i <= 3 && block - i >= data; ++i)
|
||||||
|
{
|
||||||
|
const unsigned char c = block[-i];
|
||||||
|
if (c < 0x80)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c >= 0xC0)
|
||||||
|
{
|
||||||
|
block -= i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto checked = static_cast<std::size_t>(block - data);
|
||||||
|
return checked + valid_utf8_prefix(block, n - checked) == n;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return valid_utf8_prefix(data, n) == n;
|
||||||
|
}
|
||||||
|
|
||||||
#if JSON_SCAN_VECTOR_UTF8
|
#if JSON_SCAN_VECTOR_UTF8
|
||||||
/// the end of scan_string_vector from block, where the vector loop stopped
|
/// the end of scan_string_vector from block, where the vector loop stopped
|
||||||
/// (ill-formed UTF-8, or fewer than 16 bytes left): one byte or sequence at a
|
/// (ill-formed UTF-8, or fewer than 16 bytes left): one byte or sequence at a
|
||||||
@@ -22069,6 +22199,19 @@ class binary_reader
|
|||||||
return get_bytes(len, "string", result);
|
return get_bytes(len, "string", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether @a s is valid UTF-8: ASCII eight bytes at a time, the rest
|
||||||
|
/// sequence by sequence (as the binary writers check strings)
|
||||||
|
static bool is_valid_utf8_string(const string_t& s, std::true_type /*is_byte*/) noexcept
|
||||||
|
{
|
||||||
|
return valid_utf8_string(reinterpret_cast<const unsigned char*>(s.data()), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether @a s, of a character type wider than a byte, is valid UTF-8
|
||||||
|
static bool is_valid_utf8_string(const string_t& s, std::false_type /*is_byte*/) noexcept
|
||||||
|
{
|
||||||
|
return is_valid_utf8(s);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate a decoded text string (value or object key) against @ref error_handler
|
@brief validate a decoded text string (value or object key) against @ref error_handler
|
||||||
|
|
||||||
@@ -22088,7 +22231,16 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_string_utf8(string_t& result, const char* context)
|
bool check_string_utf8(string_t& result, const char* context)
|
||||||
{
|
{
|
||||||
if (error_handler == error_handler_t::keep || is_valid_utf8(result))
|
return error_handler == error_handler_t::keep || check_string_utf8_handler(result, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @ref check_string_utf8 for an error handler other than @ref
|
||||||
|
/// error_handler_t::keep; out of line, so that reading strings without a
|
||||||
|
/// check does not pay for the code of the check
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool check_string_utf8_handler(string_t& result, const char* context)
|
||||||
|
{
|
||||||
|
if (is_valid_utf8_string(result, std::integral_constant<bool, sizeof(typename string_t::value_type) == 1>()))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -30306,6 +30458,25 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// write the head and the characters of @a value, at most
|
||||||
|
/// max_text_in_reservation of them, in one reservation
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE void write_cbor_string_reserved(const string_t& value)
|
||||||
|
{
|
||||||
|
CharType* const out = oa.reserve(value.size() + 2);
|
||||||
|
CharType* p = out;
|
||||||
|
if (value.size() <= 0x17)
|
||||||
|
{
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(0x60 + value.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*p++ = to_char_type(0x78);
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
||||||
|
}
|
||||||
|
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||||
|
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write a CBOR text string
|
@brief write a CBOR text string
|
||||||
|
|
||||||
@@ -30327,19 +30498,7 @@ class binary_writer
|
|||||||
// the string is written as it is: head and characters in one reservation
|
// the string is written as it is: head and characters in one reservation
|
||||||
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
||||||
{
|
{
|
||||||
CharType* const out = oa.reserve(value.size() + 2);
|
write_cbor_string_reserved(value);
|
||||||
CharType* p = out;
|
|
||||||
if (value.size() <= 0x17)
|
|
||||||
{
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(0x60 + value.size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*p++ = to_char_type(0x78);
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
|
||||||
}
|
|
||||||
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
|
||||||
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30353,6 +30512,13 @@ class binary_writer
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
void write_cbor_checked_string(const string_t& value, const BasicJsonType& context)
|
void write_cbor_checked_string(const string_t& value, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
|
if (error_handler == error_handler_t::strict && value.size() <= max_text_in_reservation)
|
||||||
|
{
|
||||||
|
check_utf8(value, context);
|
||||||
|
write_cbor_string_reserved(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string_t storage;
|
string_t storage;
|
||||||
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
||||||
|
|
||||||
@@ -30367,6 +30533,27 @@ class binary_writer
|
|||||||
// MsgPack //
|
// MsgPack //
|
||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
|
/// write the head and the characters of @a value, at most
|
||||||
|
/// max_text_in_reservation of them, in one reservation
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE void write_msgpack_string_reserved(const string_t& value)
|
||||||
|
{
|
||||||
|
CharType* const out = oa.reserve(value.size() + 2);
|
||||||
|
CharType* p = out;
|
||||||
|
if (value.size() <= 31)
|
||||||
|
{
|
||||||
|
// fixstr
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(0xA0 | value.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// str 8
|
||||||
|
*p++ = to_char_type(0xD9);
|
||||||
|
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
||||||
|
}
|
||||||
|
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||||
|
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write a MessagePack str
|
@brief write a MessagePack str
|
||||||
|
|
||||||
@@ -30384,21 +30571,7 @@ class binary_writer
|
|||||||
// the string is written as it is: head and characters in one reservation
|
// the string is written as it is: head and characters in one reservation
|
||||||
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
if (JSON_HEDLEY_LIKELY(value.size() <= max_text_in_reservation))
|
||||||
{
|
{
|
||||||
CharType* const out = oa.reserve(value.size() + 2);
|
write_msgpack_string_reserved(value);
|
||||||
CharType* p = out;
|
|
||||||
if (value.size() <= 31)
|
|
||||||
{
|
|
||||||
// fixstr
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(0xA0 | value.size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// str 8
|
|
||||||
*p++ = to_char_type(0xD9);
|
|
||||||
*p++ = to_char_type(static_cast<std::uint8_t>(value.size()));
|
|
||||||
}
|
|
||||||
copy_characters(p, reinterpret_cast<const CharType*>(value.data()), value.size());
|
|
||||||
oa.commit(static_cast<std::size_t>(p - out) + value.size());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30411,6 +30584,13 @@ class binary_writer
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
void write_msgpack_checked_string(const string_t& value, const BasicJsonType& context)
|
void write_msgpack_checked_string(const string_t& value, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
|
if (error_handler == error_handler_t::strict && value.size() <= max_text_in_reservation)
|
||||||
|
{
|
||||||
|
check_utf8(value, context);
|
||||||
|
write_msgpack_string_reserved(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string_t storage;
|
string_t storage;
|
||||||
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
||||||
|
|
||||||
@@ -32020,10 +32200,9 @@ class binary_writer
|
|||||||
static void check_utf8(const string_t& s, const BasicJsonType& context)
|
static void check_utf8(const string_t& s, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
||||||
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
if (JSON_HEDLEY_UNLIKELY(!valid_utf8_string(data, s.size())))
|
||||||
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
|
||||||
{
|
{
|
||||||
throw_invalid_utf8(data, valid, context);
|
throw_invalid_utf8(data, valid_utf8_prefix(data, s.size()), context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36224,23 +36403,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 +36548,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))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -403,6 +403,7 @@ TEST_CASE("guard page")
|
|||||||
CHECK(nlohmann::detail::find_string_special(p, n) <= n);
|
CHECK(nlohmann::detail::find_string_special(p, n) <= n);
|
||||||
CHECK(nlohmann::detail::find_ascii_copyable_run(p, n) <= n);
|
CHECK(nlohmann::detail::find_ascii_copyable_run(p, n) <= n);
|
||||||
CHECK(nlohmann::detail::valid_utf8_prefix(p, n) <= n);
|
CHECK(nlohmann::detail::valid_utf8_prefix(p, n) <= n);
|
||||||
|
CHECK(nlohmann::detail::valid_utf8_string(p, n) == (nlohmann::detail::valid_utf8_prefix(p, n) == n));
|
||||||
#if defined(JSON_USE_SIMDUTF) && defined(JSON_HAS_CPP_17)
|
#if defined(JSON_USE_SIMDUTF) && defined(JSON_HAS_CPP_17)
|
||||||
CHECK(nlohmann::detail::find_string_delimiter(p, n) <= n);
|
CHECK(nlohmann::detail::find_string_delimiter(p, n) <= n);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using nlohmann::json;
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "make_test_data_available.hpp"
|
#include "make_test_data_available.hpp"
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
@@ -1631,3 +1632,40 @@ TEST_CASE("Unicode RFC 3629")
|
|||||||
}
|
}
|
||||||
|
|
||||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||||
|
|
||||||
|
TEST_CASE("UTF-8 check of whole strings")
|
||||||
|
{
|
||||||
|
// valid_utf8_string() decides short strings with a word test and long ones
|
||||||
|
// 16 bytes at a time where a vector check is available; it must agree with
|
||||||
|
// the byte-wise check for every mix of ASCII, sequences of each length,
|
||||||
|
// ill-formed and truncated sequences, at every length and offset
|
||||||
|
const char* const pieces[] =
|
||||||
|
{
|
||||||
|
"a", "\x7F", "\xC3\xA9", "\xE2\x82\xAC", "\xE0\xA0\x80", "\xEF\xBF\xBF", "\xF0\x9F\x98\x80", "\xF4\x8F\xBF\xBF",
|
||||||
|
"\x80", "\xC0\xAF", "\xC3", "\xE2\x82", "\xED\xA0\x80", "\xF0\x9F", "\xF4\x90\x80\x80", "\xFF"
|
||||||
|
};
|
||||||
|
// std::mt19937's sequence is fixed by the standard; no distribution is used
|
||||||
|
std::mt19937 rng(5);
|
||||||
|
std::size_t valid = 0;
|
||||||
|
for (int i = 0; i < 100000; ++i)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
const std::size_t length = rng() % 100;
|
||||||
|
const std::uint32_t ascii = rng() % 101;
|
||||||
|
while (s.size() < length)
|
||||||
|
{
|
||||||
|
// mostly well-formed pieces (the first eight), so that long valid
|
||||||
|
// strings are common
|
||||||
|
s += (rng() % 100 < ascii) ? std::string("x") : std::string(pieces[rng() % (rng() % 8 == 0 ? 16 : 8)]);
|
||||||
|
}
|
||||||
|
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
||||||
|
const bool expected = nlohmann::detail::is_valid_utf8(s);
|
||||||
|
valid += expected ? 1 : 0;
|
||||||
|
CAPTURE(s)
|
||||||
|
CHECK(nlohmann::detail::valid_utf8_string(data, s.size()) == expected);
|
||||||
|
CHECK((nlohmann::detail::valid_utf8_prefix(data, s.size()) == s.size()) == expected);
|
||||||
|
}
|
||||||
|
// both outcomes are common enough to be tested
|
||||||
|
CHECK(valid > 20000);
|
||||||
|
CHECK(valid < 80000);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user