From 059de71cfdc62d81affe239eae61987a7aedb71d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Oct 2026 22:11:29 +0200 Subject: [PATCH] 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 --- include/nlohmann/detail/view/builder.hpp | 16 ++++++++-------- single_include/nlohmann/json_view.hpp | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/nlohmann/detail/view/builder.hpp b/include/nlohmann/detail/view/builder.hpp index 3eb85ce47..400684a43 100644 --- a/include/nlohmann/detail/view/builder.hpp +++ b/include/nlohmann/detail/view/builder.hpp @@ -954,11 +954,11 @@ indent_done: ++p; } const unsigned char* const int_start = p; - if (p != e && *p == '0') + if (cur() == '0') { ++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); } @@ -969,7 +969,7 @@ indent_done: const auto int_digits = static_cast(p - int_start); std::size_t frac_digits = 0; bool is_float = false; - if (p != e && *p == '.') + if (cur() == '.') { ++p; const unsigned char* const f0 = p; @@ -982,20 +982,20 @@ indent_done: is_float = true; } std::int64_t exponent = 0; - if (p != e && (*p | 0x20) == 'e') + if ((cur() | 0x20) == 'e') { ++p; bool exp_negative = false; - if (p != e && (*p == '+' || *p == '-')) + if (cur() == '+' || cur() == '-') { exp_negative = *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); } - while (p != e && is_digit(*p)) + while (is_digit(cur())) { if (exponent < 100000) { @@ -1057,7 +1057,7 @@ indent_done: ++p; // opening quote const unsigned char* const s = p; p = scan_string_run(p, e); - if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"')) + if (NLOHMANN_VIEW_LIKELY(cur() == '"')) { push_node(value_t::string, 0, 0, static_cast(s - b), static_cast(p - s)); ++p; diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index f5f2fe9db..0207ba7e2 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -2116,11 +2116,11 @@ indent_done: ++p; } const unsigned char* const int_start = p; - if (p != e && *p == '0') + if (cur() == '0') { ++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); } @@ -2131,7 +2131,7 @@ indent_done: const auto int_digits = static_cast(p - int_start); std::size_t frac_digits = 0; bool is_float = false; - if (p != e && *p == '.') + if (cur() == '.') { ++p; const unsigned char* const f0 = p; @@ -2144,20 +2144,20 @@ indent_done: is_float = true; } std::int64_t exponent = 0; - if (p != e && (*p | 0x20) == 'e') + if ((cur() | 0x20) == 'e') { ++p; bool exp_negative = false; - if (p != e && (*p == '+' || *p == '-')) + if (cur() == '+' || cur() == '-') { exp_negative = *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); } - while (p != e && is_digit(*p)) + while (is_digit(cur())) { if (exponent < 100000) { @@ -2219,7 +2219,7 @@ indent_done: ++p; // opening quote const unsigned char* const s = p; p = scan_string_run(p, e); - if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"')) + if (NLOHMANN_VIEW_LIKELY(cur() == '"')) { push_node(value_t::string, 0, 0, static_cast(s - b), static_cast(p - s)); ++p;