mru_cache.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright (c) 2011 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. // This file contains a template for a Most Recently Used cache that allows
  5. // constant-time access to items using a key, but easy identification of the
  6. // least-recently-used items for removal. Each key can only be associated with
  7. // one payload item at a time.
  8. //
  9. // The key object will be stored twice, so it should support efficient copying.
  10. //
  11. // NOTE: While all operations are O(1), this code is written for
  12. // legibility rather than optimality. If future profiling identifies this as
  13. // a bottleneck, there is room for smaller values of 1 in the O(1). :]
  14. #ifndef BASE_CONTAINERS_MRU_CACHE_H_
  15. #define BASE_CONTAINERS_MRU_CACHE_H_
  16. #include <stddef.h>
  17. #include <algorithm>
  18. #include <functional>
  19. #include <list>
  20. #include <map>
  21. #include <unordered_map>
  22. #include <utility>
  23. #include "base/check.h"
  24. namespace base {
  25. namespace trace_event {
  26. namespace internal {
  27. template <class MruCacheType>
  28. size_t DoEstimateMemoryUsageForMruCache(const MruCacheType&);
  29. } // namespace internal
  30. } // namespace trace_event
  31. // MRUCacheBase ----------------------------------------------------------------
  32. // This template is used to standardize map type containers that can be used
  33. // by MRUCacheBase. This level of indirection is necessary because of the way
  34. // that template template params and default template params interact.
  35. template <class KeyType, class ValueType, class CompareType>
  36. struct MRUCacheStandardMap {
  37. typedef std::map<KeyType, ValueType, CompareType> Type;
  38. };
  39. // Base class for the MRU cache specializations defined below.
  40. template <class KeyType,
  41. class PayloadType,
  42. class HashOrCompareType,
  43. template <typename, typename, typename> class MapType =
  44. MRUCacheStandardMap>
  45. class MRUCacheBase {
  46. public:
  47. // The payload of the list. This maintains a copy of the key so we can
  48. // efficiently delete things given an element of the list.
  49. typedef std::pair<KeyType, PayloadType> value_type;
  50. private:
  51. typedef std::list<value_type> PayloadList;
  52. typedef typename MapType<KeyType,
  53. typename PayloadList::iterator,
  54. HashOrCompareType>::Type KeyIndex;
  55. public:
  56. typedef typename PayloadList::size_type size_type;
  57. typedef typename PayloadList::iterator iterator;
  58. typedef typename PayloadList::const_iterator const_iterator;
  59. typedef typename PayloadList::reverse_iterator reverse_iterator;
  60. typedef typename PayloadList::const_reverse_iterator const_reverse_iterator;
  61. enum { NO_AUTO_EVICT = 0 };
  62. // The max_size is the size at which the cache will prune its members to when
  63. // a new item is inserted. If the caller wants to manager this itself (for
  64. // example, maybe it has special work to do when something is evicted), it
  65. // can pass NO_AUTO_EVICT to not restrict the cache size.
  66. explicit MRUCacheBase(size_type max_size) : max_size_(max_size) {}
  67. MRUCacheBase(const MRUCacheBase&) = delete;
  68. MRUCacheBase& operator=(const MRUCacheBase&) = delete;
  69. virtual ~MRUCacheBase() = default;
  70. size_type max_size() const { return max_size_; }
  71. // Inserts a payload item with the given key. If an existing item has
  72. // the same key, it is removed prior to insertion. An iterator indicating the
  73. // inserted item will be returned (this will always be the front of the list).
  74. //
  75. // The payload will be forwarded.
  76. template <typename Payload>
  77. iterator Put(const KeyType& key, Payload&& payload) {
  78. // Remove any existing payload with that key.
  79. typename KeyIndex::iterator index_iter = index_.find(key);
  80. if (index_iter != index_.end()) {
  81. // Erase the reference to it. The index reference will be replaced in the
  82. // code below.
  83. Erase(index_iter->second);
  84. } else if (max_size_ != NO_AUTO_EVICT) {
  85. // New item is being inserted which might make it larger than the maximum
  86. // size: kick the oldest thing out if necessary.
  87. ShrinkToSize(max_size_ - 1);
  88. }
  89. ordering_.emplace_front(key, std::forward<Payload>(payload));
  90. index_.emplace(key, ordering_.begin());
  91. return ordering_.begin();
  92. }
  93. // Retrieves the contents of the given key, or end() if not found. This method
  94. // has the side effect of moving the requested item to the front of the
  95. // recency list.
  96. iterator Get(const KeyType& key) {
  97. typename KeyIndex::iterator index_iter = index_.find(key);
  98. if (index_iter == index_.end())
  99. return end();
  100. typename PayloadList::iterator iter = index_iter->second;
  101. // Move the touched item to the front of the recency ordering.
  102. ordering_.splice(ordering_.begin(), ordering_, iter);
  103. return ordering_.begin();
  104. }
  105. // Retrieves the payload associated with a given key and returns it via
  106. // result without affecting the ordering (unlike Get).
  107. iterator Peek(const KeyType& key) {
  108. typename KeyIndex::const_iterator index_iter = index_.find(key);
  109. if (index_iter == index_.end())
  110. return end();
  111. return index_iter->second;
  112. }
  113. const_iterator Peek(const KeyType& key) const {
  114. typename KeyIndex::const_iterator index_iter = index_.find(key);
  115. if (index_iter == index_.end())
  116. return end();
  117. return index_iter->second;
  118. }
  119. // Exchanges the contents of |this| by the contents of the |other|.
  120. void Swap(MRUCacheBase& other) {
  121. ordering_.swap(other.ordering_);
  122. index_.swap(other.index_);
  123. std::swap(max_size_, other.max_size_);
  124. }
  125. // Erases the item referenced by the given iterator. An iterator to the item
  126. // following it will be returned. The iterator must be valid.
  127. iterator Erase(iterator pos) {
  128. index_.erase(pos->first);
  129. return ordering_.erase(pos);
  130. }
  131. // MRUCache entries are often processed in reverse order, so we add this
  132. // convenience function (not typically defined by STL containers).
  133. reverse_iterator Erase(reverse_iterator pos) {
  134. // We have to actually give it the incremented iterator to delete, since
  135. // the forward iterator that base() returns is actually one past the item
  136. // being iterated over.
  137. return reverse_iterator(Erase((++pos).base()));
  138. }
  139. // Shrinks the cache so it only holds |new_size| items. If |new_size| is
  140. // bigger or equal to the current number of items, this will do nothing.
  141. void ShrinkToSize(size_type new_size) {
  142. for (size_type i = size(); i > new_size; i--)
  143. Erase(rbegin());
  144. }
  145. // Deletes everything from the cache.
  146. void Clear() {
  147. index_.clear();
  148. ordering_.clear();
  149. }
  150. // Returns the number of elements in the cache.
  151. size_type size() const {
  152. // We don't use ordering_.size() for the return value because
  153. // (as a linked list) it can be O(n).
  154. DCHECK(index_.size() == ordering_.size());
  155. return index_.size();
  156. }
  157. // Allows iteration over the list. Forward iteration starts with the most
  158. // recent item and works backwards.
  159. //
  160. // Note that since these iterators are actually iterators over a list, you
  161. // can keep them as you insert or delete things (as long as you don't delete
  162. // the one you are pointing to) and they will still be valid.
  163. iterator begin() { return ordering_.begin(); }
  164. const_iterator begin() const { return ordering_.begin(); }
  165. iterator end() { return ordering_.end(); }
  166. const_iterator end() const { return ordering_.end(); }
  167. reverse_iterator rbegin() { return ordering_.rbegin(); }
  168. const_reverse_iterator rbegin() const { return ordering_.rbegin(); }
  169. reverse_iterator rend() { return ordering_.rend(); }
  170. const_reverse_iterator rend() const { return ordering_.rend(); }
  171. bool empty() const { return ordering_.empty(); }
  172. private:
  173. template <class MruCacheType>
  174. friend size_t trace_event::internal::DoEstimateMemoryUsageForMruCache(
  175. const MruCacheType&);
  176. PayloadList ordering_;
  177. KeyIndex index_;
  178. size_type max_size_;
  179. };
  180. // MRUCache --------------------------------------------------------------------
  181. // A container that does not do anything to free its data. Use this when storing
  182. // value types (as opposed to pointers) in the list.
  183. template <class KeyType,
  184. class PayloadType,
  185. class CompareType = std::less<KeyType>>
  186. class MRUCache : public MRUCacheBase<KeyType, PayloadType, CompareType> {
  187. private:
  188. using ParentType = MRUCacheBase<KeyType, PayloadType, CompareType>;
  189. public:
  190. // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
  191. explicit MRUCache(typename ParentType::size_type max_size)
  192. : ParentType(max_size) {}
  193. MRUCache(const MRUCache&) = delete;
  194. MRUCache& operator=(const MRUCache&) = delete;
  195. ~MRUCache() override = default;
  196. };
  197. // HashingMRUCache ------------------------------------------------------------
  198. template <class KeyType, class ValueType, class HashType>
  199. struct MRUCacheHashMap {
  200. typedef std::unordered_map<KeyType, ValueType, HashType> Type;
  201. };
  202. // This class is similar to MRUCache, except that it uses std::unordered_map as
  203. // the map type instead of std::map. Note that your KeyType must be hashable to
  204. // use this cache or you need to provide a hashing class.
  205. template <class KeyType, class PayloadType, class HashType = std::hash<KeyType>>
  206. class HashingMRUCache
  207. : public MRUCacheBase<KeyType, PayloadType, HashType, MRUCacheHashMap> {
  208. private:
  209. using ParentType =
  210. MRUCacheBase<KeyType, PayloadType, HashType, MRUCacheHashMap>;
  211. public:
  212. // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
  213. explicit HashingMRUCache(typename ParentType::size_type max_size)
  214. : ParentType(max_size) {}
  215. HashingMRUCache(const HashingMRUCache&) = delete;
  216. HashingMRUCache& operator=(const HashingMRUCache&) = delete;
  217. ~HashingMRUCache() override = default;
  218. };
  219. } // namespace base
  220. #endif // BASE_CONTAINERS_MRU_CACHE_H_