From a89d3d273874c55bf6d619ca0f5ae06f2a4af8ad Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Oct 2026 21:46:38 +0200 Subject: [PATCH] Search once for a new object member in unflatten() and j[json_pointer] object_member() looked a key up and, if it was missing, inserted it with operator[], which searches again. Objects that can find the position of a key and insert there (std::map) now do both with one search; other object types are unchanged. A key that exists is not copied, as before. unflatten() (instructions on macOS, Linux Ir GCC 13 / Clang 18): twitter 25.8M -> 24.2M, 24.9M -> 21.8M / 24.8M -> 21.5M; citm_catalog 43.4M -> 43.5M, 38.9M -> 37.0M / 39.6M -> 37.5M; canada unchanged. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/json_pointer.hpp | 27 +++++++++++++++++++++++- single_include/nlohmann/json.hpp | 27 +++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index ffa20d639..9d87d4f92 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -482,8 +482,27 @@ class json_pointer } /// @brief object member access with a key that is only copied if it is inserted + /// + /// Objects that can find the position of a key and insert there + /// (std::map) search once, others look the key up and add it if it is missing. template - static BasicJsonType& object_member(BasicJsonType& object, const string_t& key) + static auto object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<1> /*unused*/) + -> decltype(object.m_data.m_value.object->emplace_hint(object.m_data.m_value.object->lower_bound(key), key, nullptr), std::declval()) + { + auto& members = *object.m_data.m_value.object; + auto it = members.lower_bound(key); + if (it != members.end() && !members.key_comp()(key, it->first)) + { + return it->second; + } + + const auto old_capacity = object.container_capacity(); + it = members.emplace_hint(it, key, nullptr); + return object.set_parent_after_insert(it->second, old_capacity); + } + + template + static BasicJsonType& object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<0> /*unused*/) { auto& members = *object.m_data.m_value.object; const auto it = members.find(key); @@ -494,6 +513,12 @@ class json_pointer return object.operator[](key); } + template + static BasicJsonType& object_member(BasicJsonType& object, const string_t& key) + { + return object_member_impl(object, key, detail::priority_tag<1> {}); + } + JSON_PRIVATE_UNLESS_TESTED: json_pointer top() const { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index b9bbe42e8..0fd6ebb1b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -22832,8 +22832,27 @@ class json_pointer } /// @brief object member access with a key that is only copied if it is inserted + /// + /// Objects that can find the position of a key and insert there + /// (std::map) search once, others look the key up and add it if it is missing. template - static BasicJsonType& object_member(BasicJsonType& object, const string_t& key) + static auto object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<1> /*unused*/) + -> decltype(object.m_data.m_value.object->emplace_hint(object.m_data.m_value.object->lower_bound(key), key, nullptr), std::declval()) + { + auto& members = *object.m_data.m_value.object; + auto it = members.lower_bound(key); + if (it != members.end() && !members.key_comp()(key, it->first)) + { + return it->second; + } + + const auto old_capacity = object.container_capacity(); + it = members.emplace_hint(it, key, nullptr); + return object.set_parent_after_insert(it->second, old_capacity); + } + + template + static BasicJsonType& object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<0> /*unused*/) { auto& members = *object.m_data.m_value.object; const auto it = members.find(key); @@ -22844,6 +22863,12 @@ class json_pointer return object.operator[](key); } + template + static BasicJsonType& object_member(BasicJsonType& object, const string_t& key) + { + return object_member_impl(object, key, detail::priority_tag<1> {}); + } + JSON_PRIVATE_UNLESS_TESTED: json_pointer top() const {