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.
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.
10 // The key object will be stored twice, so it should support efficient copying.
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). :]
16 #ifndef BASE_CONTAINERS_MRU_CACHE_H_
17 #define BASE_CONTAINERS_MRU_CACHE_H_
23 #include "base/basictypes.h"
24 #include "base/containers/hash_tables.h"
25 #include "base/logging.h"
29 // MRUCacheBase ----------------------------------------------------------------
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
;
39 // Base class for the MRU cache specializations defined below.
40 // The deletor will get called on all payloads that are being removed or
42 template <class KeyType
, class PayloadType
, class DeletorType
,
43 template <typename
, typename
> class MapType
= MRUCacheStandardMap
>
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
;
51 typedef std::list
<value_type
> PayloadList
;
52 typedef typename MapType
<KeyType
,
53 typename
PayloadList::iterator
>::Type KeyIndex
;
56 typedef typename
PayloadList::size_type size_type
;
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
;
63 enum { NO_AUTO_EVICT
= 0 };
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
) {
72 MRUCacheBase(size_type max_size
, const DeletorType
& deletor
)
73 : max_size_(max_size
), deletor_(deletor
) {
76 virtual ~MRUCacheBase() {
82 size_type
max_size() const { return max_size_
; }
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).
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);
103 ordering_
.push_front(value_type(key
, payload
));
104 index_
.insert(std::make_pair(key
, ordering_
.begin()));
105 return ordering_
.begin();
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
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())
117 typename
PayloadList::iterator iter
= index_iter
->second
;
119 // Move the touched item to the front of the recency ordering.
120 ordering_
.splice(ordering_
.begin(), ordering_
, iter
);
121 return ordering_
.begin();
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())
130 return index_iter
->second
;
133 const_iterator
Peek(const KeyType
& key
) const {
134 typename
KeyIndex::const_iterator index_iter
= index_
.find(key
);
135 if (index_iter
== index_
.end())
137 return index_iter
->second
;
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
);
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()));
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
--)
164 // Deletes everything from the cache.
166 for (typename
PayloadList::iterator
i(ordering_
.begin());
167 i
!= ordering_
.end(); ++i
)
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();
181 // Allows iteration over the list. Forward iteration starts with the most
182 // recent item and works backwards.
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(); }
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(); }
197 bool empty() const { return ordering_
.empty(); }
200 PayloadList ordering_
;
205 DeletorType deletor_
;
207 DISALLOW_COPY_AND_ASSIGN(MRUCacheBase
);
210 // MRUCache --------------------------------------------------------------------
212 // A functor that does nothing. Used by the MRUCache.
213 template<class PayloadType
>
214 class MRUCacheNullDeletor
{
216 void operator()(const PayloadType
& payload
) {}
219 // A container that does not do anything to free its data. Use this when storing
220 // value types (as opposed to pointers) in the list.
221 template <class KeyType
, class PayloadType
>
222 class MRUCache
: public MRUCacheBase
<KeyType
,
224 MRUCacheNullDeletor
<PayloadType
> > {
226 typedef MRUCacheBase
<KeyType
, PayloadType
,
227 MRUCacheNullDeletor
<PayloadType
> > ParentType
;
230 // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
231 explicit MRUCache(typename
ParentType::size_type max_size
)
232 : ParentType(max_size
) {
234 virtual ~MRUCache() {
238 DISALLOW_COPY_AND_ASSIGN(MRUCache
);
241 // OwningMRUCache --------------------------------------------------------------
243 template<class PayloadType
>
244 class MRUCachePointerDeletor
{
246 void operator()(const PayloadType
& payload
) { delete payload
; }
249 // A cache that owns the payload type, which must be a non-const pointer type.
250 // The pointers will be deleted when they are removed, replaced, or when the
251 // cache is destroyed.
252 template <class KeyType
, class PayloadType
>
254 : public MRUCacheBase
<KeyType
,
256 MRUCachePointerDeletor
<PayloadType
> > {
258 typedef MRUCacheBase
<KeyType
, PayloadType
,
259 MRUCachePointerDeletor
<PayloadType
> > ParentType
;
262 // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
263 explicit OwningMRUCache(typename
ParentType::size_type max_size
)
264 : ParentType(max_size
) {
266 virtual ~OwningMRUCache() {
270 DISALLOW_COPY_AND_ASSIGN(OwningMRUCache
);
273 // HashingMRUCache ------------------------------------------------------------
275 template <class KeyType
, class ValueType
>
276 struct MRUCacheHashMap
{
277 typedef base::hash_map
<KeyType
, ValueType
> Type
;
280 // This class is similar to MRUCache, except that it uses base::hash_map as
281 // the map type instead of std::map. Note that your KeyType must be hashable
282 // to use this cache.
283 template <class KeyType
, class PayloadType
>
284 class HashingMRUCache
: public MRUCacheBase
<KeyType
,
286 MRUCacheNullDeletor
<PayloadType
>,
289 typedef MRUCacheBase
<KeyType
, PayloadType
,
290 MRUCacheNullDeletor
<PayloadType
>,
291 MRUCacheHashMap
> ParentType
;
294 // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
295 explicit HashingMRUCache(typename
ParentType::size_type max_size
)
296 : ParentType(max_size
) {
298 virtual ~HashingMRUCache() {
302 DISALLOW_COPY_AND_ASSIGN(HashingMRUCache
);
307 #endif // BASE_CONTAINERS_MRU_CACHE_H_