• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 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 // This file contains a template for a Least Recently Used cache that allows
6 // constant-time access to items, but easy identification of the
7 // least-recently-used items for removal. Variations exist to support use as a
8 // Map (`base::LRUCache`), HashMap (`base::HashingLRUCache`), Set
9 // (`base::LRUCacheSet`), or HashSet (`base::HashingLRUCacheSet`). These are
10 // implemented as aliases of `base::internal::LRUCacheBase`, defined at the
11 // bottom of this file.
12 //
13 // The key object (which is identical to the value, in the Set variations) will
14 // be stored twice, so it should support efficient copying.
15 
16 #ifndef BASE_CONTAINERS_LRU_CACHE_H_
17 #define BASE_CONTAINERS_LRU_CACHE_H_
18 
19 #include <stddef.h>
20 
21 #include <algorithm>
22 #include <functional>
23 #include <list>
24 #include <map>
25 #include <type_traits>
26 #include <unordered_map>
27 #include <utility>
28 
29 #include "base/check.h"
30 #include "base/functional/identity.h"
31 
32 namespace base {
33 namespace trace_event::internal {
34 
35 template <class LruCacheType>
36 size_t DoEstimateMemoryUsageForLruCache(const LruCacheType&);
37 
38 }  // namespace trace_event::internal
39 
40 namespace internal {
41 
42 struct GetKeyFromKVPair {
43   template <typename T1, typename T2>
operatorGetKeyFromKVPair44   constexpr const T1& operator()(const std::pair<T1, T2>& pair) {
45     return pair.first;
46   }
47 };
48 
49 // Base class for the LRU cache specializations defined below.
50 template <class ValueType, class GetKeyFromValue, class KeyIndexTemplate>
51 class LRUCacheBase {
52  public:
53   // The contents of the list. This must contain a copy of the key (that may be
54   // extracted via `GetKeyFromValue()(value)` so we can efficiently delete
55   // things given an element of the list.
56   using value_type = ValueType;
57 
58  private:
59   using ValueList = std::list<value_type>;
60   using KeyIndex =
61       typename KeyIndexTemplate::template Type<typename ValueList::iterator>;
62 
63  public:
64   using size_type = typename ValueList::size_type;
65   using key_type = typename KeyIndex::key_type;
66 
67   using iterator = typename ValueList::iterator;
68   using const_iterator = typename ValueList::const_iterator;
69   using reverse_iterator = typename ValueList::reverse_iterator;
70   using const_reverse_iterator = typename ValueList::const_reverse_iterator;
71 
72   enum { NO_AUTO_EVICT = 0 };
73 
74   // The max_size is the size at which the cache will prune its members to when
75   // a new item is inserted. If the caller wants to manage this itself (for
76   // example, maybe it has special work to do when something is evicted), it
77   // can pass NO_AUTO_EVICT to not restrict the cache size.
LRUCacheBase(size_type max_size)78   explicit LRUCacheBase(size_type max_size) : max_size_(max_size) {}
79 
80   LRUCacheBase(const LRUCacheBase&) = delete;
81   LRUCacheBase& operator=(const LRUCacheBase&) = delete;
82 
83   ~LRUCacheBase() = default;
84 
max_size()85   size_type max_size() const { return max_size_; }
86 
87   // Inserts an item into the list. If an existing item has the same key, it is
88   // removed prior to insertion. An iterator indicating the inserted item will
89   // be returned (this will always be the front of the list).
90   // In the map variations of this container, `value_type` is a `std::pair` and
91   // it's preferred to use the `Put(k, v)` overload of this method.
Put(value_type && value)92   iterator Put(value_type&& value) {
93     // Remove any existing item with that key.
94     key_type key = GetKeyFromValue{}(value);
95     typename KeyIndex::iterator index_iter = index_.find(key);
96     if (index_iter != index_.end()) {
97       // Erase the reference to it. The index reference will be replaced in the
98       // code below.
99       Erase(index_iter->second);
100     } else if (max_size_ != NO_AUTO_EVICT) {
101       // New item is being inserted which might make it larger than the maximum
102       // size: kick the oldest thing out if necessary.
103       ShrinkToSize(max_size_ - 1);
104     }
105 
106     ordering_.push_front(std::move(value));
107     index_.emplace(std::move(key), ordering_.begin());
108     return ordering_.begin();
109   }
110 
111   // Inserts an item into the list. If an existing item has the same key, it is
112   // removed prior to insertion. An iterator indicating the inserted item will
113   // be returned (this will always be the front of the list).
114   template <
115       class K,
116       class V,
117       class MapKeyGetter = GetKeyFromKVPair,
118       class = std::enable_if_t<std::is_same_v<MapKeyGetter, GetKeyFromValue>>>
Put(K && key,V && value)119   iterator Put(K&& key, V&& value) {
120     return Put(value_type{std::forward<K>(key), std::forward<V>(value)});
121   }
122 
123   // Retrieves the contents of the given key, or end() if not found. This method
124   // has the side effect of moving the requested item to the front of the
125   // recency list.
Get(const key_type & key)126   iterator Get(const key_type& key) {
127     typename KeyIndex::iterator index_iter = index_.find(key);
128     if (index_iter == index_.end())
129       return end();
130     typename ValueList::iterator iter = index_iter->second;
131 
132     // Move the touched item to the front of the recency ordering.
133     ordering_.splice(ordering_.begin(), ordering_, iter);
134     return ordering_.begin();
135   }
136 
137   // Retrieves the item associated with a given key and returns it via
138   // result without affecting the ordering (unlike Get()).
Peek(const key_type & key)139   iterator Peek(const key_type& key) {
140     typename KeyIndex::const_iterator index_iter = index_.find(key);
141     if (index_iter == index_.end())
142       return end();
143     return index_iter->second;
144   }
145 
Peek(const key_type & key)146   const_iterator Peek(const key_type& key) const {
147     typename KeyIndex::const_iterator index_iter = index_.find(key);
148     if (index_iter == index_.end())
149       return end();
150     return index_iter->second;
151   }
152 
153   // Exchanges the contents of |this| by the contents of the |other|.
Swap(LRUCacheBase & other)154   void Swap(LRUCacheBase& other) {
155     ordering_.swap(other.ordering_);
156     index_.swap(other.index_);
157     std::swap(max_size_, other.max_size_);
158   }
159 
160   // Erases the item referenced by the given iterator. An iterator to the item
161   // following it will be returned. The iterator must be valid.
Erase(iterator pos)162   iterator Erase(iterator pos) {
163     index_.erase(GetKeyFromValue()(*pos));
164     return ordering_.erase(pos);
165   }
166 
167   // LRUCache entries are often processed in reverse order, so we add this
168   // convenience function (not typically defined by STL containers).
Erase(reverse_iterator pos)169   reverse_iterator Erase(reverse_iterator pos) {
170     // We have to actually give it the incremented iterator to delete, since
171     // the forward iterator that base() returns is actually one past the item
172     // being iterated over.
173     return reverse_iterator(Erase((++pos).base()));
174   }
175 
176   // Shrinks the cache so it only holds |new_size| items. If |new_size| is
177   // bigger or equal to the current number of items, this will do nothing.
ShrinkToSize(size_type new_size)178   void ShrinkToSize(size_type new_size) {
179     for (size_type i = size(); i > new_size; i--)
180       Erase(rbegin());
181   }
182 
183   // Deletes everything from the cache.
Clear()184   void Clear() {
185     index_.clear();
186     ordering_.clear();
187   }
188 
189   // Returns the number of elements in the cache.
size()190   size_type size() const {
191     // We don't use ordering_.size() for the return value because
192     // (as a linked list) it can be O(n).
193     DCHECK(index_.size() == ordering_.size());
194     return index_.size();
195   }
196 
197   // Allows iteration over the list. Forward iteration starts with the most
198   // recent item and works backwards.
199   //
200   // Note that since these iterators are actually iterators over a list, you
201   // can keep them as you insert or delete things (as long as you don't delete
202   // the one you are pointing to) and they will still be valid.
begin()203   iterator begin() { return ordering_.begin(); }
begin()204   const_iterator begin() const { return ordering_.begin(); }
end()205   iterator end() { return ordering_.end(); }
end()206   const_iterator end() const { return ordering_.end(); }
207 
rbegin()208   reverse_iterator rbegin() { return ordering_.rbegin(); }
rbegin()209   const_reverse_iterator rbegin() const { return ordering_.rbegin(); }
rend()210   reverse_iterator rend() { return ordering_.rend(); }
rend()211   const_reverse_iterator rend() const { return ordering_.rend(); }
212 
empty()213   bool empty() const { return ordering_.empty(); }
214 
215  private:
216   template <class LruCacheType>
217   friend size_t trace_event::internal::DoEstimateMemoryUsageForLruCache(
218       const LruCacheType&);
219 
220   ValueList ordering_;
221   KeyIndex index_;
222 
223   size_type max_size_;
224 };
225 
226 template <class KeyType, class KeyCompare>
227 struct LRUCacheKeyIndex {
228   template <class ValueType>
229   using Type = std::map<KeyType, ValueType, KeyCompare>;
230 };
231 
232 template <class KeyType, class KeyHash, class KeyEqual>
233 struct HashingLRUCacheKeyIndex {
234   template <class ValueType>
235   using Type = std::unordered_map<KeyType, ValueType, KeyHash, KeyEqual>;
236 };
237 
238 }  // namespace internal
239 
240 // Implements an LRU cache of `ValueType`, where each value can be uniquely
241 // referenced by `KeyType`. Entries can be iterated in order of
242 // least-recently-used to most-recently-used by iterating from `rbegin()` to
243 // `rend()`, where a "use" is defined as a call to `Put(k, v)` or `Get(k)`.
244 template <class KeyType, class ValueType, class KeyCompare = std::less<KeyType>>
245 using LRUCache =
246     internal::LRUCacheBase<std::pair<KeyType, ValueType>,
247                            internal::GetKeyFromKVPair,
248                            internal::LRUCacheKeyIndex<KeyType, KeyCompare>>;
249 
250 // Implements an LRU cache of `ValueType`, where each value can be uniquely
251 // referenced by `KeyType`, and `KeyType` may be hashed for O(1) insertion,
252 // removal, and lookup. Entries can be iterated in order of least-recently-used
253 // to most-recently-used by iterating from `rbegin()` to `rend()`, where a "use"
254 // is defined as a call to `Put(k, v)` or `Get(k)`.
255 template <class KeyType,
256           class ValueType,
257           class KeyHash = std::hash<KeyType>,
258           class KeyEqual = std::equal_to<KeyType>>
259 using HashingLRUCache = internal::LRUCacheBase<
260     std::pair<KeyType, ValueType>,
261     internal::GetKeyFromKVPair,
262     internal::HashingLRUCacheKeyIndex<KeyType, KeyHash, KeyEqual>>;
263 
264 // Implements an LRU cache of `ValueType`, where each value is unique. Entries
265 // can be iterated in order of least-recently-used to most-recently-used by
266 // iterating from `rbegin()` to `rend()`, where a "use" is defined as a call to
267 // `Put(v)` or `Get(v)`.
268 template <class ValueType, class Compare = std::less<ValueType>>
269 using LRUCacheSet =
270     internal::LRUCacheBase<ValueType,
271                            identity,
272                            internal::LRUCacheKeyIndex<ValueType, Compare>>;
273 
274 // Implements an LRU cache of `ValueType`, where is value is unique, and may be
275 // hashed for O(1) insertion, removal, and lookup. Entries can be iterated in
276 // order of least-recently-used to most-recently-used by iterating from
277 // `rbegin()` to `rend()`, where a "use" is defined as a call to `Put(v)` or
278 // `Get(v)`.
279 template <class ValueType,
280           class Hash = std::hash<ValueType>,
281           class Equal = std::equal_to<ValueType>>
282 using HashingLRUCacheSet = internal::LRUCacheBase<
283     ValueType,
284     identity,
285     internal::HashingLRUCacheKeyIndex<ValueType, Hash, Equal>>;
286 
287 }  // namespace base
288 
289 #endif  // BASE_CONTAINERS_LRU_CACHE_H_
290