Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0efa09cf49 | ||
|
|
6491563cbb |
No files matched your search
@@ -4293,19 +4293,6 @@ 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
|
||||||
|
|
||||||
@@ -4325,16 +4312,7 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_string_utf8(string_t& result, const char* context)
|
bool check_string_utf8(string_t& result, const char* context)
|
||||||
{
|
{
|
||||||
return error_handler == error_handler_t::keep || check_string_utf8_handler(result, context);
|
if (error_handler == error_handler_t::keep || is_valid_utf8(result))
|
||||||
}
|
|
||||||
|
|
||||||
/// @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,78 +322,6 @@ 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,64 +236,6 @@ 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,25 +3080,6 @@ 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
|
||||||
|
|
||||||
@@ -3120,7 +3101,19 @@ 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))
|
||||||
{
|
{
|
||||||
write_cbor_string_reserved(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());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3134,13 +3127,6 @@ 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);
|
||||||
|
|
||||||
@@ -3155,27 +3141,6 @@ 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
|
||||||
|
|
||||||
@@ -3193,7 +3158,21 @@ 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))
|
||||||
{
|
{
|
||||||
write_msgpack_string_reserved(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());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3206,13 +3185,6 @@ 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);
|
||||||
|
|
||||||
@@ -4822,9 +4794,10 @@ 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());
|
||||||
if (JSON_HEDLEY_UNLIKELY(!valid_utf8_string(data, s.size())))
|
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
||||||
{
|
{
|
||||||
throw_invalid_utf8(data, valid_utf8_prefix(data, s.size()), context);
|
throw_invalid_utf8(data, valid, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1082,7 +1082,7 @@ indent_done:
|
|||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
const unsigned char* const f0 = p;
|
const unsigned char* const f0 = p;
|
||||||
p = skip_digits(p, e);
|
p = skip_fraction_digits(p, e);
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(p == f0))
|
if (NLOHMANN_VIEW_UNLIKELY(p == f0))
|
||||||
{
|
{
|
||||||
return fail(error_code::number_after_dot);
|
return fail(error_code::number_after_dot);
|
||||||
|
|||||||
@@ -198,6 +198,57 @@ inline const unsigned char* find_block_comment_end(const unsigned char* p, const
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The digits of a fraction, whose number varies from one number to the next: skip_digits() with 16 bytes compared at
|
||||||
|
/// once and the end of the run taken from a bit mask (SSE2), instead of a compare and a branch for each digit. That
|
||||||
|
/// is about a third of the instructions for a fraction of 12 digits, and no branch that is mispredicted when the
|
||||||
|
/// lengths vary. Where there is no SSE2, skip_digits() is used.
|
||||||
|
#if NLOHMANN_VIEW_SSE2
|
||||||
|
// The last bytes of the input, one at a time. GCC unrolls this loop in every inlined copy of the function below, which
|
||||||
|
// makes the parse loop 4 KiB larger and slows down documents that have hardly any fractions; out of line, it does
|
||||||
|
// not. Clang does better with the loop inline (a call in the parse loop costs it more than the unrolling).
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
inline NLOHMANN_VIEW_NOINLINE const unsigned char* skip_digits_tail(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (p != e && is_digit(*p))
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits_tail(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (p != e && is_digit(*p))
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_fraction_digits(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (e - p >= 16)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(p))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
// a digit is above '/' and below ':' (signed compares: a byte with the top bit set is negative, and neither)
|
||||||
|
const __m128i digits = _mm_and_si128(_mm_cmpgt_epi8(in, _mm_set1_epi8('/')), _mm_cmplt_epi8(in, _mm_set1_epi8(':')));
|
||||||
|
const auto not_digit = static_cast<std::uint64_t>(~static_cast<unsigned>(_mm_movemask_epi8(digits)) & 0xFFFFu);
|
||||||
|
if (not_digit != 0)
|
||||||
|
{
|
||||||
|
return p + count_trailing_zeros(not_digit);
|
||||||
|
}
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
return skip_digits_tail(p, e);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_fraction_digits(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
return skip_digits(p, e);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// powers of ten up to 10^19 as integers
|
/// powers of ten up to 10^19 as integers
|
||||||
inline std::uint64_t int_pow10(unsigned k) noexcept
|
inline std::uint64_t int_pow10(unsigned k) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
Generated
+32
-211
@@ -12377,78 +12377,6 @@ 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
|
||||||
@@ -12676,64 +12604,6 @@ 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
|
||||||
@@ -22199,19 +22069,6 @@ 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
|
||||||
|
|
||||||
@@ -22231,16 +22088,7 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_string_utf8(string_t& result, const char* context)
|
bool check_string_utf8(string_t& result, const char* context)
|
||||||
{
|
{
|
||||||
return error_handler == error_handler_t::keep || check_string_utf8_handler(result, context);
|
if (error_handler == error_handler_t::keep || is_valid_utf8(result))
|
||||||
}
|
|
||||||
|
|
||||||
/// @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;
|
||||||
}
|
}
|
||||||
@@ -30458,25 +30306,6 @@ 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
|
||||||
|
|
||||||
@@ -30498,7 +30327,19 @@ 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))
|
||||||
{
|
{
|
||||||
write_cbor_string_reserved(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());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30512,13 +30353,6 @@ 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);
|
||||||
|
|
||||||
@@ -30533,27 +30367,6 @@ 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
|
||||||
|
|
||||||
@@ -30571,7 +30384,21 @@ 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))
|
||||||
{
|
{
|
||||||
write_msgpack_string_reserved(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());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30584,13 +30411,6 @@ 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);
|
||||||
|
|
||||||
@@ -32200,9 +32020,10 @@ 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());
|
||||||
if (JSON_HEDLEY_UNLIKELY(!valid_utf8_string(data, s.size())))
|
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
||||||
{
|
{
|
||||||
throw_invalid_utf8(data, valid_utf8_prefix(data, s.size()), context);
|
throw_invalid_utf8(data, valid, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+52
-1
@@ -1218,6 +1218,57 @@ inline const unsigned char* find_block_comment_end(const unsigned char* p, const
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The digits of a fraction, whose number varies from one number to the next: skip_digits() with 16 bytes compared at
|
||||||
|
/// once and the end of the run taken from a bit mask (SSE2), instead of a compare and a branch for each digit. That
|
||||||
|
/// is about a third of the instructions for a fraction of 12 digits, and no branch that is mispredicted when the
|
||||||
|
/// lengths vary. Where there is no SSE2, skip_digits() is used.
|
||||||
|
#if NLOHMANN_VIEW_SSE2
|
||||||
|
// The last bytes of the input, one at a time. GCC unrolls this loop in every inlined copy of the function below, which
|
||||||
|
// makes the parse loop 4 KiB larger and slows down documents that have hardly any fractions; out of line, it does
|
||||||
|
// not. Clang does better with the loop inline (a call in the parse loop costs it more than the unrolling).
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
inline NLOHMANN_VIEW_NOINLINE const unsigned char* skip_digits_tail(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (p != e && is_digit(*p))
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits_tail(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (p != e && is_digit(*p))
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_fraction_digits(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (e - p >= 16)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(p))); // NOLINT(bugprone-casting-through-void)
|
||||||
|
// a digit is above '/' and below ':' (signed compares: a byte with the top bit set is negative, and neither)
|
||||||
|
const __m128i digits = _mm_and_si128(_mm_cmpgt_epi8(in, _mm_set1_epi8('/')), _mm_cmplt_epi8(in, _mm_set1_epi8(':')));
|
||||||
|
const auto not_digit = static_cast<std::uint64_t>(~static_cast<unsigned>(_mm_movemask_epi8(digits)) & 0xFFFFu);
|
||||||
|
if (not_digit != 0)
|
||||||
|
{
|
||||||
|
return p + count_trailing_zeros(not_digit);
|
||||||
|
}
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
return skip_digits_tail(p, e);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_fraction_digits(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
return skip_digits(p, e);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// powers of ten up to 10^19 as integers
|
/// powers of ten up to 10^19 as integers
|
||||||
inline std::uint64_t int_pow10(unsigned k) noexcept
|
inline std::uint64_t int_pow10(unsigned k) noexcept
|
||||||
{
|
{
|
||||||
@@ -2324,7 +2375,7 @@ indent_done:
|
|||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
const unsigned char* const f0 = p;
|
const unsigned char* const f0 = p;
|
||||||
p = skip_digits(p, e);
|
p = skip_fraction_digits(p, e);
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(p == f0))
|
if (NLOHMANN_VIEW_UNLIKELY(p == f0))
|
||||||
{
|
{
|
||||||
return fail(error_code::number_after_dot);
|
return fail(error_code::number_after_dot);
|
||||||
|
|||||||
@@ -403,7 +403,6 @@ 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
|
||||||
|
|||||||
@@ -523,6 +523,51 @@ TEST_CASE("json_view builder: strings across vector blocks")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("json_view builder: fraction digits")
|
||||||
|
{
|
||||||
|
// The digits of a fraction are skipped 16 bytes at a time where there are
|
||||||
|
// that many left (SSE2), and one at a time after that. Fractions of every
|
||||||
|
// length from 0 to 40 digits, followed by every kind of byte that ends a
|
||||||
|
// number or lies next to the digits in ASCII ('/' and ':'), as values, as
|
||||||
|
// keys' neighbours, and at the very end of the text (a vector without a
|
||||||
|
// terminating NUL). The length of the number is part of the value that
|
||||||
|
// the test compares with json::parse().
|
||||||
|
const std::string followers = ",]}/:.eE+-x \n\t\"09";
|
||||||
|
std::mt19937 rng(2028); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
||||||
|
for (std::size_t digits = 0; digits <= 40; ++digits)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
std::string fraction;
|
||||||
|
for (std::size_t k = 0; k < digits; ++k)
|
||||||
|
{
|
||||||
|
fraction += static_cast<char>('0' + (rng() % 10));
|
||||||
|
}
|
||||||
|
const std::string number = std::string(rng() % 2 == 0 ? "-" : "") + std::to_string(rng() % 100000) + "." + fraction;
|
||||||
|
check_same(number);
|
||||||
|
check_same("[" + number + "]");
|
||||||
|
std::string pair = "[" + number;
|
||||||
|
pair += ",";
|
||||||
|
pair += number;
|
||||||
|
pair += "]";
|
||||||
|
check_same(pair);
|
||||||
|
check_same("{\"a\":" + number + "}");
|
||||||
|
for (const char follower : followers)
|
||||||
|
{
|
||||||
|
check_same(number + follower);
|
||||||
|
check_same("[" + number + follower + "]");
|
||||||
|
check_same("[" + number + follower + std::string(rng() % 20, '1'));
|
||||||
|
}
|
||||||
|
// everything near the end of the text: the number cut in every place
|
||||||
|
for (std::size_t cut = 1; cut <= 18 && cut <= number.size(); ++cut)
|
||||||
|
{
|
||||||
|
check_same(number.substr(0, number.size() - cut));
|
||||||
|
check_same("[" + number.substr(0, number.size() - cut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("json_view builder: escapes between runs of text")
|
TEST_CASE("json_view builder: escapes between runs of text")
|
||||||
{
|
{
|
||||||
// The text between escapes is copied 16 bytes at a time where there are
|
// The text between escapes is copied 16 bytes at a time where there are
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ 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"
|
||||||
@@ -1632,40 +1631,3 @@ 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[] = // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
{
|
|
||||||
"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); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
|
|
||||||
std::size_t valid = 0;
|
|
||||||
for (int i = 0; i < 100000; ++i)
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
const std::size_t length = rng() % 100;
|
|
||||||
const auto ascii = static_cast<std::uint32_t>(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