Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22d2013b13 |
No files matched your search
@@ -10,13 +10,6 @@ at its root, a [Natvis](https://learn.microsoft.com/en-us/visualstudio/debugger/
|
|||||||
file that gives `json`/`ordered_json` values a friendly, key/value debugger view instead of showing raw internal
|
file that gives `json`/`ordered_json` values a friendly, key/value debugger view instead of showing raw internal
|
||||||
fields, when debugging with the MSVC debug engine (`cppvsdbg`) in Visual Studio or VS Code.
|
fields, when debugging with the MSVC debug engine (`cppvsdbg`) in Visual Studio or VS Code.
|
||||||
|
|
||||||
The file also has visualizers for `json_document` and `json_view` (and their `ordered_json`/editable variants). They
|
|
||||||
show the value of the document or view, and its elements as an expandable list. Because a document is only a flat index
|
|
||||||
into the source text, the strings of the direct elements are shown, but the elements of nested arrays and objects are
|
|
||||||
shown as index nodes (their kind, size, and numbers; strings only by length). Arrays and objects that have been edited
|
|
||||||
in a `json_editable_document` are not expanded. The visualizers read the internal layout of the MSVC STL (`std::array`,
|
|
||||||
`std::unique_ptr`).
|
|
||||||
|
|
||||||
The checked-in visualizers cover namespace variants with zero, one, or two ABI tags. Configurations with three or more
|
The checked-in visualizers cover namespace variants with zero, one, or two ABI tags. Configurations with three or more
|
||||||
ABI tags are not covered and may show raw internal fields instead. To generate a Natvis file for such a configuration:
|
ABI tags are not covered and may show raw internal fields instead. To generate a Natvis file for such a configuration:
|
||||||
|
|
||||||
@@ -36,7 +29,7 @@ ABI tags are not covered and may show raw internal fields instead. To generate a
|
|||||||
|
|
||||||
4. Use the generated file instead of `nlohmann_json.natvis`.
|
4. Use the generated file instead of `nlohmann_json.natvis`.
|
||||||
|
|
||||||
`--abi-tags` can be repeated for several configurations, and `--all` generates every combination (about 60 MB for 11
|
`--abi-tags` can be repeated for several configurations, and `--all` generates every combination (about 60 MB for 12
|
||||||
ABI tags, doubling with each further tag). See
|
ABI tags, doubling with each further tag). See
|
||||||
[`tools/generate_natvis`](https://github.com/nlohmann/json/tree/develop/tools/generate_natvis) for all options.
|
[`tools/generate_natvis`](https://github.com/nlohmann/json/tree/develop/tools/generate_natvis) for all options.
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
-11880
File diff suppressed because it is too large.
Load diff
Generated
+211
-32
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -6,11 +6,6 @@ The checked-in `nlohmann_json.natvis` covers the default namespace (no ABI tags)
|
|||||||
of ABI tags, with both versioned and unversioned namespace variants. Configurations that enable three or more ABI tags
|
of ABI tags, with both versioned and unversioned namespace variants. Configurations that enable three or more ABI tags
|
||||||
are not covered by this reduced file; use `--abi-tags` or `--all` (see below) to generate a file for them.
|
are not covered by this reduced file; use `--abi-tags` or `--all` (see below) to generate a file for them.
|
||||||
|
|
||||||
The visualizers of `basic_json_document` and `basic_json_view` live in the same inline namespaces as `basic_json`, so the
|
|
||||||
ABI tags apply to them in the same way, and the `view_mode` template argument is covered by a wildcard: it does not
|
|
||||||
multiply the namespaces. Each namespace gets three more types (`basic_json_view<*,*>`, `basic_json_document<*,*>`, and
|
|
||||||
`detail::view::node`), which roughly doubles the size of the file.
|
|
||||||
|
|
||||||
The ABI tag list and the library version are parsed from
|
The ABI tag list and the library version are parsed from
|
||||||
`include/nlohmann/detail/abi_macros.hpp`, so this script must be re-run (via
|
`include/nlohmann/detail/abi_macros.hpp`, so this script must be re-run (via
|
||||||
`make natvis`) whenever an `NLOHMANN_JSON_ABI_TAG_*` macro is added to that
|
`make natvis`) whenever an `NLOHMANN_JSON_ABI_TAG_*` macro is added to that
|
||||||
@@ -40,7 +35,7 @@ version other than the one in `abi_macros.hpp`.
|
|||||||
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `--list-tags` | Print every ABI tag (in namespace order) with the macro that enables it and the `NLOHMANN_JSON_ABI_TAG_*` identifier, then exit. |
|
| `--list-tags` | Print every ABI tag (in namespace order) with the macro that enables it and the `NLOHMANN_JSON_ABI_TAG_*` identifier, then exit. |
|
||||||
| `--abi-tags TAGS` | Add the namespaces of one configuration, given as a comma-separated list of ABI tags (`_diag,_dp,_ns`; the leading underscore is optional, and the order does not matter). Can be repeated for several configurations. Unknown tags are an error. |
|
| `--abi-tags TAGS` | Add the namespaces of one configuration, given as a comma-separated list of ABI tags (`_diag,_dp,_ns`; the leading underscore is optional, and the order does not matter). Can be repeated for several configurations. Unknown tags are an error. |
|
||||||
| `--all` | Generate the namespaces for every combination of ABI tags. The file size doubles with each tag (about 60 MB for 11 tags). Cannot be combined with `--abi-tags`. |
|
| `--all` | Generate the namespaces for every combination of ABI tags. The file size doubles with each tag (about 60 MB for 12 tags). Cannot be combined with `--abi-tags`. |
|
||||||
| `--output FILE` | Write the Natvis file to `FILE` instead of `nlohmann_json.natvis` in the repository root. |
|
| `--output FILE` | Write the Natvis file to `FILE` instead of `nlohmann_json.natvis` in the repository root. |
|
||||||
|
|
||||||
Each configuration gets both the versioned (`json_abi_diag_dp_ns_v3_12_0`) and the unversioned (`json_abi_diag_dp_ns`,
|
Each configuration gets both the versioned (`json_abi_diag_dp_ns_v3_12_0`) and the unversioned (`json_abi_diag_dp_ns`,
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ if __name__ == '__main__':
|
|||||||
'optional) of one configuration to add to the default namespaces; can be repeated')
|
'optional) of one configuration to add to the default namespaces; can be repeated')
|
||||||
selection.add_argument('--all', action='store_true',
|
selection.add_argument('--all', action='store_true',
|
||||||
help='Generate the namespaces for every combination of ABI tags; the file size doubles '
|
help='Generate the namespaces for every combination of ABI tags; the file size doubles '
|
||||||
'with each ABI tag (about 60 MB for 11 tags)')
|
'with each ABI tag (about 60 MB for 12 tags)')
|
||||||
parser.add_argument('--list-tags', action='store_true',
|
parser.add_argument('--list-tags', action='store_true',
|
||||||
help='List the ABI tags with the macros that enable them, and exit')
|
help='List the ABI tags with the macros that enable them, and exit')
|
||||||
parser.add_argument('--output', metavar='FILE',
|
parser.add_argument('--output', metavar='FILE',
|
||||||
|
|||||||
@@ -5,39 +5,6 @@
|
|||||||
<!-- * * * * * * * * AUTO-GENERATED FILE * * * * * * * * -->
|
<!-- * * * * * * * * AUTO-GENERATED FILE * * * * * * * * -->
|
||||||
|
|
||||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
{# The children of the container node `node` (a pointer expression) of the document `doc` (a document_data pointer
|
|
||||||
expression). A node is only an index into the source text: strings are read from `doc`, so the strings of the
|
|
||||||
direct children are readable here, but the children of a nested container are shown as nodes (see the node
|
|
||||||
visualizer below). Containers with edits (flag 8, "moved") are not expanded. The node kinds are the numbers of
|
|
||||||
node_kind: 1 object, 2 array, 3 string; the float token is shown only by the node visualizer. #}
|
|
||||||
{% macro children(node, doc=none) %}
|
|
||||||
<CustomListItems MaxItemsPerView="1000">
|
|
||||||
<Variable Name="nd" InitialValue="{{ node }}" />
|
|
||||||
<Variable Name="obj" InitialValue="nd != 0 && (int)nd->kind == 1" />
|
|
||||||
<Variable Name="n" InitialValue="(nd != 0 && ((int)nd->kind == 1 || (int)nd->kind == 2) && (nd->flags & 8) == 0) ? (int)nd->len : 0" />
|
|
||||||
<Variable Name="p" InitialValue="nd + 1" />
|
|
||||||
<Variable Name="i" InitialValue="0" />
|
|
||||||
<Size>obj ? 2 * n : n</Size>
|
|
||||||
<Loop Condition="i < n">
|
|
||||||
{% if doc %}
|
|
||||||
<Item Name="key" Condition="obj">{{ doc }}->base._Elems[p->flags & 3] + p->off,[p->len]s8</Item>
|
|
||||||
{% else %}
|
|
||||||
<Item Name="key" Condition="obj">*p</Item>
|
|
||||||
{% endif %}
|
|
||||||
<Exec Condition="obj">p = p + 1</Exec>
|
|
||||||
{% if doc %}
|
|
||||||
<Item Name="value" Condition="obj && (int)p->kind == 3">{{ doc }}->base._Elems[p->flags & 3] + p->off,[p->len]s8</Item>
|
|
||||||
{% endif %}
|
|
||||||
<Item Name="value" Condition="obj{% if doc %} && (int)p->kind != 3{% endif %}">*p</Item>
|
|
||||||
{% if doc %}
|
|
||||||
<Item Condition="!obj && (int)p->kind == 3">{{ doc }}->base._Elems[p->flags & 3] + p->off,[p->len]s8</Item>
|
|
||||||
{% endif %}
|
|
||||||
<Item Condition="!obj{% if doc %} && (int)p->kind != 3{% endif %}">*p</Item>
|
|
||||||
<Exec>p = p + (((int)p->kind == 1 || (int)p->kind == 2) ? p->next : 1)</Exec>
|
|
||||||
<Exec>i = i + 1</Exec>
|
|
||||||
</Loop>
|
|
||||||
</CustomListItems>
|
|
||||||
{%- endmacro %}
|
|
||||||
{% for ns in namespaces %}
|
{% for ns in namespaces %}
|
||||||
<!-- Namespace {{ ns }} -->
|
<!-- Namespace {{ ns }} -->
|
||||||
<Type Name="{{ ns }}::basic_json<*>">
|
<Type Name="{{ ns }}::basic_json<*>">
|
||||||
@@ -95,44 +62,5 @@
|
|||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<!-- json_view / json_document (include/nlohmann/json_view.hpp). They live in the same inline namespaces as
|
|
||||||
basic_json, so the ABI tags need no extra entries; the view_mode is a template argument that the wildcard
|
|
||||||
covers. A view is a pointer to the document's data and a pointer to a node of its flat index; the
|
|
||||||
strings of the source are found through document_data::base (std::array of the MSVC STL: _Elems). -->
|
|
||||||
<Type Name="{{ ns }}::detail::view::node">
|
|
||||||
<DisplayString Condition="(int)kind == 0">null</DisplayString>
|
|
||||||
<DisplayString Condition="(int)kind == 1">object, size={len}</DisplayString>
|
|
||||||
<DisplayString Condition="(int)kind == 2">array, size={len}</DisplayString>
|
|
||||||
<DisplayString Condition="(int)kind == 3">string, length={len}</DisplayString>
|
|
||||||
<DisplayString Condition="(int)kind == 4">boolean: {(flags & 4) != 0}</DisplayString>
|
|
||||||
<DisplayString Condition="(int)kind == 5">int64: {(__int64)((unsigned __int64)len | ((unsigned __int64)next << 32))}</DisplayString>
|
|
||||||
<DisplayString Condition="(int)kind == 6">uint64: {(unsigned __int64)len | ((unsigned __int64)next << 32)}</DisplayString>
|
|
||||||
<DisplayString Condition="(int)kind == 7">float, length={len}</DisplayString>
|
|
||||||
<DisplayString>link</DisplayString>
|
|
||||||
<Expand>
|
|
||||||
{{ children('this') }}
|
|
||||||
</Expand>
|
|
||||||
</Type>
|
|
||||||
|
|
||||||
<Type Name="{{ ns }}::basic_json_view<*,*>">
|
|
||||||
<DisplayString Condition="m_node == 0">discarded</DisplayString>
|
|
||||||
<DisplayString Condition="(int)m_node->kind == 3">{m_doc->base._Elems[m_node->flags & 3] + m_node->off,[m_node->len]s8}</DisplayString>
|
|
||||||
<DisplayString Condition="(int)m_node->kind == 7">{m_doc->base._Elems[m_node->flags & 3] + m_node->off,[m_node->len]s8b}</DisplayString>
|
|
||||||
<DisplayString>{*m_node}</DisplayString>
|
|
||||||
<Expand>
|
|
||||||
{{ children('m_node', 'm_doc') }}
|
|
||||||
</Expand>
|
|
||||||
</Type>
|
|
||||||
|
|
||||||
<Type Name="{{ ns }}::basic_json_document<*,*>">
|
|
||||||
<DisplayString Condition="m_data._Mypair._Myval2 == 0 || m_data._Mypair._Myval2->discarded">discarded</DisplayString>
|
|
||||||
<DisplayString>{*m_data._Mypair._Myval2->nodes}</DisplayString>
|
|
||||||
<Expand>
|
|
||||||
<Item Name="[source]" Condition="m_data._Mypair._Myval2 != 0">m_data._Mypair._Myval2->src,[m_data._Mypair._Myval2->size]s8b</Item>
|
|
||||||
<Item Name="[nodes]" Condition="m_data._Mypair._Myval2 != 0">m_data._Mypair._Myval2->node_count</Item>
|
|
||||||
{{ children('m_data._Mypair._Myval2->nodes', 'm_data._Mypair._Myval2') }}
|
|
||||||
</Expand>
|
|
||||||
</Type>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</AutoVisualizer>
|
</AutoVisualizer>
|
||||||
Reference in new issue
Block a user