From f2c0e8b6f96329cb9e654cf6768231b83710b9cd Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Oct 2026 21:22:53 +0200 Subject: [PATCH] Skip the token copy for array indices and the prefix look-up for a first child 0 in unflatten() Array elements are addressed by a plain index of up to 9 digits in nearly all flattened documents. Parse it straight from the key instead of copying the token into a string and converting that. A new container whose first child has the token 0 is an array by definition, so the hash set of array prefixes is only consulted for other first children. unflatten() (instructions on macOS, Linux Ir GCC 13 / Clang 18): canada 188.4M -> 161.1M, 151.3M -> 128.1M / 162.3M -> 138.0M; citm_catalog 45.0M -> 43.3M, 40.3M -> 38.9M / 41.1M -> 39.6M; twitter unchanged. Output and exceptions are identical to develop over the differential check. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/json_pointer.hpp | 25 ++++++++++++++++++++++-- single_include/nlohmann/json.hpp | 25 ++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index dc88bf046..d421a8e0f 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -1595,8 +1595,28 @@ class json_pointer { end = key.size(); } + const std::size_t token_size = end - pos - 1; + + // a token that is a plain index of up to 9 digits, which is + // the case for almost all array elements, is not copied + std::size_t index = 0; + bool plain_index = current->type() == detail::value_t::array && token_size != 0 && token_size <= 9 && (token_size == 1 || data[pos + 1] != '0'); + for (std::size_t k = pos + 1; plain_index && k < end; ++k) + { + plain_index = data[k] >= '0' && data[k] <= '9'; + index = (index * 10) + static_cast(data[k] - '0'); + } + if (plain_index) + { + current = ¤t->operator[](static_cast(index)); + values.push_back(current); + ends.push_back(end); + pos = end; + continue; + } + token.clear(); - token.append(data + pos + 1, end - pos - 1); + token.append(data + pos + 1, token_size); if (token.find_first_of('~') != string_t::npos) { detail::unescape(token); @@ -1606,7 +1626,8 @@ class json_pointer { case detail::value_t::null: { - if (arrays.contains(data, pos)) + // a first child 0 makes the value an array without a look-up + if ((token_size == 1 && data[pos + 1] == '0') || arrays.contains(data, pos)) { // some reference token below this position is 0, so the // value is an array diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 493b3c5d6..12846dedc 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -23945,8 +23945,28 @@ class json_pointer { end = key.size(); } + const std::size_t token_size = end - pos - 1; + + // a token that is a plain index of up to 9 digits, which is + // the case for almost all array elements, is not copied + std::size_t index = 0; + bool plain_index = current->type() == detail::value_t::array && token_size != 0 && token_size <= 9 && (token_size == 1 || data[pos + 1] != '0'); + for (std::size_t k = pos + 1; plain_index && k < end; ++k) + { + plain_index = data[k] >= '0' && data[k] <= '9'; + index = (index * 10) + static_cast(data[k] - '0'); + } + if (plain_index) + { + current = ¤t->operator[](static_cast(index)); + values.push_back(current); + ends.push_back(end); + pos = end; + continue; + } + token.clear(); - token.append(data + pos + 1, end - pos - 1); + token.append(data + pos + 1, token_size); if (token.find_first_of('~') != string_t::npos) { detail::unescape(token); @@ -23956,7 +23976,8 @@ class json_pointer { case detail::value_t::null: { - if (arrays.contains(data, pos)) + // a first child 0 makes the value an array without a look-up + if ((token_size == 1 && data[pos + 1] == '0') || arrays.contains(data, pos)) { // some reference token below this position is 0, so the // value is an array