1 /*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef ANDROID_UTILS_LRU_CACHE_H
18 #define ANDROID_UTILS_LRU_CACHE_H
19
20 #include <memory>
21 #include <unordered_set>
22
23 #include "utils/TypeHelpers.h" // hash_t
24
25 namespace android {
26
27 /**
28 * GenerationCache callback used when an item is removed
29 */
30 template <typename EntryKey, typename EntryValue>
31 class OnEntryRemoved {
32 public:
~OnEntryRemoved()33 virtual ~OnEntryRemoved(){};
34 virtual void operator()(EntryKey& key, EntryValue& value) = 0;
35 }; // class OnEntryRemoved
36
37 template <typename TKey, typename TValue>
38 class LruCache {
39 public:
40 explicit LruCache(uint32_t maxCapacity);
41 virtual ~LruCache();
42
43 enum Capacity {
44 kUnlimitedCapacity,
45 };
46
47 void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
48 size_t size() const;
49 const TValue& get(const TKey& key);
50 bool put(const TKey& key, const TValue& value);
51 bool remove(const TKey& key);
52 bool removeOldest();
53 void clear();
54 const TValue& peekOldestValue();
55
56 private:
57 LruCache(const LruCache& that); // disallow copy constructor
58
59 // Super class so that we can have entries having only a key reference, for
60 // searches.
61 class KeyedEntry {
62 public:
63 virtual const TKey& getKey() const = 0;
64 // Make sure the right destructor is executed so that keys and values are
65 // deleted.
~KeyedEntry()66 virtual ~KeyedEntry() {}
67 };
68
69 class Entry final : public KeyedEntry {
70 public:
71 TKey key;
72 TValue value;
73 Entry* parent;
74 Entry* child;
75
Entry(TKey _key,TValue _value)76 Entry(TKey _key, TValue _value)
77 : key(_key), value(_value), parent(NULL), child(NULL) {}
getKey()78 const TKey& getKey() const final { return key; }
79 };
80
81 class EntryForSearch : public KeyedEntry {
82 public:
83 const TKey& key;
EntryForSearch(const TKey & key_)84 EntryForSearch(const TKey& key_) : key(key_) {}
getKey()85 const TKey& getKey() const final { return key; }
86 };
87
88 struct HashForEntry {
operatorHashForEntry89 size_t operator()(const KeyedEntry* entry) const {
90 return hash_type(entry->getKey());
91 };
92 };
93
94 struct EqualityForHashedEntries {
operatorEqualityForHashedEntries95 bool operator()(const KeyedEntry* lhs, const KeyedEntry* rhs) const {
96 return lhs->getKey() == rhs->getKey();
97 };
98 };
99
100 // All entries in the set will be Entry*. Using the weaker KeyedEntry as to
101 // allow entries that have only a key reference, for searching.
102 typedef std::
103 unordered_set<KeyedEntry*, HashForEntry, EqualityForHashedEntries>
104 LruCacheSet;
105
106 void attachToCache(Entry& entry);
107 void detachFromCache(Entry& entry);
108
findByKey(const TKey & key)109 typename LruCacheSet::iterator findByKey(const TKey& key) {
110 EntryForSearch entryForSearch(key);
111 typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
112 return result;
113 }
114
115 std::unique_ptr<LruCacheSet> mSet;
116 OnEntryRemoved<TKey, TValue>* mListener;
117 Entry* mOldest;
118 Entry* mYoungest;
119 uint32_t mMaxCapacity;
120 TValue mNullValue;
121
122 public:
123 // To be used like:
124 // while (it.next()) {
125 // it.value(); it.key();
126 // }
127 class Iterator {
128 public:
Iterator(const LruCache<TKey,TValue> & cache)129 Iterator(const LruCache<TKey, TValue>& cache)
130 : mCache(cache),
131 mIterator(mCache.mSet->begin()),
132 mBeginReturned(false) {}
133
next()134 bool next() {
135 if (mIterator == mCache.mSet->end()) {
136 return false;
137 }
138 if (!mBeginReturned) {
139 // mIterator has been initialized to the beginning and
140 // hasn't been returned. Do not advance:
141 mBeginReturned = true;
142 } else {
143 std::advance(mIterator, 1);
144 }
145 bool ret = (mIterator != mCache.mSet->end());
146 return ret;
147 }
148
value()149 const TValue& value() const {
150 // All the elements in the set are of type Entry. See comment in the
151 // definition of LruCacheSet above.
152 return reinterpret_cast<Entry*>(*mIterator)->value;
153 }
154
key()155 const TKey& key() const { return (*mIterator)->getKey(); }
156
157 private:
158 const LruCache<TKey, TValue>& mCache;
159 typename LruCacheSet::iterator mIterator;
160 bool mBeginReturned;
161 };
162 };
163
164 // Implementation is here, because it's fully templated
165 template <typename TKey, typename TValue>
LruCache(uint32_t maxCapacity)166 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
167 : mSet(new LruCacheSet()),
168 mListener(NULL),
169 mOldest(NULL),
170 mYoungest(NULL),
171 mMaxCapacity(maxCapacity),
172 mNullValue(0) {
173 mSet->max_load_factor(1.0);
174 };
175
176 template <typename TKey, typename TValue>
~LruCache()177 LruCache<TKey, TValue>::~LruCache() {
178 // Need to delete created entries.
179 clear();
180 };
181
182 template <typename K, typename V>
setOnEntryRemovedListener(OnEntryRemoved<K,V> * listener)183 void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
184 mListener = listener;
185 }
186
187 template <typename TKey, typename TValue>
size()188 size_t LruCache<TKey, TValue>::size() const {
189 return mSet->size();
190 }
191
192 template <typename TKey, typename TValue>
get(const TKey & key)193 const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
194 typename LruCacheSet::const_iterator find_result = findByKey(key);
195 if (find_result == mSet->end()) {
196 return mNullValue;
197 }
198 // All the elements in the set are of type Entry. See comment in the
199 // definition of LruCacheSet above.
200 Entry* entry = reinterpret_cast<Entry*>(*find_result);
201 detachFromCache(*entry);
202 attachToCache(*entry);
203 return entry->value;
204 }
205
206 template <typename TKey, typename TValue>
put(const TKey & key,const TValue & value)207 bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
208 if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
209 removeOldest();
210 }
211
212 if (findByKey(key) != mSet->end()) {
213 return false;
214 }
215
216 Entry* newEntry = new Entry(key, value);
217 mSet->insert(newEntry);
218 attachToCache(*newEntry);
219 return true;
220 }
221
222 template <typename TKey, typename TValue>
remove(const TKey & key)223 bool LruCache<TKey, TValue>::remove(const TKey& key) {
224 typename LruCacheSet::const_iterator find_result = findByKey(key);
225 if (find_result == mSet->end()) {
226 return false;
227 }
228 // All the elements in the set are of type Entry. See comment in the
229 // definition of LruCacheSet above.
230 Entry* entry = reinterpret_cast<Entry*>(*find_result);
231 mSet->erase(entry);
232 if (mListener) {
233 (*mListener)(entry->key, entry->value);
234 }
235 detachFromCache(*entry);
236 delete entry;
237 return true;
238 }
239
240 template <typename TKey, typename TValue>
removeOldest()241 bool LruCache<TKey, TValue>::removeOldest() {
242 if (mOldest != NULL) {
243 return remove(mOldest->key);
244 // TODO: should probably abort if false
245 }
246 return false;
247 }
248
249 template <typename TKey, typename TValue>
peekOldestValue()250 const TValue& LruCache<TKey, TValue>::peekOldestValue() {
251 if (mOldest) {
252 return mOldest->value;
253 }
254 return mNullValue;
255 }
256
257 template <typename TKey, typename TValue>
clear()258 void LruCache<TKey, TValue>::clear() {
259 if (mListener) {
260 for (Entry* p = mOldest; p != NULL; p = p->child) {
261 (*mListener)(p->key, p->value);
262 }
263 }
264 mYoungest = NULL;
265 mOldest = NULL;
266 for (auto entry : *mSet.get()) {
267 delete entry;
268 }
269 mSet->clear();
270 }
271
272 template <typename TKey, typename TValue>
attachToCache(Entry & entry)273 void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
274 if (mYoungest == NULL) {
275 mYoungest = mOldest = &entry;
276 } else {
277 entry.parent = mYoungest;
278 mYoungest->child = &entry;
279 mYoungest = &entry;
280 }
281 }
282
283 template <typename TKey, typename TValue>
detachFromCache(Entry & entry)284 void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
285 if (entry.parent != NULL) {
286 entry.parent->child = entry.child;
287 } else {
288 mOldest = entry.child;
289 }
290 if (entry.child != NULL) {
291 entry.child->parent = entry.parent;
292 } else {
293 mYoungest = entry.parent;
294 }
295
296 entry.parent = NULL;
297 entry.child = NULL;
298 }
299
300 } // namespace android
301 #endif // ANDROID_UTILS_LRU_CACHE_H
302