json_view: keep accept()'s string check out of run()

Clang 23 made run() slower on files without escapes (mesh.pretty +15 %) when
the check-only string path was a separate call target. Dispatch inside
slow_string() instead, so the call in run() is the same as before, and skip
the SIMD run scan between escapes that follow each other (twitterescaped
1.11 -> 0.82 against the previous commit).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-31 11:14:52 +01:00
1 parent 6b42cfd1c9
commit 7004ec71bb
3 files changed
+36 -36

No files matched your search

+11 -18
View File
@@ -16,7 +16,6 @@
#include <cstring> // memcmp, memcpy
#include <limits> // numeric_limits
#include <string> // string
#include <type_traits> // false_type, integral_constant, true_type
#include <vector> // vector
#include <nlohmann/json.hpp>
@@ -309,31 +308,25 @@ class builder
--p;
return failed(error_code::string_escape, p);
}
if (p != end && *p == '\\')
{
continue; // (escapes in a row: no run of text between them)
}
p = scan_string_run(p, end); // the text up to the next backslash, quote, control character, or ill-formed UTF-8
}
}
/// the string from p, decoded into the arena, or only checked (Store == false)
decoded decode_string(const unsigned char* s, const unsigned char* p)
{
return decode_string(s, p, std::integral_constant<bool, Store>());
}
decoded decode_string(const unsigned char* s, const unsigned char* p, std::true_type /*unused*/)
{
return slow_string(s, p);
}
decoded decode_string(const unsigned char* /*unused*/, const unsigned char* p, std::false_type /*unused*/)
{
return check_string(p);
}
/// s points to the start of the string, p to a backslash, a control
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
/// the arena
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
{
if (!enabled(Store))
{
// (accept(): the string is checked, not decoded)
static_cast<void>(s);
return check_string(p);
}
// single-character escapes; 0: invalid (and 'u', handled separately)
static const std::array<char, 128> simple_escape =
{
@@ -1271,7 +1264,7 @@ indent_done:
++p;
return true;
}
const decoded r = cold.decode_string(s, p);
const decoded r = cold.slow_string(s, p);
if (r.p == nullptr)
{
return false;
+11 -18
View File
@@ -88,7 +88,6 @@
#include <cstring> // memcmp, memcpy
#include <limits> // numeric_limits
#include <string> // string
#include <type_traits> // false_type, integral_constant, true_type
#include <vector> // vector
// #include <nlohmann/json.hpp>
@@ -1551,31 +1550,25 @@ class builder
--p;
return failed(error_code::string_escape, p);
}
if (p != end && *p == '\\')
{
continue; // (escapes in a row: no run of text between them)
}
p = scan_string_run(p, end); // the text up to the next backslash, quote, control character, or ill-formed UTF-8
}
}
/// the string from p, decoded into the arena, or only checked (Store == false)
decoded decode_string(const unsigned char* s, const unsigned char* p)
{
return decode_string(s, p, std::integral_constant<bool, Store>());
}
decoded decode_string(const unsigned char* s, const unsigned char* p, std::true_type /*unused*/)
{
return slow_string(s, p);
}
decoded decode_string(const unsigned char* /*unused*/, const unsigned char* p, std::false_type /*unused*/)
{
return check_string(p);
}
/// s points to the start of the string, p to a backslash, a control
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
/// the arena
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
{
if (!enabled(Store))
{
// (accept(): the string is checked, not decoded)
static_cast<void>(s);
return check_string(p);
}
// single-character escapes; 0: invalid (and 'u', handled separately)
static const std::array<char, 128> simple_escape =
{
@@ -2513,7 +2506,7 @@ indent_done:
++p;
return true;
}
const decoded r = cold.decode_string(s, p);
const decoded r = cold.slow_string(s, p);
if (r.p == nullptr)
{
return false;
+14
View File
@@ -553,6 +553,20 @@ TEST_CASE("json_view builder")
}
}
SECTION("escapes in a row")
{
for (const char* content :
{
"\\n", "\\n\\t\\\\", "\\u00e9\\u00e9\\u4e2d", "\\u00e9\\u00e", "\\u00e9\\", "\\n\\q", "\\n\\ud83d\\ude00\\n",
"\\ud83d\\ude00\\ud83d", "\\n\\n\\n abc \\n\\n", "\\n\x01", "\\n\xC3\x28", "\\n\\"
})
{
check_accept(std::string("[\"") + content + "\"]");
check_accept(std::string("[\"") + content);
check_accept(std::string("{\"") + content + "\":1}");
}
}
SECTION("test files")
{
for (const char* name :