Merge 8103283685 into f3dc4684b4
This commit is contained in:
commit
fe0942314a
@ -568,51 +568,65 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
if (t == value_t::array || t == value_t::object)
|
if (t == value_t::array || t == value_t::object)
|
||||||
{
|
{
|
||||||
// flatten the current json_value to a heap-allocated stack
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
|
||||||
std::vector<basic_json> stack;
|
try
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
// flatten the current json_value to a heap-allocated stack
|
||||||
|
std::vector<basic_json, allocator_type> stack;
|
||||||
|
|
||||||
// move the top-level items to stack
|
// move the top-level items to stack
|
||||||
if (t == value_t::array)
|
if (t == value_t::array)
|
||||||
{
|
|
||||||
stack.reserve(array->size());
|
|
||||||
std::move(array->begin(), array->end(), std::back_inserter(stack));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stack.reserve(object->size());
|
|
||||||
for (auto&& it : *object)
|
|
||||||
{
|
{
|
||||||
stack.push_back(std::move(it.second));
|
stack.reserve(array->size());
|
||||||
|
std::move(array->begin(), array->end(), std::back_inserter(stack));
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
while (!stack.empty())
|
|
||||||
{
|
|
||||||
// move the last item to local variable to be processed
|
|
||||||
basic_json current_item(std::move(stack.back()));
|
|
||||||
stack.pop_back();
|
|
||||||
|
|
||||||
// if current_item is array/object, move
|
|
||||||
// its children to the stack to be processed later
|
|
||||||
if (current_item.is_array())
|
|
||||||
{
|
{
|
||||||
std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));
|
stack.reserve(object->size());
|
||||||
|
for (auto&& it : *object)
|
||||||
current_item.m_data.m_value.array->clear();
|
|
||||||
}
|
|
||||||
else if (current_item.is_object())
|
|
||||||
{
|
|
||||||
for (auto&& it : *current_item.m_data.m_value.object)
|
|
||||||
{
|
{
|
||||||
stack.push_back(std::move(it.second));
|
stack.push_back(std::move(it.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
current_item.m_data.m_value.object->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's now safe that current_item get destructed
|
while (!stack.empty())
|
||||||
// since it doesn't have any children
|
{
|
||||||
|
// move the last item to local variable to be processed
|
||||||
|
basic_json current_item(std::move(stack.back()));
|
||||||
|
stack.pop_back();
|
||||||
|
|
||||||
|
// if current_item is array/object, move
|
||||||
|
// its children to the stack to be processed later
|
||||||
|
if (current_item.is_array())
|
||||||
|
{
|
||||||
|
std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));
|
||||||
|
|
||||||
|
current_item.m_data.m_value.array->clear();
|
||||||
|
}
|
||||||
|
else if (current_item.is_object())
|
||||||
|
{
|
||||||
|
for (auto&& it : *current_item.m_data.m_value.object)
|
||||||
|
{
|
||||||
|
stack.push_back(std::move(it.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_item.m_data.m_value.object->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's now safe that current_item get destructed
|
||||||
|
// since it doesn't have any children
|
||||||
|
}
|
||||||
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
|
||||||
}
|
}
|
||||||
|
catch (...) // NOLINT(bugprone-empty-catch)
|
||||||
|
{
|
||||||
|
// Recursion avoidance has issue allocating temporary space. This may have been `std::bad_alloc`
|
||||||
|
// or any other exception thrown by a custom allocator.
|
||||||
|
// RAII will correctly clean up anything moved into `stack`.
|
||||||
|
// Then we continue with regular recursion based destroy, which will not heap allocate.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (t)
|
switch (t)
|
||||||
|
|||||||
@ -20567,51 +20567,65 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
if (t == value_t::array || t == value_t::object)
|
if (t == value_t::array || t == value_t::object)
|
||||||
{
|
{
|
||||||
// flatten the current json_value to a heap-allocated stack
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
|
||||||
std::vector<basic_json> stack;
|
try
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
// flatten the current json_value to a heap-allocated stack
|
||||||
|
std::vector<basic_json, allocator_type> stack;
|
||||||
|
|
||||||
// move the top-level items to stack
|
// move the top-level items to stack
|
||||||
if (t == value_t::array)
|
if (t == value_t::array)
|
||||||
{
|
|
||||||
stack.reserve(array->size());
|
|
||||||
std::move(array->begin(), array->end(), std::back_inserter(stack));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stack.reserve(object->size());
|
|
||||||
for (auto&& it : *object)
|
|
||||||
{
|
{
|
||||||
stack.push_back(std::move(it.second));
|
stack.reserve(array->size());
|
||||||
|
std::move(array->begin(), array->end(), std::back_inserter(stack));
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
while (!stack.empty())
|
|
||||||
{
|
|
||||||
// move the last item to local variable to be processed
|
|
||||||
basic_json current_item(std::move(stack.back()));
|
|
||||||
stack.pop_back();
|
|
||||||
|
|
||||||
// if current_item is array/object, move
|
|
||||||
// its children to the stack to be processed later
|
|
||||||
if (current_item.is_array())
|
|
||||||
{
|
{
|
||||||
std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));
|
stack.reserve(object->size());
|
||||||
|
for (auto&& it : *object)
|
||||||
current_item.m_data.m_value.array->clear();
|
|
||||||
}
|
|
||||||
else if (current_item.is_object())
|
|
||||||
{
|
|
||||||
for (auto&& it : *current_item.m_data.m_value.object)
|
|
||||||
{
|
{
|
||||||
stack.push_back(std::move(it.second));
|
stack.push_back(std::move(it.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
current_item.m_data.m_value.object->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's now safe that current_item get destructed
|
while (!stack.empty())
|
||||||
// since it doesn't have any children
|
{
|
||||||
|
// move the last item to local variable to be processed
|
||||||
|
basic_json current_item(std::move(stack.back()));
|
||||||
|
stack.pop_back();
|
||||||
|
|
||||||
|
// if current_item is array/object, move
|
||||||
|
// its children to the stack to be processed later
|
||||||
|
if (current_item.is_array())
|
||||||
|
{
|
||||||
|
std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));
|
||||||
|
|
||||||
|
current_item.m_data.m_value.array->clear();
|
||||||
|
}
|
||||||
|
else if (current_item.is_object())
|
||||||
|
{
|
||||||
|
for (auto&& it : *current_item.m_data.m_value.object)
|
||||||
|
{
|
||||||
|
stack.push_back(std::move(it.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_item.m_data.m_value.object->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's now safe that current_item get destructed
|
||||||
|
// since it doesn't have any children
|
||||||
|
}
|
||||||
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
|
||||||
}
|
}
|
||||||
|
catch (...) // NOLINT(bugprone-empty-catch)
|
||||||
|
{
|
||||||
|
// Recursion avoidance has issue allocating temporary space. This may have been `std::bad_alloc`
|
||||||
|
// or any other exception thrown by a custom allocator.
|
||||||
|
// RAII will correctly clean up anything moved into `stack`.
|
||||||
|
// Then we continue with regular recursion based destroy, which will not heap allocate.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (t)
|
switch (t)
|
||||||
|
|||||||
@ -261,3 +261,58 @@ TEST_CASE("bad my_allocator::construct")
|
|||||||
j["test"].push_back("should not leak");
|
j["test"].push_back("should not leak");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
thread_local bool should_throw = false;
|
||||||
|
|
||||||
|
struct QuotaReached : std::exception {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct allocator_controlled_throw : std::allocator<T>
|
||||||
|
{
|
||||||
|
allocator_controlled_throw() = default;
|
||||||
|
template <class U>
|
||||||
|
allocator_controlled_throw(allocator_controlled_throw<U> /*unused*/) {}
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
struct rebind
|
||||||
|
{
|
||||||
|
using other = allocator_controlled_throw<U>;
|
||||||
|
};
|
||||||
|
|
||||||
|
T* allocate(size_t n)
|
||||||
|
{
|
||||||
|
if (should_throw)
|
||||||
|
{
|
||||||
|
throw QuotaReached{};
|
||||||
|
}
|
||||||
|
return std::allocator<T>::allocate(n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("controlled my_allocator::allocate")
|
||||||
|
{
|
||||||
|
SECTION("~basic_json tolerant of internal exceptions")
|
||||||
|
{
|
||||||
|
using my_alloc_json = nlohmann::basic_json<std::map,
|
||||||
|
std::vector,
|
||||||
|
std::string,
|
||||||
|
bool,
|
||||||
|
std::int64_t,
|
||||||
|
std::uint64_t,
|
||||||
|
double,
|
||||||
|
allocator_controlled_throw>;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto j = my_alloc_json{1, 2, 3, 4};
|
||||||
|
should_throw = true;
|
||||||
|
// `j` is destroyed, ~basic_json is noexcept
|
||||||
|
// if allocation attempted, exception thrown
|
||||||
|
// exception should be internally handled
|
||||||
|
} // should not std::terminate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user