#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
#define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
#include <stdint.h>
#include <array>
#include <concepts>
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "base/base_export.h"
#include "base/containers/circular_deque.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/heap_array.h"
#include "base/containers/linked_list.h"
#include "base/containers/lru_cache.h"
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/stl_util.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
namespace base {
namespace trace_event {
template <class T>
auto EstimateMemoryUsage(const T& object)
-> decltype(object.EstimateMemoryUsage());
template <class C, class T, class A>
size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
template <class T, size_t N>
size_t EstimateMemoryUsage(const std::array<T, N>& array);
template <class T, size_t N>
size_t EstimateMemoryUsage(T (&array)[N]);
template <class T>
size_t EstimateMemoryUsage(const base::HeapArray<T>& array);
template <class T>
size_t EstimateMemoryUsage(base::span<T> array);
template <class T, class D>
size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
template <class T>
size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr);
template <class F, class S>
size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
template <class T, class A>
size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
template <class T, class A>
size_t EstimateMemoryUsage(const std::list<T, A>& list);
template <class T>
size_t EstimateMemoryUsage(const base::LinkedList<T>& list);
template <class T, class C, class A>
size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
template <class T, class C, class A>
size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
template <class K, class V, class C, class A>
size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
template <class K, class V, class C, class A>
size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
template <class T, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
template <class T, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
template <class K, class V, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
template <class K, class V, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
template <class T, class A>
size_t EstimateMemoryUsage(const std::deque<T, A>& deque);
template <class T, class C>
size_t EstimateMemoryUsage(const std::queue<T, C>& queue);
template <class T, class C>
size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue);
template <class T, class C>
size_t EstimateMemoryUsage(const std::stack<T, C>& stack);
template <class T>
size_t EstimateMemoryUsage(const base::circular_deque<T>& deque);
template <class T, class C>
size_t EstimateMemoryUsage(const base::flat_set<T, C>& set);
template <class K, class V, class C>
size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map);
template <class K, class V, class C>
size_t EstimateMemoryUsage(const base::LRUCache<K, V, C>& lru);
template <class K, class V, class C>
size_t EstimateMemoryUsage(const base::HashingLRUCache<K, V, C>& lru);
template <class V, class C>
size_t EstimateMemoryUsage(const base::LRUCacheSet<V, C>& lru);
template <class V, class C>
size_t EstimateMemoryUsage(const base::HashingLRUCacheSet<V, C>& lru);
template <class K, class V, class H, class E, class A>
size_t EstimateMemoryUsage(const absl::flat_hash_map<K, V, H, E, A>& map);
namespace internal {
template <typename T>
concept HasEMU = requires(const T& t) {
{ EstimateMemoryUsage(t) } -> std::same_as<size_t>;
};
template <typename I>
using IteratorValueType = typename std::iterator_traits<I>::value_type;
template <typename I, typename InstantiatedContainer>
concept IsIteratorOfInstantiatedContainer =
(std::same_as<typename InstantiatedContainer::iterator, I> ||
std::same_as<typename InstantiatedContainer::const_iterator, I> ||
std::same_as<typename InstantiatedContainer::reverse_iterator, I> ||
std::same_as<typename InstantiatedContainer::const_reverse_iterator, I>);
template <typename I, template <typename...> typename Container>
concept IsIteratorOfContainer =
!std::is_pointer_v<I> &&
IsIteratorOfInstantiatedContainer<I, Container<IteratorValueType<I>>>;
template <typename T>
using array_test_helper = std::array<T, 1>;
template <typename T>
concept IsIteratorOfStandardContainer =
IsIteratorOfContainer<T, array_test_helper> ||
IsIteratorOfContainer<T, std::vector> ||
IsIteratorOfContainer<T, std::deque> ||
IsIteratorOfContainer<T, std::list> || IsIteratorOfContainer<T, std::set> ||
IsIteratorOfContainer<T, std::multiset>;
template <typename T>
concept IsKnownNonAllocatingType =
std::is_trivially_destructible_v<T> || base::IsRawPtr<T> ||
IsIteratorOfStandardContainer<T>;
}
template <class T>
size_t EstimateItemMemoryUsage(const T& value) {
if constexpr (internal::HasEMU<T>) {
return EstimateMemoryUsage(value);
} else if constexpr (!internal::IsKnownNonAllocatingType<T>) {
static_assert(false,
"Neither global function 'size_t EstimateMemoryUsage(T)' "
"nor member function 'size_t T::EstimateMemoryUsage() const' "
"is defined for the type.");
}
return 0;
}
template <class I>
size_t EstimateIterableMemoryUsage(const I& iterable) {
size_t memory_usage = 0;
for (const auto& item : iterable) {
memory_usage += EstimateItemMemoryUsage(item);
}
return memory_usage;
}
template <class T>
auto EstimateMemoryUsage(const T& object)
-> decltype(object.EstimateMemoryUsage()) {
static_assert(std::same_as<decltype(object.EstimateMemoryUsage()), size_t>,
"'T::EstimateMemoryUsage() const' must return size_t.");
return object.EstimateMemoryUsage();
}
template <class C, class T, class A>
size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
using string_type = std::basic_string<C, T, A>;
using value_type = typename string_type::value_type;
const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
return 0;
}
return (string.capacity() + 1) * sizeof(value_type);
}
extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::u16string&);
template <class T, size_t N>
size_t EstimateMemoryUsage(const std::array<T, N>& array) {
return EstimateIterableMemoryUsage(array);
}
template <class T, size_t N>
size_t EstimateMemoryUsage(T (&array)[N]) {
return EstimateIterableMemoryUsage(array);
}
template <class T>
size_t EstimateMemoryUsage(const base::HeapArray<T>& array) {
return sizeof(T) * array.size() + EstimateIterableMemoryUsage(array);
}
template <class T>
size_t EstimateMemoryUsage(base::span<T> array) {
return sizeof(T) * array.size() + EstimateIterableMemoryUsage(array);
}
template <class T, class D>
size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
}
template <class T>
size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) {
auto use_count = ptr.use_count();
if (use_count == 0) {
return 0;
}
struct SharedPointer {
raw_ptr<void> vtbl;
long shared_owners;
long shared_weak_owners;
raw_ptr<T> value;
};
return sizeof(SharedPointer) +
(EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count;
}
template <class F, class S>
size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
return EstimateItemMemoryUsage(pair.first) +
EstimateItemMemoryUsage(pair.second);
}
template <class T, class A>
size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
}
template <class T, class A>
size_t EstimateMemoryUsage(const std::list<T, A>& list) {
using value_type = typename std::list<T, A>::value_type;
struct Node {
raw_ptr<Node> prev;
raw_ptr<Node> next;
value_type value;
};
return sizeof(Node) * list.size() + EstimateIterableMemoryUsage(list);
}
template <class T>
size_t EstimateMemoryUsage(const base::LinkedList<T>& list) {
size_t memory_usage = 0u;
for (base::LinkNode<T>* node = list.head(); node != list.end();
node = node->next()) {
memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T);
}
return memory_usage;
}
template <class V>
size_t EstimateTreeMemoryUsage(size_t size) {
struct Node {
raw_ptr<Node> left;
raw_ptr<Node> right;
raw_ptr<Node> parent;
bool is_black;
V value;
};
return sizeof(Node) * size;
}
template <class T, class C, class A>
size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
using value_type = typename std::set<T, C, A>::value_type;
return EstimateTreeMemoryUsage<value_type>(set.size()) +
EstimateIterableMemoryUsage(set);
}
template <class T, class C, class A>
size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
using value_type = typename std::multiset<T, C, A>::value_type;
return EstimateTreeMemoryUsage<value_type>(set.size()) +
EstimateIterableMemoryUsage(set);
}
template <class K, class V, class C, class A>
size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
using value_type = typename std::map<K, V, C, A>::value_type;
return EstimateTreeMemoryUsage<value_type>(map.size()) +
EstimateIterableMemoryUsage(map);
}
template <class K, class V, class C, class A>
size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
using value_type = typename std::multimap<K, V, C, A>::value_type;
return EstimateTreeMemoryUsage<value_type>(map.size()) +
EstimateIterableMemoryUsage(map);
}
namespace internal {
template <class V>
size_t HashMapBucketCountForTesting(size_t bucket_count) {
return bucket_count;
}
template <class LruCacheType>
size_t DoEstimateMemoryUsageForLruCache(const LruCacheType& lru_cache) {
return EstimateMemoryUsage(lru_cache.ordering_) +
EstimateMemoryUsage(lru_cache.index_);
}
}
template <class V>
size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
struct Node {
raw_ptr<void> next;
size_t hash;
V value;
};
using Bucket = void*;
bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
}
template <class K, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
set.size()) +
EstimateIterableMemoryUsage(set);
}
template <class K, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
set.size()) +
EstimateIterableMemoryUsage(set);
}
template <class K, class V, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
map.size()) +
EstimateIterableMemoryUsage(map);
}
template <class K, class V, class H, class KE, class A>
size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
using value_type =
typename std::unordered_multimap<K, V, H, KE, A>::value_type;
return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
map.size()) +
EstimateIterableMemoryUsage(map);
}
template <class T, class A>
size_t EstimateMemoryUsage(const std::deque<T, A>& deque) {
#if defined(_LIBCPP_VERSION)
size_t kBlockSize = 4096;
size_t kMinBlockLength = 16;
#elif defined(__GLIBCXX__)
size_t kBlockSize = 512;
size_t kMinBlockLength = 1;
#elif defined(_MSC_VER)
size_t kBlockSize = 16;
size_t kMinBlockLength = 1;
#else
size_t kBlockSize = 0;
size_t kMinBlockLength = 1;
#endif
size_t block_length =
(sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T);
size_t blocks = (deque.size() + block_length - 1) / block_length;
#if defined(__GLIBCXX__)
if (!blocks) {
blocks = 1;
}
#endif
#if defined(_LIBCPP_VERSION)
if (!blocks && deque.begin().operator->()) {
blocks = 1;
}
#endif
return (blocks * block_length * sizeof(T)) +
EstimateIterableMemoryUsage(deque);
}
template <class T, class C>
size_t EstimateMemoryUsage(const std::queue<T, C>& queue) {
return EstimateMemoryUsage(GetUnderlyingContainer(queue));
}
template <class T, class C>
size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) {
return EstimateMemoryUsage(GetUnderlyingContainer(queue));
}
template <class T, class C>
size_t EstimateMemoryUsage(const std::stack<T, C>& stack) {
return EstimateMemoryUsage(GetUnderlyingContainer(stack));
}
template <class T>
size_t EstimateMemoryUsage(const base::circular_deque<T>& deque) {
return sizeof(T) * deque.capacity() + EstimateIterableMemoryUsage(deque);
}
template <class T, class C>
size_t EstimateMemoryUsage(const base::flat_set<T, C>& set) {
using value_type = typename base::flat_set<T, C>::value_type;
return sizeof(value_type) * set.capacity() + EstimateIterableMemoryUsage(set);
}
template <class K, class V, class C>
size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map) {
using value_type = typename base::flat_map<K, V, C>::value_type;
return sizeof(value_type) * map.capacity() + EstimateIterableMemoryUsage(map);
}
template <class K, class V, class C>
size_t EstimateMemoryUsage(const LRUCache<K, V, C>& lru_cache) {
return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
}
template <class K, class V, class C>
size_t EstimateMemoryUsage(const HashingLRUCache<K, V, C>& lru_cache) {
return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
}
template <class V, class C>
size_t EstimateMemoryUsage(const LRUCacheSet<V, C>& lru_cache) {
return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
}
template <class V, class C>
size_t EstimateMemoryUsage(const HashingLRUCacheSet<V, C>& lru_cache) {
return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
}
template <class K, class V, class H, class E, class A>
size_t EstimateMemoryUsage(const absl::flat_hash_map<K, V, H, E, A>& map) {
using value_type = typename absl::flat_hash_map<K, V, H, E, A>::value_type;
return map.capacity() * (sizeof(value_type) + sizeof(char)) +
EstimateIterableMemoryUsage(map);
}
}
}
#endif