From f930ed5f71ef9bd635fd6793bd1bc9260e9406ca Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Oct 2026 21:41:08 +0200 Subject: [PATCH] Write the path of an array element in flatten() with a single resize Replacing the path of the previous child by "/" took a resize, a push_back and an append. For array elements the path is now resized once to its final length and the slash and the digits are written into it. Object keys keep the three calls: a version that also wrote them directly made flatten() of twitter 4 % slower in cycles at equal instructions. flatten() (instructions on macOS, Linux Ir GCC 13 / Clang 18): canada 172.1M -> 164.7M, 123.8M -> 120.6M / 121.2M -> 118.9M; citm_catalog 40.4M -> 39.5M, 30.1M -> 29.6M / 30.0M -> 29.6M; twitter unchanged. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/json_pointer.hpp | 22 ++++++++++++++++++---- single_include/nlohmann/json.hpp | 22 ++++++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 5c9fd3836..ffa20d639 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -1517,8 +1517,9 @@ class json_pointer // frame, which would invalidate a reference to it const BasicJsonType* const container = stack.back().container; - // drop the path of the previous child - path.resize(stack.back().path_length); + // the path of the previous child is replaced by the path of the + // next one; for an array element in one resize + const std::size_t path_length = stack.back().path_length; if (container->is_array()) { @@ -1532,8 +1533,20 @@ class json_pointer // iterate array and use index as a reference string ++stack.back().index; - path.push_back('/'); - append_index(path, i); + std::array digits{}; + std::size_t pos = digits.size(); + std::size_t rest = i; + do + { + digits[--pos] = static_cast('0' + rest % 10); + rest /= 10; + } + while (rest != 0); + const std::size_t count = digits.size() - pos; + path.resize(path_length + 1 + count); + char* const out = &path[path_length]; + out[0] = '/'; + std::memcpy(out + 1, digits.data() + pos, count); enter(array[i]); } else @@ -1547,6 +1560,7 @@ class json_pointer // iterate object and use keys as reference string ++stack.back().member; + path.resize(path_length); path.push_back('/'); detail::escape_into(path, it->first); enter(it->second); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 6917083c5..b9bbe42e8 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -23867,8 +23867,9 @@ class json_pointer // frame, which would invalidate a reference to it const BasicJsonType* const container = stack.back().container; - // drop the path of the previous child - path.resize(stack.back().path_length); + // the path of the previous child is replaced by the path of the + // next one; for an array element in one resize + const std::size_t path_length = stack.back().path_length; if (container->is_array()) { @@ -23882,8 +23883,20 @@ class json_pointer // iterate array and use index as a reference string ++stack.back().index; - path.push_back('/'); - append_index(path, i); + std::array digits{}; + std::size_t pos = digits.size(); + std::size_t rest = i; + do + { + digits[--pos] = static_cast('0' + rest % 10); + rest /= 10; + } + while (rest != 0); + const std::size_t count = digits.size() - pos; + path.resize(path_length + 1 + count); + char* const out = &path[path_length]; + out[0] = '/'; + std::memcpy(out + 1, digits.data() + pos, count); enter(array[i]); } else @@ -23897,6 +23910,7 @@ class json_pointer // iterate object and use keys as reference string ++stack.back().member; + path.resize(path_length); path.push_back('/'); detail::escape_into(path, it->first); enter(it->second);