Skip to content

Commit 990b0a2

Browse files
authored
Merge pull request #5 from vyavdoshenko/Dragonfly.178_compute_memory_size
fix: compute_memory_size() added
2 parents 95ce941 + e59fe1d commit 990b0a2

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

include/jsoncons/basic_json.hpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <utility> // std::move
2222
#include <type_traits> // std::enable_if
2323
#include <istream> // std::basic_istream
24+
#include <functional> // std::function
2425
#include <jsoncons/json_fwd.hpp>
2526
#include <jsoncons/json_type.hpp>
2627
#include <jsoncons/config/version.hpp>
@@ -2111,6 +2112,144 @@ namespace jsoncons {
21112112
}
21122113
}
21132114

2115+
// Callback type for getting usable size of allocated memory
2116+
using memory_size_callback = std::function<std::size_t(const void*)>;
2117+
2118+
// Computes the actual memory size used by this JSON value
2119+
// including all dynamically allocated memory.
2120+
// Uses iterative traversal (not recursion) to avoid stack overflow.
2121+
//
2122+
// Example usage with mimalloc:
2123+
// auto cb = [](const void* ptr) { return ptr ? mi_usable_size(const_cast<void*>(ptr)) : 0; };
2124+
// size_t size = json_obj.compute_memory_size(cb);
2125+
std::size_t compute_memory_size(const memory_size_callback& get_usable_size) const
2126+
{
2127+
std::size_t mem_size = 0;
2128+
2129+
// Use explicit stack for iterative traversal (avoids recursion/stack overflow)
2130+
std::vector<const basic_json*> stack;
2131+
stack.reserve(8); // Reserve some space to reduce allocations
2132+
stack.push_back(this);
2133+
2134+
while (!stack.empty())
2135+
{
2136+
const basic_json* current = stack.back();
2137+
stack.pop_back();
2138+
2139+
switch (current->storage_kind())
2140+
{
2141+
case json_storage_kind::null:
2142+
case json_storage_kind::empty_object:
2143+
case json_storage_kind::boolean:
2144+
case json_storage_kind::int64:
2145+
case json_storage_kind::uint64:
2146+
case json_storage_kind::half_float:
2147+
case json_storage_kind::float64:
2148+
case json_storage_kind::short_str:
2149+
// These are stored inline, no dynamic allocation
2150+
break;
2151+
2152+
case json_storage_kind::long_str:
2153+
{
2154+
// Get the string data pointer and compute its allocated size
2155+
const auto& storage = current->template cast<long_string_storage>();
2156+
const char_type* str_ptr = storage.data();
2157+
2158+
// Use callback to get actual allocated size
2159+
mem_size += get_usable_size(static_cast<const void*>(str_ptr));
2160+
break;
2161+
}
2162+
2163+
case json_storage_kind::byte_str:
2164+
{
2165+
// Similar to long_str
2166+
const auto& storage = current->template cast<byte_string_storage>();
2167+
const uint8_t* data_ptr = storage.data();
2168+
2169+
// Use callback to get actual allocated size
2170+
mem_size += get_usable_size(static_cast<const void*>(data_ptr));
2171+
break;
2172+
}
2173+
2174+
case json_storage_kind::array:
2175+
{
2176+
// Get array internal storage
2177+
const array& arr = current->template cast<array_storage>().value();
2178+
2179+
// Memory for the array's internal buffer
2180+
// Note: We only count dynamically allocated memory (heap).
2181+
// The array object itself is part of array_storage allocation.
2182+
if (!arr.empty())
2183+
{
2184+
// Get pointer to internal vector buffer
2185+
const basic_json* data_ptr = &arr[0];
2186+
// Use callback for precise allocated size
2187+
mem_size += get_usable_size(static_cast<const void*>(data_ptr));
2188+
2189+
// Add array elements to stack for processing
2190+
// Optimization: only add elements that need traversal (arrays/objects)
2191+
for (const auto& elem : arr)
2192+
{
2193+
// capacity() > 0 only for arrays/objects
2194+
if (elem.capacity() > 0)
2195+
{
2196+
stack.push_back(&elem);
2197+
}
2198+
}
2199+
}
2200+
break;
2201+
}
2202+
2203+
case json_storage_kind::object:
2204+
{
2205+
// Get object internal storage
2206+
const object& obj = current->template cast<object_storage>().value();
2207+
2208+
// Memory for the object's internal storage (vector of key_value_type)
2209+
// Note: We only count dynamically allocated memory (heap).
2210+
// The object itself is part of object_storage allocation.
2211+
if (!obj.empty())
2212+
{
2213+
// Get pointer to internal vector buffer via iterator
2214+
const key_value_type* data_ptr = &(*obj.begin());
2215+
// Use callback for precise allocated size
2216+
mem_size += get_usable_size(static_cast<const void*>(data_ptr));
2217+
}
2218+
2219+
// Process keys and values
2220+
for (const auto& member : obj)
2221+
{
2222+
// Key size: check if key has heap allocation
2223+
const auto& key_str = member.key();
2224+
const char_type* key_data = key_str.data();
2225+
std::size_t key_heap_size = get_usable_size(static_cast<const void*>(key_data));
2226+
mem_size += key_heap_size;
2227+
2228+
// Add value to stack for processing
2229+
// Optimization: only add values that need traversal (arrays/objects)
2230+
const auto& value = member.value();
2231+
// capacity() > 0 only for arrays/objects
2232+
if (value.capacity() > 0)
2233+
{
2234+
stack.push_back(&value);
2235+
}
2236+
}
2237+
break;
2238+
}
2239+
2240+
case json_storage_kind::const_json_pointer:
2241+
// This is just a pointer to another JSON value, no ownership
2242+
break;
2243+
2244+
default:
2245+
// Unknown storage type
2246+
break;
2247+
}
2248+
}
2249+
2250+
return mem_size;
2251+
}
2252+
21142253
string_view_type as_string_view() const
21152254
{
21162255
switch (storage_kind())

0 commit comments

Comments
 (0)