blob: 89dccd6138d446301646c764d15216b642622179 [file] [log] [blame]
Raph Levienb6ea1752012-10-25 23:11:13 -07001/*
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
Romain Guyb3176ac2012-11-28 12:59:40 -080017#ifndef ANDROID_UTILS_LRU_CACHE_H
18#define ANDROID_UTILS_LRU_CACHE_H
19
Dan Albertf2d25092015-11-05 01:09:22 -080020#include <memory>
Sergio Girobb58cde2015-09-25 18:03:18 +010021#include <unordered_set>
22
Sergio Girobb58cde2015-09-25 18:03:18 +010023#include "utils/TypeHelpers.h" // hash_t
Raph Levienb6ea1752012-10-25 23:11:13 -070024
25namespace android {
26
Mathias Agopian9eb2a3b2013-05-06 20:20:50 -070027/**
28 * GenerationCache callback used when an item is removed
29 */
30template<typename EntryKey, typename EntryValue>
31class OnEntryRemoved {
32public:
33 virtual ~OnEntryRemoved() { };
34 virtual void operator()(EntryKey& key, EntryValue& value) = 0;
35}; // class OnEntryRemoved
Raph Levienb6ea1752012-10-25 23:11:13 -070036
37template <typename TKey, typename TValue>
38class LruCache {
39public:
40 explicit LruCache(uint32_t maxCapacity);
Sergio Girobb58cde2015-09-25 18:03:18 +010041 virtual ~LruCache();
Raph Levienb6ea1752012-10-25 23:11:13 -070042
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();
John Reck9d8707c2014-04-11 19:14:15 -070054 const TValue& peekOldestValue();
Raph Levienb6ea1752012-10-25 23:11:13 -070055
56private:
57 LruCache(const LruCache& that); // disallow copy constructor
58
Sergio Giro4c56e0a2016-06-23 17:19:13 +010059 // Super class so that we can have entries having only a key reference, for searches.
60 class KeyedEntry {
61 public:
62 virtual const TKey& getKey() const = 0;
63 // Make sure the right destructor is executed so that keys and values are deleted.
64 virtual ~KeyedEntry() {}
65 };
66
67 class Entry final : public KeyedEntry {
68 public:
Raph Levienb6ea1752012-10-25 23:11:13 -070069 TKey key;
70 TValue value;
71 Entry* parent;
72 Entry* child;
73
Sergio Giro4c56e0a2016-06-23 17:19:13 +010074 Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
Raph Levienb6ea1752012-10-25 23:11:13 -070075 }
Sergio Giro4c56e0a2016-06-23 17:19:13 +010076 const TKey& getKey() const final { return key; }
Raph Levienb6ea1752012-10-25 23:11:13 -070077 };
78
Sergio Giro4c56e0a2016-06-23 17:19:13 +010079 class EntryForSearch : public KeyedEntry {
80 public:
81 const TKey& key;
82 EntryForSearch(const TKey& key_) : key(key_) {
83 }
84 const TKey& getKey() const final { return key; }
85 };
86
87 struct HashForEntry : public std::unary_function<KeyedEntry*, hash_t> {
88 size_t operator() (const KeyedEntry* entry) const {
89 return hash_type(entry->getKey());
Sergio Girobb58cde2015-09-25 18:03:18 +010090 };
91 };
92
Sergio Giro4c56e0a2016-06-23 17:19:13 +010093 struct EqualityForHashedEntries : public std::unary_function<KeyedEntry*, hash_t> {
94 bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {
95 return lhs->getKey() == rhs->getKey();
Sergio Girobb58cde2015-09-25 18:03:18 +010096 };
97 };
98
Sergio Giro4c56e0a2016-06-23 17:19:13 +010099 // All entries in the set will be Entry*. Using the weaker KeyedEntry as to allow entries
100 // that have only a key reference, for searching.
101 typedef std::unordered_set<KeyedEntry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
Sergio Girobb58cde2015-09-25 18:03:18 +0100102
Raph Levienb6ea1752012-10-25 23:11:13 -0700103 void attachToCache(Entry& entry);
104 void detachFromCache(Entry& entry);
Raph Levienb6ea1752012-10-25 23:11:13 -0700105
Sergio Girobb58cde2015-09-25 18:03:18 +0100106 typename LruCacheSet::iterator findByKey(const TKey& key) {
Sergio Giro4c56e0a2016-06-23 17:19:13 +0100107 EntryForSearch entryForSearch(key);
Sergio Girobb58cde2015-09-25 18:03:18 +0100108 typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
109 return result;
110 }
111
Dan Albertf2d25092015-11-05 01:09:22 -0800112 std::unique_ptr<LruCacheSet> mSet;
Raph Levienb6ea1752012-10-25 23:11:13 -0700113 OnEntryRemoved<TKey, TValue>* mListener;
114 Entry* mOldest;
115 Entry* mYoungest;
116 uint32_t mMaxCapacity;
117 TValue mNullValue;
Sergio Girobb58cde2015-09-25 18:03:18 +0100118
119public:
Sergio Giro0cb59c02015-10-02 17:31:34 +0100120 // To be used like:
121 // while (it.next()) {
122 // it.value(); it.key();
123 // }
Sergio Girobb58cde2015-09-25 18:03:18 +0100124 class Iterator {
125 public:
Sergio Giro0cb59c02015-10-02 17:31:34 +0100126 Iterator(const LruCache<TKey, TValue>& cache):
127 mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100128 }
129
130 bool next() {
131 if (mIterator == mCache.mSet->end()) {
132 return false;
133 }
Sergio Giro0cb59c02015-10-02 17:31:34 +0100134 if (!mBeginReturned) {
135 // mIterator has been initialized to the beginning and
136 // hasn't been returned. Do not advance:
137 mBeginReturned = true;
138 } else {
139 std::advance(mIterator, 1);
140 }
141 bool ret = (mIterator != mCache.mSet->end());
142 return ret;
Sergio Girobb58cde2015-09-25 18:03:18 +0100143 }
144
145 const TValue& value() const {
Sergio Giro4c56e0a2016-06-23 17:19:13 +0100146 // All the elements in the set are of type Entry. See comment in the definition
147 // of LruCacheSet above.
148 return reinterpret_cast<Entry *>(*mIterator)->value;
Sergio Girobb58cde2015-09-25 18:03:18 +0100149 }
150
151 const TKey& key() const {
Sergio Giro4c56e0a2016-06-23 17:19:13 +0100152 return (*mIterator)->getKey();
Sergio Girobb58cde2015-09-25 18:03:18 +0100153 }
154 private:
155 const LruCache<TKey, TValue>& mCache;
156 typename LruCacheSet::iterator mIterator;
Sergio Giro0cb59c02015-10-02 17:31:34 +0100157 bool mBeginReturned;
Sergio Girobb58cde2015-09-25 18:03:18 +0100158 };
Raph Levienb6ea1752012-10-25 23:11:13 -0700159};
160
161// Implementation is here, because it's fully templated
162template <typename TKey, typename TValue>
Mark Salyzyn48878422014-05-22 16:08:52 -0700163LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
Sergio Girobb58cde2015-09-25 18:03:18 +0100164 : mSet(new LruCacheSet())
Mark Salyzyn48878422014-05-22 16:08:52 -0700165 , mListener(NULL)
166 , mOldest(NULL)
167 , mYoungest(NULL)
168 , mMaxCapacity(maxCapacity)
Colin Cross17b5b822016-09-15 18:15:37 -0700169 , mNullValue(0) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100170 mSet->max_load_factor(1.0);
171};
172
173template <typename TKey, typename TValue>
174LruCache<TKey, TValue>::~LruCache() {
175 // Need to delete created entries.
176 clear();
Raph Levienb6ea1752012-10-25 23:11:13 -0700177};
178
179template<typename K, typename V>
180void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
181 mListener = listener;
182}
183
184template <typename TKey, typename TValue>
185size_t LruCache<TKey, TValue>::size() const {
Sergio Girobb58cde2015-09-25 18:03:18 +0100186 return mSet->size();
Raph Levienb6ea1752012-10-25 23:11:13 -0700187}
188
189template <typename TKey, typename TValue>
190const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100191 typename LruCacheSet::const_iterator find_result = findByKey(key);
192 if (find_result == mSet->end()) {
Raph Levienb6ea1752012-10-25 23:11:13 -0700193 return mNullValue;
194 }
Sergio Giro4c56e0a2016-06-23 17:19:13 +0100195 // All the elements in the set are of type Entry. See comment in the definition
196 // of LruCacheSet above.
197 Entry *entry = reinterpret_cast<Entry*>(*find_result);
Sergio Girobb58cde2015-09-25 18:03:18 +0100198 detachFromCache(*entry);
199 attachToCache(*entry);
200 return entry->value;
Raph Levienb6ea1752012-10-25 23:11:13 -0700201}
202
203template <typename TKey, typename TValue>
204bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
205 if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
206 removeOldest();
207 }
208
Sergio Girobb58cde2015-09-25 18:03:18 +0100209 if (findByKey(key) != mSet->end()) {
Raph Levienb6ea1752012-10-25 23:11:13 -0700210 return false;
211 }
Raph Levienb6ea1752012-10-25 23:11:13 -0700212
Sergio Girobb58cde2015-09-25 18:03:18 +0100213 Entry* newEntry = new Entry(key, value);
214 mSet->insert(newEntry);
215 attachToCache(*newEntry);
Raph Levienb6ea1752012-10-25 23:11:13 -0700216 return true;
217}
218
219template <typename TKey, typename TValue>
220bool LruCache<TKey, TValue>::remove(const TKey& key) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100221 typename LruCacheSet::const_iterator find_result = findByKey(key);
222 if (find_result == mSet->end()) {
Raph Levienb6ea1752012-10-25 23:11:13 -0700223 return false;
224 }
Sergio Giro4c56e0a2016-06-23 17:19:13 +0100225 // All the elements in the set are of type Entry. See comment in the definition
226 // of LruCacheSet above.
227 Entry* entry = reinterpret_cast<Entry*>(*find_result);
Sergio Girob7170fe2015-11-17 11:52:05 +0000228 mSet->erase(entry);
Raph Levienb6ea1752012-10-25 23:11:13 -0700229 if (mListener) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100230 (*mListener)(entry->key, entry->value);
Raph Levienb6ea1752012-10-25 23:11:13 -0700231 }
Sergio Girobb58cde2015-09-25 18:03:18 +0100232 detachFromCache(*entry);
Sergio Girobb58cde2015-09-25 18:03:18 +0100233 delete entry;
Raph Levienb6ea1752012-10-25 23:11:13 -0700234 return true;
235}
236
237template <typename TKey, typename TValue>
238bool LruCache<TKey, TValue>::removeOldest() {
239 if (mOldest != NULL) {
240 return remove(mOldest->key);
241 // TODO: should probably abort if false
242 }
243 return false;
244}
245
246template <typename TKey, typename TValue>
John Reck9d8707c2014-04-11 19:14:15 -0700247const TValue& LruCache<TKey, TValue>::peekOldestValue() {
248 if (mOldest) {
249 return mOldest->value;
250 }
251 return mNullValue;
252}
253
254template <typename TKey, typename TValue>
Raph Levienb6ea1752012-10-25 23:11:13 -0700255void LruCache<TKey, TValue>::clear() {
256 if (mListener) {
257 for (Entry* p = mOldest; p != NULL; p = p->child) {
258 (*mListener)(p->key, p->value);
259 }
260 }
261 mYoungest = NULL;
262 mOldest = NULL;
Sergio Girobb58cde2015-09-25 18:03:18 +0100263 for (auto entry : *mSet.get()) {
264 delete entry;
265 }
266 mSet->clear();
Raph Levienb6ea1752012-10-25 23:11:13 -0700267}
268
269template <typename TKey, typename TValue>
270void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
271 if (mYoungest == NULL) {
272 mYoungest = mOldest = &entry;
273 } else {
274 entry.parent = mYoungest;
275 mYoungest->child = &entry;
276 mYoungest = &entry;
277 }
278}
279
280template <typename TKey, typename TValue>
281void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
282 if (entry.parent != NULL) {
283 entry.parent->child = entry.child;
284 } else {
285 mOldest = entry.child;
286 }
287 if (entry.child != NULL) {
288 entry.child->parent = entry.parent;
289 } else {
290 mYoungest = entry.parent;
291 }
292
293 entry.parent = NULL;
294 entry.child = NULL;
295}
296
Raph Levienb6ea1752012-10-25 23:11:13 -0700297}
Romain Guyb3176ac2012-11-28 12:59:40 -0800298#endif // ANDROID_UTILS_LRU_CACHE_H