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