xref: /aosp_15_r20/external/libchrome/base/containers/mru_cache.h (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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