json_view: check_utf8 skips well-formed runs with the parser's string scan

Strings of 32 bytes or more that are written into an editable document
are validated with scan_string_run(), which skips well-formed UTF-8 16
bytes at a time; the byte-wise check runs only from where the scan found
ill-formed UTF-8, to report the same type_error.316. Shorter strings
keep the byte-wise check (inlined with a constant start, so that this
path is the code it was), where the vector scan costs more than it
saves.

set()/push_back() of strings of 32 bytes or more take 0.29-0.76 of the
time (x86-64 GCC, averaged over code layouts) and 0.25-0.80 (AArch64
clang); shorter strings take 1.5-4 % fewer instructions; the json_view
rows of bench_edit execute at most 0.04 % more instructions.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-26 07:13:32 +01:00
1 parent fc3b716675
commit 8d30cc24bf
3 files changed
+201 -6

No files matched your search

+41 -3
View File
@@ -3727,6 +3727,8 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/view/node.hpp>
// #include <nlohmann/detail/view/scan.hpp>
// #include <nlohmann/detail/view/view_mode.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
@@ -3775,10 +3777,9 @@ struct first_true<false, Conditions...> : std::integral_constant < int, 1 + firs
/// (type_error.316 with the same message), so that an editable document
/// only holds valid UTF-8: the error is at the first byte that no
/// well-formed sequence can continue with (Unicode, Table 3-7).
inline void check_utf8(const char* s, std::size_t n)
NLOHMANN_VIEW_ALWAYS_INLINE void check_utf8_bytewise(const unsigned char* p, std::size_t n, std::size_t i)
{
const auto* const p = reinterpret_cast<const unsigned char*>(s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
for (std::size_t i = 0; i < n;)
for (; i < n;)
{
const unsigned char c = p[i];
if (c < 0x80)
@@ -3808,6 +3809,43 @@ inline void check_utf8(const char* s, std::size_t n)
}
}
/// The long-string path of check_utf8(): skips well-formed runs with the
/// parser's string scan (which stops at a quote, a backslash, or a control
/// character, all legal here, and at ill-formed UTF-8); the byte-wise check
/// runs only from where the scan found ill-formed UTF-8, to report it.
NLOHMANN_VIEW_NOINLINE inline void check_utf8_scan(const unsigned char* p, std::size_t n)
{
const unsigned char* const e = p + n;
const unsigned char* q = p;
for (;;)
{
q = scan_string_run<true>(q, e);
if (q == e)
{
return;
}
if (*q >= 0x80)
{
check_utf8_bytewise(p, n, static_cast<std::size_t>(q - p));
return; // not reached: the scan stopped at ill-formed UTF-8
}
++q; // a quote, backslash, or control character
}
}
inline void check_utf8(const char* s, std::size_t n)
{
const auto* const p = reinterpret_cast<const unsigned char*>(s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
if (NLOHMANN_VIEW_UNLIKELY(n >= 32))
{
check_utf8_scan(p, n);
return;
}
// short strings: the vector scan costs more than it saves; (the loop is
// inlined with a constant start, so this path is the plain byte-wise check)
check_utf8_bytewise(p, n, 0);
}
/*!
@brief the edits of an editable basic_json_document