diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index d421a8e0f..5c9fd3836 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -1354,6 +1354,52 @@ class json_pointer obj[key] = std::forward(v); } + /// the number of entries that flattening @a value adds: its primitive + /// values and empty containers + template + static std::size_t count_flattened(const BasicJsonType& value) + { + std::size_t count = 0; + std::vector pending(1, &value); + while (!pending.empty()) + { + const BasicJsonType* const v = pending.back(); + pending.pop_back(); + if (v->is_array() && !v->m_data.m_value.array->empty()) + { + for (const auto& element : *v->m_data.m_value.array) + { + pending.push_back(&element); + } + } + else if (v->is_object() && !v->m_data.m_value.object->empty()) + { + for (const auto& member : *v->m_data.m_value.object) + { + pending.push_back(&member.second); + } + } + else + { + ++count; + } + } + return count; + } + + // Reserve the storage for @a value of objects that have a reserve(), so + // that it does not have to be moved while it grows + template + static auto reserve_flattened(ObjectType& obj, const BasicJsonType& value, detail::priority_tag<1> /*unused*/) + -> decltype(obj.reserve(std::size_t {}), void()) + { + obj.reserve(count_flattened(value)); + } + + template + static void reserve_flattened(ObjectType& /*obj*/, const BasicJsonType& /*value*/, detail::priority_tag<0> /*unused*/) + {} + /// append the decimal representation of @a i to @a out static void append_index(string_t& out, std::size_t i) { @@ -1463,6 +1509,7 @@ class json_pointer } }; + reserve_flattened(object, value, detail::priority_tag<1> {}); enter(value); while (!stack.empty()) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 12846dedc..e53b6ec4c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -23704,6 +23704,52 @@ class json_pointer obj[key] = std::forward(v); } + /// the number of entries that flattening @a value adds: its primitive + /// values and empty containers + template + static std::size_t count_flattened(const BasicJsonType& value) + { + std::size_t count = 0; + std::vector pending(1, &value); + while (!pending.empty()) + { + const BasicJsonType* const v = pending.back(); + pending.pop_back(); + if (v->is_array() && !v->m_data.m_value.array->empty()) + { + for (const auto& element : *v->m_data.m_value.array) + { + pending.push_back(&element); + } + } + else if (v->is_object() && !v->m_data.m_value.object->empty()) + { + for (const auto& member : *v->m_data.m_value.object) + { + pending.push_back(&member.second); + } + } + else + { + ++count; + } + } + return count; + } + + // Reserve the storage for @a value of objects that have a reserve(), so + // that it does not have to be moved while it grows + template + static auto reserve_flattened(ObjectType& obj, const BasicJsonType& value, detail::priority_tag<1> /*unused*/) + -> decltype(obj.reserve(std::size_t {}), void()) + { + obj.reserve(count_flattened(value)); + } + + template + static void reserve_flattened(ObjectType& /*obj*/, const BasicJsonType& /*value*/, detail::priority_tag<0> /*unused*/) + {} + /// append the decimal representation of @a i to @a out static void append_index(string_t& out, std::size_t i) { @@ -23813,6 +23859,7 @@ class json_pointer } }; + reserve_flattened(object, value, detail::priority_tag<1> {}); enter(value); while (!stack.empty()) {