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