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