• 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 <concepts>
23 #include <functional>
24 #include <list>
25 #include <map>
26 #include <type_traits>
27 #include <unordered_map>
28 #include <utility>
29 
30 #include "base/check.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   // In theory, LRUCacheBase could be copyable, but since copying `ValueList`
81   // might be costly, it's currently move-only to ensure users don't
82   // accidentally incur performance penalties. If you need this to become
83   // copyable, talk to base/ OWNERS.
84   LRUCacheBase(LRUCacheBase&&) noexcept = default;
85   LRUCacheBase& operator=(LRUCacheBase&&) noexcept = default;
86 
87   ~LRUCacheBase() = default;
88 
max_size()89   size_type max_size() const { return max_size_; }
90 
91   // Inserts an item into the list. If an existing item has the same key, it is
92   // removed prior to insertion. An iterator indicating the inserted item will
93   // be returned (this will always be the front of the list).
94   // In the map variations of this container, `value_type` is a `std::pair` and
95   // it's preferred to use the `Put(k, v)` overload of this method.
Put(value_type && value)96   iterator Put(value_type&& value) {
97     // Remove any existing item with that key.
98     key_type key = GetKeyFromValue{}(value);
99     typename KeyIndex::iterator index_iter = index_.find(key);
100     if (index_iter != index_.end()) {
101       // Erase the reference to it. The index reference will be replaced in the
102       // code below.
103       Erase(index_iter->second);
104     } else if (max_size_ != NO_AUTO_EVICT) {
105       // New item is being inserted which might make it larger than the maximum
106       // size: kick the oldest thing out if necessary.
107       ShrinkToSize(max_size_ - 1);
108     }
109 
110     ordering_.push_front(std::move(value));
111     index_.emplace(std::move(key), ordering_.begin());
112     return ordering_.begin();
113   }
114 
115   // Inserts an item into the list. If an existing item has the same key, it is
116   // removed prior to insertion. An iterator indicating the inserted item will
117   // be returned (this will always be the front of the list).
118   template <class K, class V>
requires(std::same_as<GetKeyFromValue,GetKeyFromKVPair>)119     requires(std::same_as<GetKeyFromValue, GetKeyFromKVPair>)
120   iterator Put(K&& key, V&& value) {
121     return Put(value_type{std::forward<K>(key), std::forward<V>(value)});
122   }
123 
124   // Retrieves the contents of the given key, or end() if not found. This method
125   // has the side effect of moving the requested item to the front of the
126   // recency list.
Get(const key_type & key)127   iterator Get(const key_type& key) {
128     typename KeyIndex::iterator index_iter = index_.find(key);
129     if (index_iter == index_.end())
130       return end();
131     typename ValueList::iterator iter = index_iter->second;
132 
133     // Move the touched item to the front of the recency ordering.
134     ordering_.splice(ordering_.begin(), ordering_, iter);
135     return ordering_.begin();
136   }
137 
138   // Retrieves the item associated with a given key and returns it via
139   // result without affecting the ordering (unlike Get()).
Peek(const key_type & key)140   iterator Peek(const key_type& key) {
141     typename KeyIndex::const_iterator index_iter = index_.find(key);
142     if (index_iter == index_.end())
143       return end();
144     return index_iter->second;
145   }
146 
Peek(const key_type & key)147   const_iterator Peek(const key_type& key) const {
148     typename KeyIndex::const_iterator index_iter = index_.find(key);
149     if (index_iter == index_.end())
150       return end();
151     return index_iter->second;
152   }
153 
154   // Exchanges the contents of |this| by the contents of the |other|.
Swap(LRUCacheBase & other)155   void Swap(LRUCacheBase& other) {
156     ordering_.swap(other.ordering_);
157     index_.swap(other.index_);
158     std::swap(max_size_, other.max_size_);
159   }
160 
161   // Erases the item referenced by the given iterator. An iterator to the item
162   // following it will be returned. The iterator must be valid.
Erase(iterator pos)163   iterator Erase(iterator pos) {
164     index_.erase(GetKeyFromValue()(*pos));
165     return ordering_.erase(pos);
166   }
167 
168   // LRUCache entries are often processed in reverse order, so we add this
169   // convenience function (not typically defined by STL containers).
Erase(reverse_iterator pos)170   reverse_iterator Erase(reverse_iterator pos) {
171     // We have to actually give it the incremented iterator to delete, since
172     // the forward iterator that base() returns is actually one past the item
173     // being iterated over.
174     return reverse_iterator(Erase((++pos).base()));
175   }
176 
177   // Shrinks the cache so it only holds |new_size| items. If |new_size| is
178   // bigger or equal to the current number of items, this will do nothing.
ShrinkToSize(size_type new_size)179   void ShrinkToSize(size_type new_size) {
180     for (size_type i = size(); i > new_size; i--)
181       Erase(rbegin());
182   }
183 
184   // Deletes everything from the cache.
Clear()185   void Clear() {
186     index_.clear();
187     ordering_.clear();
188   }
189 
190   // Returns the number of elements in the cache.
size()191   size_type size() const {
192     // We don't use ordering_.size() for the return value because
193     // (as a linked list) it can be O(n).
194     DCHECK(index_.size() == ordering_.size());
195     return index_.size();
196   }
197 
198   // Allows iteration over the list. Forward iteration starts with the most
199   // recent item and works backwards.
200   //
201   // Note that since these iterators are actually iterators over a list, you
202   // can keep them as you insert or delete things (as long as you don't delete
203   // the one you are pointing to) and they will still be valid.
begin()204   iterator begin() { return ordering_.begin(); }
begin()205   const_iterator begin() const { return ordering_.begin(); }
end()206   iterator end() { return ordering_.end(); }
end()207   const_iterator end() const { return ordering_.end(); }
208 
rbegin()209   reverse_iterator rbegin() { return ordering_.rbegin(); }
rbegin()210   const_reverse_iterator rbegin() const { return ordering_.rbegin(); }
rend()211   reverse_iterator rend() { return ordering_.rend(); }
rend()212   const_reverse_iterator rend() const { return ordering_.rend(); }
213 
214   struct IndexRange {
215     using iterator = KeyIndex::const_iterator;
216 
IndexRangeIndexRange217     IndexRange(const iterator& begin, const iterator& end)
218         : begin_(begin), end_(end) {}
219 
beginIndexRange220     iterator begin() const { return begin_; }
endIndexRange221     iterator end() const { return end_; }
222 
223    private:
224     iterator begin_;
225     iterator end_;
226   };
227   // Allows iterating the index, which can be useful when the index is ordered.
index()228   IndexRange index() const { return IndexRange(index_.begin(), index_.end()); }
229 
empty()230   bool empty() const { return ordering_.empty(); }
231 
232  private:
233   template <class LruCacheType>
234   friend size_t trace_event::internal::DoEstimateMemoryUsageForLruCache(
235       const LruCacheType&);
236 
237   ValueList ordering_;
238   // TODO(crbug.com/40069408): Remove annotation once crbug.com/1472363 is
239   // fixed.
240   __attribute__((annotate("blink_gc_plugin_ignore"))) KeyIndex index_;
241 
242   size_type max_size_;
243 };
244 
245 template <class KeyType, class KeyCompare>
246 struct LRUCacheKeyIndex {
247   template <class ValueType>
248   using Type = std::map<KeyType, ValueType, KeyCompare>;
249 };
250 
251 template <class KeyType, class KeyHash, class KeyEqual>
252 struct HashingLRUCacheKeyIndex {
253   template <class ValueType>
254   using Type = std::unordered_map<KeyType, ValueType, KeyHash, KeyEqual>;
255 };
256 
257 }  // namespace internal
258 
259 // Implements an LRU cache of `ValueType`, where each value can be uniquely
260 // referenced by `KeyType`. Entries can be iterated in order of
261 // least-recently-used to most-recently-used by iterating from `rbegin()` to
262 // `rend()`, where a "use" is defined as a call to `Put(k, v)` or `Get(k)`.
263 template <class KeyType, class ValueType, class KeyCompare = std::less<KeyType>>
264 using LRUCache =
265     internal::LRUCacheBase<std::pair<KeyType, ValueType>,
266                            internal::GetKeyFromKVPair,
267                            internal::LRUCacheKeyIndex<KeyType, KeyCompare>>;
268 
269 // Implements an LRU cache of `ValueType`, where each value can be uniquely
270 // referenced by `KeyType`, and `KeyType` may be hashed for O(1) insertion,
271 // removal, and lookup. Entries can be iterated in order of least-recently-used
272 // to most-recently-used by iterating from `rbegin()` to `rend()`, where a "use"
273 // is defined as a call to `Put(k, v)` or `Get(k)`.
274 template <class KeyType,
275           class ValueType,
276           class KeyHash = std::hash<KeyType>,
277           class KeyEqual = std::equal_to<KeyType>>
278 using HashingLRUCache = internal::LRUCacheBase<
279     std::pair<KeyType, ValueType>,
280     internal::GetKeyFromKVPair,
281     internal::HashingLRUCacheKeyIndex<KeyType, KeyHash, KeyEqual>>;
282 
283 // Implements an LRU cache of `ValueType`, where each value is unique. Entries
284 // can be iterated in order of least-recently-used to most-recently-used by
285 // iterating from `rbegin()` to `rend()`, where a "use" is defined as a call to
286 // `Put(v)` or `Get(v)`.
287 template <class ValueType, class Compare = std::less<ValueType>>
288 using LRUCacheSet =
289     internal::LRUCacheBase<ValueType,
290                            std::identity,
291                            internal::LRUCacheKeyIndex<ValueType, Compare>>;
292 
293 // Implements an LRU cache of `ValueType`, where is value is unique, and may be
294 // hashed for O(1) insertion, removal, and lookup. Entries can be iterated in
295 // order of least-recently-used to most-recently-used by iterating from
296 // `rbegin()` to `rend()`, where a "use" is defined as a call to `Put(v)` or
297 // `Get(v)`.
298 template <class ValueType,
299           class Hash = std::hash<ValueType>,
300           class Equal = std::equal_to<ValueType>>
301 using HashingLRUCacheSet = internal::LRUCacheBase<
302     ValueType,
303     std::identity,
304     internal::HashingLRUCacheKeyIndex<ValueType, Hash, Equal>>;
305 
306 }  // namespace base
307 
308 #endif  // BASE_CONTAINERS_LRU_CACHE_H_
309