|
21 | 21 | #include <utility> // std::move |
22 | 22 | #include <type_traits> // std::enable_if |
23 | 23 | #include <istream> // std::basic_istream |
| 24 | +#include <functional> // std::function |
24 | 25 | #include <jsoncons/json_fwd.hpp> |
25 | 26 | #include <jsoncons/json_type.hpp> |
26 | 27 | #include <jsoncons/config/version.hpp> |
@@ -2111,6 +2112,144 @@ namespace jsoncons { |
2111 | 2112 | } |
2112 | 2113 | } |
2113 | 2114 |
|
| 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 | + |
2114 | 2253 | string_view_type as_string_view() const |
2115 | 2254 | { |
2116 | 2255 | switch (storage_kind()) |
|
0 commit comments