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