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