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