Skip the bounds checks of number and string tokens in NUL-terminated input

For a NUL-terminated input (std::string, C string, string literal), the
parser reads the terminator instead of comparing p with the end, via
cursor::cur(). Number and string scanning still tested `p != e` before each
look at the next byte (after the integer digits for '.', for 'e', after
the exponent sign and digits, and after a string for its closing quote),
although the terminator can never be one of those characters. They now use
cur(), which is the plain load for NUL-terminated input and the bounds check
for ptr+len input, as before.

Parsing, Apple M1 instructions per parse: twitter 3.126 M -> 3.089 M (-1.2 %),
citm_catalog 8.70 M -> 8.58 M (-1.3 %), jeopardy 306.1 M -> 302.5 M (-1.2 %), signed
integers 281 M -> 266 M (-5.4 %), canada and floats -0.1 % / -0.9 %.
Linux x86-64 Ir, GCC 13 / Clang 18: twitter -0.5 % / -0.0 %, signed integers +0.2 % / -0.7 %.

Belongs to the PR "json_view: read-side speedups".

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-22 09:30:14 +02:00
1 parent c8b380769c
commit 059de71cfd
2 files changed
+16 -16

No files matched your search

+8 -8
View File
@@ -954,11 +954,11 @@ indent_done:
++p; ++p;
} }
const unsigned char* const int_start = p; const unsigned char* const int_start = p;
if (p != e && *p == '0') if (cur() == '0')
{ {
++p; ++p;
} }
else if (NLOHMANN_VIEW_LIKELY(p != e && *p >= '1' && *p <= '9')) else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
{ {
p = skip_digits(p + 1, e); p = skip_digits(p + 1, e);
} }
@@ -969,7 +969,7 @@ indent_done:
const auto int_digits = static_cast<std::size_t>(p - int_start); const auto int_digits = static_cast<std::size_t>(p - int_start);
std::size_t frac_digits = 0; std::size_t frac_digits = 0;
bool is_float = false; bool is_float = false;
if (p != e && *p == '.') if (cur() == '.')
{ {
++p; ++p;
const unsigned char* const f0 = p; const unsigned char* const f0 = p;
@@ -982,20 +982,20 @@ indent_done:
is_float = true; is_float = true;
} }
std::int64_t exponent = 0; std::int64_t exponent = 0;
if (p != e && (*p | 0x20) == 'e') if ((cur() | 0x20) == 'e')
{ {
++p; ++p;
bool exp_negative = false; bool exp_negative = false;
if (p != e && (*p == '+' || *p == '-')) if (cur() == '+' || cur() == '-')
{ {
exp_negative = *p == '-'; exp_negative = *p == '-';
++p; ++p;
} }
if (NLOHMANN_VIEW_UNLIKELY(p == e || !is_digit(*p))) if (NLOHMANN_VIEW_UNLIKELY(!is_digit(cur())))
{ {
return fail(error_code::number_after_exponent); return fail(error_code::number_after_exponent);
} }
while (p != e && is_digit(*p)) while (is_digit(cur()))
{ {
if (exponent < 100000) if (exponent < 100000)
{ {
@@ -1057,7 +1057,7 @@ indent_done:
++p; // opening quote ++p; // opening quote
const unsigned char* const s = p; const unsigned char* const s = p;
p = scan_string_run<Value>(p, e); p = scan_string_run<Value>(p, e);
if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"')) if (NLOHMANN_VIEW_LIKELY(cur() == '"'))
{ {
push_node(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s)); push_node(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
++p; ++p;
+8 -8
View File
@@ -2116,11 +2116,11 @@ indent_done:
++p; ++p;
} }
const unsigned char* const int_start = p; const unsigned char* const int_start = p;
if (p != e && *p == '0') if (cur() == '0')
{ {
++p; ++p;
} }
else if (NLOHMANN_VIEW_LIKELY(p != e && *p >= '1' && *p <= '9')) else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
{ {
p = skip_digits(p + 1, e); p = skip_digits(p + 1, e);
} }
@@ -2131,7 +2131,7 @@ indent_done:
const auto int_digits = static_cast<std::size_t>(p - int_start); const auto int_digits = static_cast<std::size_t>(p - int_start);
std::size_t frac_digits = 0; std::size_t frac_digits = 0;
bool is_float = false; bool is_float = false;
if (p != e && *p == '.') if (cur() == '.')
{ {
++p; ++p;
const unsigned char* const f0 = p; const unsigned char* const f0 = p;
@@ -2144,20 +2144,20 @@ indent_done:
is_float = true; is_float = true;
} }
std::int64_t exponent = 0; std::int64_t exponent = 0;
if (p != e && (*p | 0x20) == 'e') if ((cur() | 0x20) == 'e')
{ {
++p; ++p;
bool exp_negative = false; bool exp_negative = false;
if (p != e && (*p == '+' || *p == '-')) if (cur() == '+' || cur() == '-')
{ {
exp_negative = *p == '-'; exp_negative = *p == '-';
++p; ++p;
} }
if (NLOHMANN_VIEW_UNLIKELY(p == e || !is_digit(*p))) if (NLOHMANN_VIEW_UNLIKELY(!is_digit(cur())))
{ {
return fail(error_code::number_after_exponent); return fail(error_code::number_after_exponent);
} }
while (p != e && is_digit(*p)) while (is_digit(cur()))
{ {
if (exponent < 100000) if (exponent < 100000)
{ {
@@ -2219,7 +2219,7 @@ indent_done:
++p; // opening quote ++p; // opening quote
const unsigned char* const s = p; const unsigned char* const s = p;
p = scan_string_run<Value>(p, e); p = scan_string_run<Value>(p, e);
if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"')) if (NLOHMANN_VIEW_LIKELY(cur() == '"'))
{ {
push_node(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s)); push_node(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
++p; ++p;