1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
11 #define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
12
13 #include <stdint.h>
14
15 #include <array>
16 #include <concepts>
17 #include <deque>
18 #include <list>
19 #include <map>
20 #include <memory>
21 #include <queue>
22 #include <set>
23 #include <stack>
24 #include <string>
25 #include <type_traits>
26 #include <unordered_map>
27 #include <unordered_set>
28 #include <vector>
29
30 #include "base/base_export.h"
31 #include "base/containers/circular_deque.h"
32 #include "base/containers/flat_map.h"
33 #include "base/containers/flat_set.h"
34 #include "base/containers/heap_array.h"
35 #include "base/containers/linked_list.h"
36 #include "base/containers/lru_cache.h"
37 #include "base/containers/queue.h"
38 #include "base/containers/span.h"
39 #include "base/memory/raw_ptr.h"
40 #include "base/stl_util.h"
41 #include "base/types/always_false.h"
42
43 // Composable memory usage estimators.
44 //
45 // This file defines set of EstimateMemoryUsage(object) functions that return
46 // approximate dynamically allocated memory usage of their argument.
47 //
48 // The ultimate goal is to make memory usage estimation for a class simply a
49 // matter of aggregating EstimateMemoryUsage() results over all fields.
50 //
51 // That is achieved via composability: if EstimateMemoryUsage() is defined
52 // for T then EstimateMemoryUsage() is also defined for any combination of
53 // containers holding T (e.g. std::map<int, std::vector<T>>).
54 //
55 // There are two ways of defining EstimateMemoryUsage() for a type:
56 //
57 // 1. As a global function 'size_t EstimateMemoryUsage(T)' in
58 // in base::trace_event namespace.
59 //
60 // 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
61 // EstimateMemoryUsage(T) function in base::trace_event namespace is
62 // provided automatically.
63 //
64 // Here is an example implementation:
65 //
66 // class MyClass {
67 // ...
68 // ...
69 // size_t EstimateMemoryUsage() const {
70 // return base::trace_event::EstimateMemoryUsage(set_) +
71 // base::trace_event::EstimateMemoryUsage(name_) +
72 // base::trace_event::EstimateMemoryUsage(foo_);
73 // }
74 // ...
75 // private:
76 // ...
77 // std::set<int> set_;
78 // std::string name_;
79 // Foo foo_;
80 // int id_;
81 // bool success_;
82 // }
83 //
84 // The approach is simple: first call EstimateMemoryUsage() on all members,
85 // then recursively fix compilation errors that are caused by types not
86 // implementing EstimateMemoryUsage().
87 //
88 // Note that in the above example, the memory estimates for `id_` and `success_` are
89 // intentionally omitted. This is because these members do not allocate any _dynamic_ memory.
90 // If, for example, `MyClass` is declared as a heap-allocated `unique_ptr` member in some parent
91 // class, then `EstimateMemoryUsage` on the `unique_ptr` will automatically take into account
92 // `sizeof(MyClass)`.
93
94 namespace base {
95 namespace trace_event {
96
97 // Declarations
98
99 // If T declares 'EstimateMemoryUsage() const' member function, then
100 // global function EstimateMemoryUsage(T) is available, and just calls
101 // the member function.
102 template <class T>
103 auto EstimateMemoryUsage(const T& object)
104 -> decltype(object.EstimateMemoryUsage());
105
106 // String
107
108 template <class C, class T, class A>
109 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
110
111 // Arrays
112
113 template <class T, size_t N>
114 size_t EstimateMemoryUsage(const std::array<T, N>& array);
115
116 template <class T, size_t N>
117 size_t EstimateMemoryUsage(T (&array)[N]);
118
119 template <class T>
120 size_t EstimateMemoryUsage(const base::HeapArray<T>& array);
121
122 template <class T>
123 size_t EstimateMemoryUsage(base::span<T> array);
124
125 // std::unique_ptr
126
127 template <class T, class D>
128 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
129
130 // std::shared_ptr
131
132 template <class T>
133 size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr);
134
135 // Containers
136
137 template <class F, class S>
138 size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
139
140 template <class T, class A>
141 size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
142
143 template <class T, class A>
144 size_t EstimateMemoryUsage(const std::list<T, A>& list);
145
146 template <class T>
147 size_t EstimateMemoryUsage(const base::LinkedList<T>& list);
148
149 template <class T, class C, class A>
150 size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
151
152 template <class T, class C, class A>
153 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
154
155 template <class K, class V, class C, class A>
156 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
157
158 template <class K, class V, class C, class A>
159 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
160
161 template <class T, class H, class KE, class A>
162 size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
163
164 template <class T, class H, class KE, class A>
165 size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
166
167 template <class K, class V, class H, class KE, class A>
168 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
169
170 template <class K, class V, class H, class KE, class A>
171 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
172
173 template <class T, class A>
174 size_t EstimateMemoryUsage(const std::deque<T, A>& deque);
175
176 template <class T, class C>
177 size_t EstimateMemoryUsage(const std::queue<T, C>& queue);
178
179 template <class T, class C>
180 size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue);
181
182 template <class T, class C>
183 size_t EstimateMemoryUsage(const std::stack<T, C>& stack);
184
185 template <class T>
186 size_t EstimateMemoryUsage(const base::circular_deque<T>& deque);
187
188 template <class T, class C>
189 size_t EstimateMemoryUsage(const base::flat_set<T, C>& set);
190
191 template <class K, class V, class C>
192 size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map);
193
194 template <class K, class V, class C>
195 size_t EstimateMemoryUsage(const base::LRUCache<K, V, C>& lru);
196
197 template <class K, class V, class C>
198 size_t EstimateMemoryUsage(const base::HashingLRUCache<K, V, C>& lru);
199
200 template <class V, class C>
201 size_t EstimateMemoryUsage(const base::LRUCacheSet<V, C>& lru);
202
203 template <class V, class C>
204 size_t EstimateMemoryUsage(const base::HashingLRUCacheSet<V, C>& lru);
205
206 // TODO(dskiba):
207 // std::forward_list
208
209 // Definitions
210
211 namespace internal {
212
213 // HasEMU<T> is true iff EstimateMemoryUsage(const T&) is available.
214 template <typename T>
requires(const T & t)215 concept HasEMU = requires(const T& t) {
216 { EstimateMemoryUsage(t) } -> std::same_as<size_t>;
217 };
218
219 template <typename I>
220 using IteratorValueType = typename std::iterator_traits<I>::value_type;
221
222 template <typename I, typename InstantiatedContainer>
223 concept IsIteratorOfInstantiatedContainer =
224 (std::same_as<typename InstantiatedContainer::iterator, I> ||
225 std::same_as<typename InstantiatedContainer::const_iterator, I> ||
226 std::same_as<typename InstantiatedContainer::reverse_iterator, I> ||
227 std::same_as<typename InstantiatedContainer::const_reverse_iterator, I>);
228
229 template <typename I, template <typename...> typename Container>
230 concept IsIteratorOfContainer =
231 !std::is_pointer_v<I> &&
232 IsIteratorOfInstantiatedContainer<I, Container<IteratorValueType<I>>>;
233
234 // std::array has an extra required template argument.
235 template <typename T>
236 using array_test_helper = std::array<T, 1>;
237
238 // TODO(dyaroshev): deal with maps iterators if there is a need.
239 // It requires to parse pairs into keys and values.
240 // TODO(dyaroshev): deal with unordered containers: they do not have reverse
241 // iterators.
242 template <typename T>
243 concept IsIteratorOfStandardContainer =
244 IsIteratorOfContainer<T, array_test_helper> ||
245 IsIteratorOfContainer<T, std::vector> ||
246 IsIteratorOfContainer<T, std::deque> ||
247 IsIteratorOfContainer<T, std::list> || IsIteratorOfContainer<T, std::set> ||
248 IsIteratorOfContainer<T, std::multiset>;
249
250 template <typename T>
251 concept IsKnownNonAllocatingType =
252 std::is_trivially_destructible_v<T> || base::IsRawPtrV<T> ||
253 IsIteratorOfStandardContainer<T>;
254
255 } // namespace internal
256
257 // Estimates T's memory usage as follows:
258 // 1. Calls `EstimateMemoryUsage(T)` if it is available.
259 // 2. If `EstimateMemoryUsage(T)` is not available, but T has trivial dtor
260 // (i.e. it's POD, integer, pointer, enum, etc.) then it returns 0. This is
261 // useful for containers, which allocate memory regardless of T (also for
262 // cases like std::map<int, MyClass>).
263 // 3. Otherwise, it triggers a `static_assert` with a helpful message.
264 //
265 // To be used by `EstimateMemoryUsage()` implementations for containers.
266 template <class T>
EstimateItemMemoryUsage(const T & value)267 size_t EstimateItemMemoryUsage(const T& value) {
268 if constexpr (internal::HasEMU<T>) {
269 return EstimateMemoryUsage(value);
270 } else if constexpr (!internal::IsKnownNonAllocatingType<T>) {
271 static_assert(base::AlwaysFalse<T>,
272 "Neither global function 'size_t EstimateMemoryUsage(T)' "
273 "nor member function 'size_t T::EstimateMemoryUsage() const' "
274 "is defined for the type.");
275 }
276 return 0;
277 }
278
279 template <class I>
EstimateIterableMemoryUsage(const I & iterable)280 size_t EstimateIterableMemoryUsage(const I& iterable) {
281 size_t memory_usage = 0;
282 for (const auto& item : iterable) {
283 memory_usage += EstimateItemMemoryUsage(item);
284 }
285 return memory_usage;
286 }
287
288 // Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
289 template <class T>
290 auto EstimateMemoryUsage(const T& object)
291 -> decltype(object.EstimateMemoryUsage()) {
292 static_assert(std::same_as<decltype(object.EstimateMemoryUsage()), size_t>,
293 "'T::EstimateMemoryUsage() const' must return size_t.");
294 return object.EstimateMemoryUsage();
295 }
296
297 // String
298
299 template <class C, class T, class A>
EstimateMemoryUsage(const std::basic_string<C,T,A> & string)300 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
301 using string_type = std::basic_string<C, T, A>;
302 using value_type = typename string_type::value_type;
303 // C++11 doesn't leave much room for implementors - std::string can
304 // use short string optimization, but that's about it. We detect SSO
305 // by checking that c_str() points inside |string|.
306 const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
307 const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
308 if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
309 // SSO string
310 return 0;
311 }
312 return (string.capacity() + 1) * sizeof(value_type);
313 }
314
315 // Use explicit instantiations from the .cc file (reduces bloat).
316 extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
317 extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::u16string&);
318
319 // Arrays
320
321 template <class T, size_t N>
EstimateMemoryUsage(const std::array<T,N> & array)322 size_t EstimateMemoryUsage(const std::array<T, N>& array) {
323 return EstimateIterableMemoryUsage(array);
324 }
325
326 template <class T, size_t N>
EstimateMemoryUsage(T (& array)[N])327 size_t EstimateMemoryUsage(T (&array)[N]) {
328 return EstimateIterableMemoryUsage(array);
329 }
330
331 template <class T>
EstimateMemoryUsage(const base::HeapArray<T> & array)332 size_t EstimateMemoryUsage(const base::HeapArray<T>& array) {
333 return sizeof(T) * array.size() + EstimateIterableMemoryUsage(array);
334 }
335
336 template <class T>
EstimateMemoryUsage(base::span<T> array)337 size_t EstimateMemoryUsage(base::span<T> array) {
338 return sizeof(T) * array.size() + EstimateIterableMemoryUsage(array);
339 }
340
341 // std::unique_ptr
342
343 template <class T, class D>
EstimateMemoryUsage(const std::unique_ptr<T,D> & ptr)344 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
345 return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
346 }
347
348 // std::shared_ptr
349
350 template <class T>
EstimateMemoryUsage(const std::shared_ptr<T> & ptr)351 size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) {
352 auto use_count = ptr.use_count();
353 if (use_count == 0) {
354 return 0;
355 }
356 // Model shared_ptr after libc++,
357 // see __shared_ptr_pointer from include/memory
358 struct SharedPointer {
359 raw_ptr<void> vtbl;
360 long shared_owners;
361 long shared_weak_owners;
362 raw_ptr<T> value;
363 };
364 // If object of size S shared N > S times we prefer to (potentially)
365 // overestimate than to return 0.
366 return sizeof(SharedPointer) +
367 (EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count;
368 }
369
370 // std::pair
371
372 template <class F, class S>
EstimateMemoryUsage(const std::pair<F,S> & pair)373 size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
374 return EstimateItemMemoryUsage(pair.first) +
375 EstimateItemMemoryUsage(pair.second);
376 }
377
378 // std::vector
379
380 template <class T, class A>
EstimateMemoryUsage(const std::vector<T,A> & vector)381 size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
382 return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
383 }
384
385 // std::list
386
387 template <class T, class A>
EstimateMemoryUsage(const std::list<T,A> & list)388 size_t EstimateMemoryUsage(const std::list<T, A>& list) {
389 using value_type = typename std::list<T, A>::value_type;
390 struct Node {
391 raw_ptr<Node> prev;
392 raw_ptr<Node> next;
393 value_type value;
394 };
395 return sizeof(Node) * list.size() +
396 EstimateIterableMemoryUsage(list);
397 }
398
399 template <class T>
EstimateMemoryUsage(const base::LinkedList<T> & list)400 size_t EstimateMemoryUsage(const base::LinkedList<T>& list) {
401 size_t memory_usage = 0u;
402 for (base::LinkNode<T>* node = list.head(); node != list.end();
403 node = node->next()) {
404 // Since we increment by calling node = node->next() we know that node
405 // isn't nullptr.
406 memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T);
407 }
408 return memory_usage;
409 }
410
411 // Tree containers
412
413 template <class V>
EstimateTreeMemoryUsage(size_t size)414 size_t EstimateTreeMemoryUsage(size_t size) {
415 // Tree containers are modeled after libc++
416 // (__tree_node from include/__tree)
417 struct Node {
418 raw_ptr<Node> left;
419 raw_ptr<Node> right;
420 raw_ptr<Node> parent;
421 bool is_black;
422 V value;
423 };
424 return sizeof(Node) * size;
425 }
426
427 template <class T, class C, class A>
EstimateMemoryUsage(const std::set<T,C,A> & set)428 size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
429 using value_type = typename std::set<T, C, A>::value_type;
430 return EstimateTreeMemoryUsage<value_type>(set.size()) +
431 EstimateIterableMemoryUsage(set);
432 }
433
434 template <class T, class C, class A>
EstimateMemoryUsage(const std::multiset<T,C,A> & set)435 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
436 using value_type = typename std::multiset<T, C, A>::value_type;
437 return EstimateTreeMemoryUsage<value_type>(set.size()) +
438 EstimateIterableMemoryUsage(set);
439 }
440
441 template <class K, class V, class C, class A>
EstimateMemoryUsage(const std::map<K,V,C,A> & map)442 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
443 using value_type = typename std::map<K, V, C, A>::value_type;
444 return EstimateTreeMemoryUsage<value_type>(map.size()) +
445 EstimateIterableMemoryUsage(map);
446 }
447
448 template <class K, class V, class C, class A>
EstimateMemoryUsage(const std::multimap<K,V,C,A> & map)449 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
450 using value_type = typename std::multimap<K, V, C, A>::value_type;
451 return EstimateTreeMemoryUsage<value_type>(map.size()) +
452 EstimateIterableMemoryUsage(map);
453 }
454
455 // HashMap containers
456
457 namespace internal {
458
459 // While hashtable containers model doesn't depend on STL implementation, one
460 // detail still crept in: bucket_count. It's used in size estimation, but its
461 // value after inserting N items is not predictable.
462 // This function is specialized by unittests to return constant value, thus
463 // excluding bucket_count from testing.
464 template <class V>
HashMapBucketCountForTesting(size_t bucket_count)465 size_t HashMapBucketCountForTesting(size_t bucket_count) {
466 return bucket_count;
467 }
468
469 template <class LruCacheType>
DoEstimateMemoryUsageForLruCache(const LruCacheType & lru_cache)470 size_t DoEstimateMemoryUsageForLruCache(const LruCacheType& lru_cache) {
471 return EstimateMemoryUsage(lru_cache.ordering_) +
472 EstimateMemoryUsage(lru_cache.index_);
473 }
474
475 } // namespace internal
476
477 template <class V>
EstimateHashMapMemoryUsage(size_t bucket_count,size_t size)478 size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
479 // Hashtable containers are modeled after libc++
480 // (__hash_node from include/__hash_table)
481 struct Node {
482 raw_ptr<void> next;
483 size_t hash;
484 V value;
485 };
486 using Bucket = void*;
487 bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
488 return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
489 }
490
491 template <class K, class H, class KE, class A>
EstimateMemoryUsage(const std::unordered_set<K,H,KE,A> & set)492 size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
493 using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
494 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
495 set.size()) +
496 EstimateIterableMemoryUsage(set);
497 }
498
499 template <class K, class H, class KE, class A>
EstimateMemoryUsage(const std::unordered_multiset<K,H,KE,A> & set)500 size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
501 using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
502 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
503 set.size()) +
504 EstimateIterableMemoryUsage(set);
505 }
506
507 template <class K, class V, class H, class KE, class A>
EstimateMemoryUsage(const std::unordered_map<K,V,H,KE,A> & map)508 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
509 using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
510 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
511 map.size()) +
512 EstimateIterableMemoryUsage(map);
513 }
514
515 template <class K, class V, class H, class KE, class A>
EstimateMemoryUsage(const std::unordered_multimap<K,V,H,KE,A> & map)516 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
517 using value_type =
518 typename std::unordered_multimap<K, V, H, KE, A>::value_type;
519 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
520 map.size()) +
521 EstimateIterableMemoryUsage(map);
522 }
523
524 // std::deque
525
526 template <class T, class A>
EstimateMemoryUsage(const std::deque<T,A> & deque)527 size_t EstimateMemoryUsage(const std::deque<T, A>& deque) {
528 // Since std::deque implementations are wildly different
529 // (see crbug.com/674287), we can't have one "good enough"
530 // way to estimate.
531
532 // kBlockSize - minimum size of a block, in bytes
533 // kMinBlockLength - number of elements in a block
534 // if sizeof(T) > kBlockSize
535 #if defined(_LIBCPP_VERSION)
536 size_t kBlockSize = 4096;
537 size_t kMinBlockLength = 16;
538 #elif defined(__GLIBCXX__)
539 size_t kBlockSize = 512;
540 size_t kMinBlockLength = 1;
541 #elif defined(_MSC_VER)
542 size_t kBlockSize = 16;
543 size_t kMinBlockLength = 1;
544 #else
545 size_t kBlockSize = 0;
546 size_t kMinBlockLength = 1;
547 #endif
548
549 size_t block_length =
550 (sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T);
551
552 size_t blocks = (deque.size() + block_length - 1) / block_length;
553
554 #if defined(__GLIBCXX__)
555 // libstdc++: deque always has at least one block
556 if (!blocks)
557 blocks = 1;
558 #endif
559
560 #if defined(_LIBCPP_VERSION)
561 // libc++: deque keeps at most two blocks when it shrinks,
562 // so even if the size is zero, deque might be holding up
563 // to 4096 * 2 bytes. One way to know whether deque has
564 // ever allocated (and hence has 1 or 2 blocks) is to check
565 // iterator's pointer. Non-zero value means that deque has
566 // at least one block.
567 if (!blocks && deque.begin().operator->())
568 blocks = 1;
569 #endif
570
571 return (blocks * block_length * sizeof(T)) +
572 EstimateIterableMemoryUsage(deque);
573 }
574
575 // Container adapters
576
577 template <class T, class C>
EstimateMemoryUsage(const std::queue<T,C> & queue)578 size_t EstimateMemoryUsage(const std::queue<T, C>& queue) {
579 return EstimateMemoryUsage(GetUnderlyingContainer(queue));
580 }
581
582 template <class T, class C>
EstimateMemoryUsage(const std::priority_queue<T,C> & queue)583 size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) {
584 return EstimateMemoryUsage(GetUnderlyingContainer(queue));
585 }
586
587 template <class T, class C>
EstimateMemoryUsage(const std::stack<T,C> & stack)588 size_t EstimateMemoryUsage(const std::stack<T, C>& stack) {
589 return EstimateMemoryUsage(GetUnderlyingContainer(stack));
590 }
591
592 // base::circular_deque
593
594 template <class T>
EstimateMemoryUsage(const base::circular_deque<T> & deque)595 size_t EstimateMemoryUsage(const base::circular_deque<T>& deque) {
596 return sizeof(T) * deque.capacity() + EstimateIterableMemoryUsage(deque);
597 }
598
599 // Flat containers
600
601 template <class T, class C>
EstimateMemoryUsage(const base::flat_set<T,C> & set)602 size_t EstimateMemoryUsage(const base::flat_set<T, C>& set) {
603 using value_type = typename base::flat_set<T, C>::value_type;
604 return sizeof(value_type) * set.capacity() + EstimateIterableMemoryUsage(set);
605 }
606
607 template <class K, class V, class C>
EstimateMemoryUsage(const base::flat_map<K,V,C> & map)608 size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map) {
609 using value_type = typename base::flat_map<K, V, C>::value_type;
610 return sizeof(value_type) * map.capacity() + EstimateIterableMemoryUsage(map);
611 }
612
613 template <class K, class V, class C>
EstimateMemoryUsage(const LRUCache<K,V,C> & lru_cache)614 size_t EstimateMemoryUsage(const LRUCache<K, V, C>& lru_cache) {
615 return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
616 }
617
618 template <class K, class V, class C>
EstimateMemoryUsage(const HashingLRUCache<K,V,C> & lru_cache)619 size_t EstimateMemoryUsage(const HashingLRUCache<K, V, C>& lru_cache) {
620 return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
621 }
622
623 template <class V, class C>
EstimateMemoryUsage(const LRUCacheSet<V,C> & lru_cache)624 size_t EstimateMemoryUsage(const LRUCacheSet<V, C>& lru_cache) {
625 return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
626 }
627
628 template <class V, class C>
EstimateMemoryUsage(const HashingLRUCacheSet<V,C> & lru_cache)629 size_t EstimateMemoryUsage(const HashingLRUCacheSet<V, C>& lru_cache) {
630 return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
631 }
632
633 } // namespace trace_event
634 } // namespace base
635
636 #endif // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
637