mirror of
https://github.com/nlohmann/json.git
synced 2025-01-19 07:43:03 +08:00
Merge pull request #2872 from nlohmann/issue2865
Fix memory leak in to_json
This commit is contained in:
commit
f883ddd1c2
@ -22,6 +22,13 @@ namespace detail
|
|||||||
// constructors //
|
// constructors //
|
||||||
//////////////////
|
//////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note all external_constructor<>::construct functions need to call
|
||||||
|
* j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
|
||||||
|
* allocated value (e.g., a string). See bug issue
|
||||||
|
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@ -30,6 +37,7 @@ struct external_constructor<value_t::boolean>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::boolean;
|
j.m_type = value_t::boolean;
|
||||||
j.m_value = b;
|
j.m_value = b;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -42,6 +50,7 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::string;
|
j.m_type = value_t::string;
|
||||||
j.m_value = s;
|
j.m_value = s;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -50,6 +59,7 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::string;
|
j.m_type = value_t::string;
|
||||||
j.m_value = std::move(s);
|
j.m_value = std::move(s);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -60,6 +70,7 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::string;
|
j.m_type = value_t::string;
|
||||||
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -72,6 +83,7 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::binary;
|
j.m_type = value_t::binary;
|
||||||
j.m_value = typename BasicJsonType::binary_t(b);
|
j.m_value = typename BasicJsonType::binary_t(b);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -80,6 +92,7 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::binary;
|
j.m_type = value_t::binary;
|
||||||
j.m_value = typename BasicJsonType::binary_t(std::move(b));;
|
j.m_value = typename BasicJsonType::binary_t(std::move(b));;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -92,6 +105,7 @@ struct external_constructor<value_t::number_float>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::number_float;
|
j.m_type = value_t::number_float;
|
||||||
j.m_value = val;
|
j.m_value = val;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -104,6 +118,7 @@ struct external_constructor<value_t::number_unsigned>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::number_unsigned;
|
j.m_type = value_t::number_unsigned;
|
||||||
j.m_value = val;
|
j.m_value = val;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -116,6 +131,7 @@ struct external_constructor<value_t::number_integer>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::number_integer;
|
j.m_type = value_t::number_integer;
|
||||||
j.m_value = val;
|
j.m_value = val;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -128,6 +144,7 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = arr;
|
j.m_value = arr;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -137,6 +154,7 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = std::move(arr);
|
j.m_value = std::move(arr);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -150,6 +168,8 @@ struct external_constructor<value_t::array>
|
|||||||
{
|
{
|
||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -159,6 +179,7 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = value_t::array;
|
j.m_value = value_t::array;
|
||||||
j.m_value.array->reserve(arr.size());
|
j.m_value.array->reserve(arr.size());
|
||||||
@ -174,6 +195,7 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = value_t::array;
|
j.m_value = value_t::array;
|
||||||
j.m_value.array->resize(arr.size());
|
j.m_value.array->resize(arr.size());
|
||||||
@ -192,6 +214,7 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::object;
|
j.m_type = value_t::object;
|
||||||
j.m_value = obj;
|
j.m_value = obj;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -201,6 +224,7 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::object;
|
j.m_type = value_t::object;
|
||||||
j.m_value = std::move(obj);
|
j.m_value = std::move(obj);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -214,6 +238,7 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::object;
|
j.m_type = value_t::object;
|
||||||
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
|
@ -1131,53 +1131,55 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
binary = create<binary_t>(std::move(value));
|
binary = create<binary_t>(std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy(value_t t) noexcept
|
void destroy(value_t t)
|
||||||
{
|
{
|
||||||
// flatten the current json_value to a heap-allocated stack
|
if (t == value_t::array || t == value_t::object)
|
||||||
std::vector<basic_json> stack;
|
{
|
||||||
|
// flatten the current json_value to a heap-allocated stack
|
||||||
|
std::vector<basic_json> 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 if (t == value_t::object)
|
|
||||||
{
|
|
||||||
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_value.array->begin(), current_item.m_value.array->end(),
|
stack.reserve(object->size());
|
||||||
std::back_inserter(stack));
|
for (auto&& it : *object)
|
||||||
|
|
||||||
current_item.m_value.array->clear();
|
|
||||||
}
|
|
||||||
else if (current_item.is_object())
|
|
||||||
{
|
|
||||||
for (auto&& it : *current_item.m_value.object)
|
|
||||||
{
|
{
|
||||||
stack.push_back(std::move(it.second));
|
stack.push_back(std::move(it.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
current_item.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_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));
|
||||||
|
|
||||||
|
current_item.m_value.array->clear();
|
||||||
|
}
|
||||||
|
else if (current_item.is_object())
|
||||||
|
{
|
||||||
|
for (auto&& it : *current_item.m_value.object)
|
||||||
|
{
|
||||||
|
stack.push_back(std::move(it.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_item.m_value.object->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's now safe that current_item get destructed
|
||||||
|
// since it doesn't have any children
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (t)
|
switch (t)
|
||||||
|
@ -4480,6 +4480,13 @@ namespace detail
|
|||||||
// constructors //
|
// constructors //
|
||||||
//////////////////
|
//////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note all external_constructor<>::construct functions need to call
|
||||||
|
* j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
|
||||||
|
* allocated value (e.g., a string). See bug issue
|
||||||
|
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@ -4488,6 +4495,7 @@ struct external_constructor<value_t::boolean>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::boolean;
|
j.m_type = value_t::boolean;
|
||||||
j.m_value = b;
|
j.m_value = b;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4500,6 +4508,7 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::string;
|
j.m_type = value_t::string;
|
||||||
j.m_value = s;
|
j.m_value = s;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4508,6 +4517,7 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::string;
|
j.m_type = value_t::string;
|
||||||
j.m_value = std::move(s);
|
j.m_value = std::move(s);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4518,6 +4528,7 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::string;
|
j.m_type = value_t::string;
|
||||||
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4530,6 +4541,7 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::binary;
|
j.m_type = value_t::binary;
|
||||||
j.m_value = typename BasicJsonType::binary_t(b);
|
j.m_value = typename BasicJsonType::binary_t(b);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4538,6 +4550,7 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::binary;
|
j.m_type = value_t::binary;
|
||||||
j.m_value = typename BasicJsonType::binary_t(std::move(b));;
|
j.m_value = typename BasicJsonType::binary_t(std::move(b));;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4550,6 +4563,7 @@ struct external_constructor<value_t::number_float>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::number_float;
|
j.m_type = value_t::number_float;
|
||||||
j.m_value = val;
|
j.m_value = val;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4562,6 +4576,7 @@ struct external_constructor<value_t::number_unsigned>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::number_unsigned;
|
j.m_type = value_t::number_unsigned;
|
||||||
j.m_value = val;
|
j.m_value = val;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4574,6 +4589,7 @@ struct external_constructor<value_t::number_integer>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::number_integer;
|
j.m_type = value_t::number_integer;
|
||||||
j.m_value = val;
|
j.m_value = val;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
@ -4586,6 +4602,7 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = arr;
|
j.m_value = arr;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -4595,6 +4612,7 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = std::move(arr);
|
j.m_value = std::move(arr);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -4608,6 +4626,8 @@ struct external_constructor<value_t::array>
|
|||||||
{
|
{
|
||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -4617,6 +4637,7 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = value_t::array;
|
j.m_value = value_t::array;
|
||||||
j.m_value.array->reserve(arr.size());
|
j.m_value.array->reserve(arr.size());
|
||||||
@ -4632,6 +4653,7 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::array;
|
j.m_type = value_t::array;
|
||||||
j.m_value = value_t::array;
|
j.m_value = value_t::array;
|
||||||
j.m_value.array->resize(arr.size());
|
j.m_value.array->resize(arr.size());
|
||||||
@ -4650,6 +4672,7 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::object;
|
j.m_type = value_t::object;
|
||||||
j.m_value = obj;
|
j.m_value = obj;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -4659,6 +4682,7 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::object;
|
j.m_type = value_t::object;
|
||||||
j.m_value = std::move(obj);
|
j.m_value = std::move(obj);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -4672,6 +4696,7 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
j.m_value.destroy(j.m_type);
|
||||||
j.m_type = value_t::object;
|
j.m_type = value_t::object;
|
||||||
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
@ -18166,53 +18191,55 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
binary = create<binary_t>(std::move(value));
|
binary = create<binary_t>(std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy(value_t t) noexcept
|
void destroy(value_t t)
|
||||||
{
|
{
|
||||||
// flatten the current json_value to a heap-allocated stack
|
if (t == value_t::array || t == value_t::object)
|
||||||
std::vector<basic_json> stack;
|
{
|
||||||
|
// flatten the current json_value to a heap-allocated stack
|
||||||
|
std::vector<basic_json> 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 if (t == value_t::object)
|
|
||||||
{
|
|
||||||
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_value.array->begin(), current_item.m_value.array->end(),
|
stack.reserve(object->size());
|
||||||
std::back_inserter(stack));
|
for (auto&& it : *object)
|
||||||
|
|
||||||
current_item.m_value.array->clear();
|
|
||||||
}
|
|
||||||
else if (current_item.is_object())
|
|
||||||
{
|
|
||||||
for (auto&& it : *current_item.m_value.object)
|
|
||||||
{
|
{
|
||||||
stack.push_back(std::move(it.second));
|
stack.push_back(std::move(it.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
current_item.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_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));
|
||||||
|
|
||||||
|
current_item.m_value.array->clear();
|
||||||
|
}
|
||||||
|
else if (current_item.is_object())
|
||||||
|
{
|
||||||
|
for (auto&& it : *current_item.m_value.object)
|
||||||
|
{
|
||||||
|
stack.push_back(std::move(it.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_item.m_value.object->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's now safe that current_item get destructed
|
||||||
|
// since it doesn't have any children
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (t)
|
switch (t)
|
||||||
|
@ -594,4 +594,30 @@ TEST_CASE("regression tests 2")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("issue #2865 - ASAN detects memory leaks")
|
||||||
|
{
|
||||||
|
// the code below is expected to not leak memory
|
||||||
|
{
|
||||||
|
nlohmann::json o;
|
||||||
|
std::string s = "bar";
|
||||||
|
|
||||||
|
nlohmann::to_json(o["foo"], s);
|
||||||
|
|
||||||
|
nlohmann::json p = o;
|
||||||
|
|
||||||
|
// call to_json with a non-null JSON value
|
||||||
|
nlohmann::to_json(p["foo"], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
nlohmann::json o;
|
||||||
|
std::string s = "bar";
|
||||||
|
|
||||||
|
nlohmann::to_json(o["foo"], s);
|
||||||
|
|
||||||
|
// call to_json with a non-null JSON value
|
||||||
|
nlohmann::to_json(o["foo"], s);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user