From d5ac35142622f35841a78b356f33618f03c85629 Mon Sep 17 00:00:00 2001 From: mistersandman Date: Mon, 14 Jan 2019 21:16:43 +0100 Subject: [PATCH] Explicit iterative destruction of JSON containers (objects and arrays) instead of implicit recursive destruction in order to avoid stack overflows for deeply nested hierarchies --- include/nlohmann/json.hpp | 75 +++++++++++++++++++++++++++----- single_include/nlohmann/json.hpp | 75 +++++++++++++++++++++++++++----- 2 files changed, 130 insertions(+), 20 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 3b3da50ff..b2b41c76b 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -997,18 +997,73 @@ class basic_json switch (t) { case value_t::object: - { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, object); - std::allocator_traits::deallocate(alloc, object, 1); - break; - } - case value_t::array: { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, array); - std::allocator_traits::deallocate(alloc, array, 1); + if ((t == value_t::object && !this->object->empty()) || (t == value_t::array && !this->array->empty())) + { + std::vector> stack; + stack.push_back(std::make_pair(this, t)); + while (!stack.empty()) + { + json_value* value; + value_t type; + std::tie(value, type) = stack.back(); + if (type == value_t::object) + { + while (!value->object->empty()) + { + basic_json& inner_value = value->object->begin()->second; + value_t inner_type = inner_value.type(); + if ((inner_type == value_t::object || inner_type == value_t::array) && !inner_value.empty()) + { + stack.push_back(std::make_pair(&inner_value.m_value, inner_type)); + break; + } + else + { + value->object->erase(value->object->begin()); + } + } + if (value->object->empty()) + { + stack.pop_back(); + } + } + else + { + while (!value->array->empty()) + { + basic_json& inner_value = value->array->back(); + value_t inner_type = inner_value.type(); + if ((inner_type == value_t::object || inner_type == value_t::array) && !inner_value.empty()) + { + stack.push_back(std::make_pair(&inner_value.m_value, inner_type)); + break; + } + else + { + value->array->pop_back(); + } + } + if (value->array->empty()) + { + stack.pop_back(); + } + } + } + } + if (t == value_t::object) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + } + else + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + } break; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 70b6c8a85..a90ae2736 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -13465,18 +13465,73 @@ class basic_json switch (t) { case value_t::object: - { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, object); - std::allocator_traits::deallocate(alloc, object, 1); - break; - } - case value_t::array: { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, array); - std::allocator_traits::deallocate(alloc, array, 1); + if ((t == value_t::object && !this->object->empty()) || (t == value_t::array && !this->array->empty())) + { + std::vector> stack; + stack.push_back(std::make_pair(this, t)); + while (!stack.empty()) + { + json_value* value; + value_t type; + std::tie(value, type) = stack.back(); + if (type == value_t::object) + { + while (!value->object->empty()) + { + basic_json& inner_value = value->object->begin()->second; + value_t inner_type = inner_value.type(); + if ((inner_type == value_t::object || inner_type == value_t::array) && !inner_value.empty()) + { + stack.push_back(std::make_pair(&inner_value.m_value, inner_type)); + break; + } + else + { + value->object->erase(value->object->begin()); + } + } + if (value->object->empty()) + { + stack.pop_back(); + } + } + else + { + while (!value->array->empty()) + { + basic_json& inner_value = value->array->back(); + value_t inner_type = inner_value.type(); + if ((inner_type == value_t::object || inner_type == value_t::array) && !inner_value.empty()) + { + stack.push_back(std::make_pair(&inner_value.m_value, inner_type)); + break; + } + else + { + value->array->pop_back(); + } + } + if (value->array->empty()) + { + stack.pop_back(); + } + } + } + } + if (t == value_t::object) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + } + else + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + } break; }