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