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 BASE_CONTAINERS_MRU_CACHE_H_ 17 #define BASE_CONTAINERS_MRU_CACHE_H_ 18 19 #include <list> 20 #include <map> 21 #include <utility> 22 23 #include "base/basictypes.h" 24 #include "base/containers/hash_tables.h" 25 #include "base/logging.h" 26 27 namespace base { 28 29 // MRUCacheBase ---------------------------------------------------------------- 30 31 // This template is used to standardize map type containers that can be used 32 // by MRUCacheBase. This level of indirection is necessary because of the way 33 // that template template params and default template params interact. 34 template <class KeyType, class ValueType> 35 struct MRUCacheStandardMap { 36 typedef std::map<KeyType, ValueType> Type; 37 }; 38 39 // Base class for the MRU cache specializations defined below. 40 // The deletor will get called on all payloads that are being removed or 41 // replaced. 42 template <class KeyType, class PayloadType, class DeletorType, 43 template <typename, typename> class MapType = MRUCacheStandardMap> 44 class MRUCacheBase { 45 public: 46 // The payload of the list. This maintains a copy of the key so we can 47 // efficiently delete things given an element of the list. 48 typedef std::pair<KeyType, PayloadType> value_type; 49 50 private: 51 typedef std::list<value_type> PayloadList; 52 typedef typename MapType<KeyType, 53 typename PayloadList::iterator>::Type KeyIndex; 54 55 public: 56 typedef typename PayloadList::size_type size_type; 57 58 typedef typename PayloadList::iterator iterator; 59 typedef typename PayloadList::const_iterator const_iterator; 60 typedef typename PayloadList::reverse_iterator reverse_iterator; 61 typedef typename PayloadList::const_reverse_iterator const_reverse_iterator; 62 63 enum { NO_AUTO_EVICT = 0 }; 64 65 // The max_size is the size at which the cache will prune its members to when 66 // a new item is inserted. If the caller wants to manager this itself (for 67 // example, maybe it has special work to do when something is evicted), it 68 // can pass NO_AUTO_EVICT to not restrict the cache size. 69 explicit MRUCacheBase(size_type max_size) : max_size_(max_size) { 70 } 71 72 MRUCacheBase(size_type max_size, const DeletorType& deletor) 73 : max_size_(max_size), deletor_(deletor) { 74 } 75 76 virtual ~MRUCacheBase() { 77 iterator i = begin(); 78 while (i != end()) 79 i = Erase(i); 80 } 81 82 size_type max_size() const { return max_size_; } 83 84 // Inserts a payload item with the given key. If an existing item has 85 // the same key, it is removed prior to insertion. An iterator indicating the 86 // inserted item will be returned (this will always be the front of the list). 87 // 88 // The payload will be copied. In the case of an OwningMRUCache, this function 89 // will take ownership of the pointer. 90 iterator Put(const KeyType& key, const PayloadType& payload) { 91 // Remove any existing payload with that key. 92 typename KeyIndex::iterator index_iter = index_.find(key); 93 if (index_iter != index_.end()) { 94 // Erase the reference to it. This will call the deletor on the removed 95 // element. The index reference will be replaced in the code below. 96 Erase(index_iter->second); 97 } else if (max_size_ != NO_AUTO_EVICT) { 98 // New item is being inserted which might make it larger than the maximum 99 // size: kick the oldest thing out if necessary. 100 ShrinkToSize(max_size_ - 1); 101 } 102 103 ordering_.push_front(value_type(key, payload)); 104 index_.insert(std::make_pair(key, ordering_.begin())); 105 return ordering_.begin(); 106 } 107 108 // Retrieves the contents of the given key, or end() if not found. This method 109 // has the side effect of moving the requested item to the front of the 110 // recency list. 111 // 112 // TODO(brettw) We may want a const version of this function in the future. 113 iterator Get(const KeyType& key) { 114 typename KeyIndex::iterator index_iter = index_.find(key); 115 if (index_iter == index_.end()) 116 return end(); 117 typename PayloadList::iterator iter = index_iter->second; 118 119 // Move the touched item to the front of the recency ordering. 120 ordering_.splice(ordering_.begin(), ordering_, iter); 121 return ordering_.begin(); 122 } 123 124 // Retrieves the payload associated with a given key and returns it via 125 // result without affecting the ordering (unlike Get). 126 iterator Peek(const KeyType& key) { 127 typename KeyIndex::const_iterator index_iter = index_.find(key); 128 if (index_iter == index_.end()) 129 return end(); 130 return index_iter->second; 131 } 132 133 const_iterator Peek(const KeyType& key) const { 134 typename KeyIndex::const_iterator index_iter = index_.find(key); 135 if (index_iter == index_.end()) 136 return end(); 137 return index_iter->second; 138 } 139 140 // Erases the item referenced by the given iterator. An iterator to the item 141 // following it will be returned. The iterator must be valid. 142 iterator Erase(iterator pos) { 143 deletor_(pos->second); 144 index_.erase(pos->first); 145 return ordering_.erase(pos); 146 } 147 148 // MRUCache entries are often processed in reverse order, so we add this 149 // convenience function (not typically defined by STL containers). 150 reverse_iterator Erase(reverse_iterator pos) { 151 // We have to actually give it the incremented iterator to delete, since 152 // the forward iterator that base() returns is actually one past the item 153 // being iterated over. 154 return reverse_iterator(Erase((++pos).base())); 155 } 156 157 // Shrinks the cache so it only holds |new_size| items. If |new_size| is 158 // bigger or equal to the current number of items, this will do nothing. 159 void ShrinkToSize(size_type new_size) { 160 for (size_type i = size(); i > new_size; i--) 161 Erase(rbegin()); 162 } 163 164 // Deletes everything from the cache. 165 void Clear() { 166 for (typename PayloadList::iterator i(ordering_.begin()); 167 i != ordering_.end(); ++i) 168 deletor_(i->second); 169 index_.clear(); 170 ordering_.clear(); 171 } 172 173 // Returns the number of elements in the cache. 174 size_type size() const { 175 // We don't use ordering_.size() for the return value because 176 // (as a linked list) it can be O(n). 177 DCHECK(index_.size() == ordering_.size()); 178 return index_.size(); 179 } 180 181 // Allows iteration over the list. Forward iteration starts with the most 182 // recent item and works backwards. 183 // 184 // Note that since these iterators are actually iterators over a list, you 185 // can keep them as you insert or delete things (as long as you don't delete 186 // the one you are pointing to) and they will still be valid. 187 iterator begin() { return ordering_.begin(); } 188 const_iterator begin() const { return ordering_.begin(); } 189 iterator end() { return ordering_.end(); } 190 const_iterator end() const { return ordering_.end(); } 191 192 reverse_iterator rbegin() { return ordering_.rbegin(); } 193 const_reverse_iterator rbegin() const { return ordering_.rbegin(); } 194 reverse_iterator rend() { return ordering_.rend(); } 195 const_reverse_iterator rend() const { return ordering_.rend(); } 196 197 bool empty() const { return ordering_.empty(); } 198 199 private: 200 PayloadList ordering_; 201 KeyIndex index_; 202 203 size_type max_size_; 204 205 DeletorType deletor_; 206 207 DISALLOW_COPY_AND_ASSIGN(MRUCacheBase); 208 }; 209 210 // MRUCache -------------------------------------------------------------------- 211 212 // A functor that does nothing. Used by the MRUCache. 213 template<class PayloadType> 214 class MRUCacheNullDeletor { 215 public: 216 void operator()(PayloadType& payload) { 217 } 218 }; 219 220 // A container that does not do anything to free its data. Use this when storing 221 // value types (as opposed to pointers) in the list. 222 template <class KeyType, class PayloadType> 223 class MRUCache : public MRUCacheBase<KeyType, 224 PayloadType, 225 MRUCacheNullDeletor<PayloadType> > { 226 private: 227 typedef MRUCacheBase<KeyType, PayloadType, 228 MRUCacheNullDeletor<PayloadType> > ParentType; 229 230 public: 231 // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. 232 explicit MRUCache(typename ParentType::size_type max_size) 233 : ParentType(max_size) { 234 } 235 virtual ~MRUCache() { 236 } 237 238 private: 239 DISALLOW_COPY_AND_ASSIGN(MRUCache); 240 }; 241 242 // OwningMRUCache -------------------------------------------------------------- 243 244 template<class PayloadType> 245 class MRUCachePointerDeletor { 246 public: 247 void operator()(PayloadType& payload) { 248 delete payload; 249 } 250 }; 251 252 // A cache that owns the payload type, which must be a non-const pointer type. 253 // The pointers will be deleted when they are removed, replaced, or when the 254 // cache is destroyed. 255 template <class KeyType, class PayloadType> 256 class OwningMRUCache 257 : public MRUCacheBase<KeyType, 258 PayloadType, 259 MRUCachePointerDeletor<PayloadType> > { 260 private: 261 typedef MRUCacheBase<KeyType, PayloadType, 262 MRUCachePointerDeletor<PayloadType> > ParentType; 263 264 public: 265 // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. 266 explicit OwningMRUCache(typename ParentType::size_type max_size) 267 : ParentType(max_size) { 268 } 269 virtual ~OwningMRUCache() { 270 } 271 272 private: 273 DISALLOW_COPY_AND_ASSIGN(OwningMRUCache); 274 }; 275 276 // HashingMRUCache ------------------------------------------------------------ 277 278 template <class KeyType, class ValueType> 279 struct MRUCacheHashMap { 280 typedef base::hash_map<KeyType, ValueType> Type; 281 }; 282 283 // This class is similar to MRUCache, except that it uses base::hash_map as 284 // the map type instead of std::map. Note that your KeyType must be hashable 285 // to use this cache. 286 template <class KeyType, class PayloadType> 287 class HashingMRUCache : public MRUCacheBase<KeyType, 288 PayloadType, 289 MRUCacheNullDeletor<PayloadType>, 290 MRUCacheHashMap> { 291 private: 292 typedef MRUCacheBase<KeyType, PayloadType, 293 MRUCacheNullDeletor<PayloadType>, 294 MRUCacheHashMap> ParentType; 295 296 public: 297 // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. 298 explicit HashingMRUCache(typename ParentType::size_type max_size) 299 : ParentType(max_size) { 300 } 301 virtual ~HashingMRUCache() { 302 } 303 304 private: 305 DISALLOW_COPY_AND_ASSIGN(HashingMRUCache); 306 }; 307 308 } // namespace base 309 310 #endif // BASE_CONTAINERS_MRU_CACHE_H_ 311