blob: 20a379eccf21a00da6b6735c70f7af3ba008ec2b [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
Sergio Girobb58cde2015-09-25 18:03:18 +010020#include <unordered_set>
21
Kenny Root0ddfe112013-09-11 23:41:23 -070022#include <UniquePtr.h>
Sergio Girobb58cde2015-09-25 18:03:18 +010023
24#include "utils/TypeHelpers.h" // hash_t
Raph Levienb6ea1752012-10-25 23:11:13 -070025
26namespace android {
27
Mathias Agopian9eb2a3b2013-05-06 20:20:50 -070028/**
29 * GenerationCache callback used when an item is removed
30 */
31template<typename EntryKey, typename EntryValue>
32class OnEntryRemoved {
33public:
34 virtual ~OnEntryRemoved() { };
35 virtual void operator()(EntryKey& key, EntryValue& value) = 0;
36}; // class OnEntryRemoved
Raph Levienb6ea1752012-10-25 23:11:13 -070037
38template <typename TKey, typename TValue>
39class LruCache {
40public:
41 explicit LruCache(uint32_t maxCapacity);
Sergio Girobb58cde2015-09-25 18:03:18 +010042 virtual ~LruCache();
Raph Levienb6ea1752012-10-25 23:11:13 -070043
44 enum Capacity {
45 kUnlimitedCapacity,
46 };
47
48 void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
49 size_t size() const;
50 const TValue& get(const TKey& key);
51 bool put(const TKey& key, const TValue& value);
52 bool remove(const TKey& key);
53 bool removeOldest();
54 void clear();
John Reck9d8707c2014-04-11 19:14:15 -070055 const TValue& peekOldestValue();
Raph Levienb6ea1752012-10-25 23:11:13 -070056
57private:
58 LruCache(const LruCache& that); // disallow copy constructor
59
60 struct Entry {
61 TKey key;
62 TValue value;
63 Entry* parent;
64 Entry* child;
65
66 Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
67 }
68 const TKey& getKey() const { return key; }
69 };
70
Sergio Girobb58cde2015-09-25 18:03:18 +010071 struct HashForEntry : public std::unary_function<Entry*, hash_t> {
72 size_t operator() (const Entry* entry) const {
73 return hash_type(entry->key);
74 };
75 };
76
77 struct EqualityForHashedEntries : public std::unary_function<Entry*, hash_t> {
78 bool operator() (const Entry* lhs, const Entry* rhs) const {
79 return lhs->key == rhs->key;
80 };
81 };
82
83 typedef std::unordered_set<Entry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
84
Raph Levienb6ea1752012-10-25 23:11:13 -070085 void attachToCache(Entry& entry);
86 void detachFromCache(Entry& entry);
Raph Levienb6ea1752012-10-25 23:11:13 -070087
Sergio Girobb58cde2015-09-25 18:03:18 +010088 typename LruCacheSet::iterator findByKey(const TKey& key) {
89 Entry entryForSearch(key, mNullValue);
90 typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
91 return result;
92 }
93
94 UniquePtr<LruCacheSet> mSet;
Raph Levienb6ea1752012-10-25 23:11:13 -070095 OnEntryRemoved<TKey, TValue>* mListener;
96 Entry* mOldest;
97 Entry* mYoungest;
98 uint32_t mMaxCapacity;
99 TValue mNullValue;
Sergio Girobb58cde2015-09-25 18:03:18 +0100100
101public:
Sergio Giro0cb59c02015-10-02 17:31:34 +0100102 // To be used like:
103 // while (it.next()) {
104 // it.value(); it.key();
105 // }
Sergio Girobb58cde2015-09-25 18:03:18 +0100106 class Iterator {
107 public:
Sergio Giro0cb59c02015-10-02 17:31:34 +0100108 Iterator(const LruCache<TKey, TValue>& cache):
109 mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100110 }
111
112 bool next() {
113 if (mIterator == mCache.mSet->end()) {
114 return false;
115 }
Sergio Giro0cb59c02015-10-02 17:31:34 +0100116 if (!mBeginReturned) {
117 // mIterator has been initialized to the beginning and
118 // hasn't been returned. Do not advance:
119 mBeginReturned = true;
120 } else {
121 std::advance(mIterator, 1);
122 }
123 bool ret = (mIterator != mCache.mSet->end());
124 return ret;
Sergio Girobb58cde2015-09-25 18:03:18 +0100125 }
126
127 const TValue& value() const {
128 return (*mIterator)->value;
129 }
130
131 const TKey& key() const {
132 return (*mIterator)->key;
133 }
134 private:
135 const LruCache<TKey, TValue>& mCache;
136 typename LruCacheSet::iterator mIterator;
Sergio Giro0cb59c02015-10-02 17:31:34 +0100137 bool mBeginReturned;
Sergio Girobb58cde2015-09-25 18:03:18 +0100138 };
Raph Levienb6ea1752012-10-25 23:11:13 -0700139};
140
141// Implementation is here, because it's fully templated
142template <typename TKey, typename TValue>
Mark Salyzyn48878422014-05-22 16:08:52 -0700143LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
Sergio Girobb58cde2015-09-25 18:03:18 +0100144 : mSet(new LruCacheSet())
Mark Salyzyn48878422014-05-22 16:08:52 -0700145 , mListener(NULL)
146 , mOldest(NULL)
147 , mYoungest(NULL)
148 , mMaxCapacity(maxCapacity)
149 , mNullValue(NULL) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100150 mSet->max_load_factor(1.0);
151};
152
153template <typename TKey, typename TValue>
154LruCache<TKey, TValue>::~LruCache() {
155 // Need to delete created entries.
156 clear();
Raph Levienb6ea1752012-10-25 23:11:13 -0700157};
158
159template<typename K, typename V>
160void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
161 mListener = listener;
162}
163
164template <typename TKey, typename TValue>
165size_t LruCache<TKey, TValue>::size() const {
Sergio Girobb58cde2015-09-25 18:03:18 +0100166 return mSet->size();
Raph Levienb6ea1752012-10-25 23:11:13 -0700167}
168
169template <typename TKey, typename TValue>
170const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100171 typename LruCacheSet::const_iterator find_result = findByKey(key);
172 if (find_result == mSet->end()) {
Raph Levienb6ea1752012-10-25 23:11:13 -0700173 return mNullValue;
174 }
Sergio Girobb58cde2015-09-25 18:03:18 +0100175 Entry *entry = *find_result;
176 detachFromCache(*entry);
177 attachToCache(*entry);
178 return entry->value;
Raph Levienb6ea1752012-10-25 23:11:13 -0700179}
180
181template <typename TKey, typename TValue>
182bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
183 if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
184 removeOldest();
185 }
186
Sergio Girobb58cde2015-09-25 18:03:18 +0100187 if (findByKey(key) != mSet->end()) {
Raph Levienb6ea1752012-10-25 23:11:13 -0700188 return false;
189 }
Raph Levienb6ea1752012-10-25 23:11:13 -0700190
Sergio Girobb58cde2015-09-25 18:03:18 +0100191 Entry* newEntry = new Entry(key, value);
192 mSet->insert(newEntry);
193 attachToCache(*newEntry);
Raph Levienb6ea1752012-10-25 23:11:13 -0700194 return true;
195}
196
197template <typename TKey, typename TValue>
198bool LruCache<TKey, TValue>::remove(const TKey& key) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100199 typename LruCacheSet::const_iterator find_result = findByKey(key);
200 if (find_result == mSet->end()) {
Raph Levienb6ea1752012-10-25 23:11:13 -0700201 return false;
202 }
Sergio Girobb58cde2015-09-25 18:03:18 +0100203 Entry* entry = *find_result;
Raph Levienb6ea1752012-10-25 23:11:13 -0700204 if (mListener) {
Sergio Girobb58cde2015-09-25 18:03:18 +0100205 (*mListener)(entry->key, entry->value);
Raph Levienb6ea1752012-10-25 23:11:13 -0700206 }
Sergio Girobb58cde2015-09-25 18:03:18 +0100207 detachFromCache(*entry);
208 mSet->erase(entry);
209 delete entry;
Raph Levienb6ea1752012-10-25 23:11:13 -0700210 return true;
211}
212
213template <typename TKey, typename TValue>
214bool LruCache<TKey, TValue>::removeOldest() {
215 if (mOldest != NULL) {
216 return remove(mOldest->key);
217 // TODO: should probably abort if false
218 }
219 return false;
220}
221
222template <typename TKey, typename TValue>
John Reck9d8707c2014-04-11 19:14:15 -0700223const TValue& LruCache<TKey, TValue>::peekOldestValue() {
224 if (mOldest) {
225 return mOldest->value;
226 }
227 return mNullValue;
228}
229
230template <typename TKey, typename TValue>
Raph Levienb6ea1752012-10-25 23:11:13 -0700231void LruCache<TKey, TValue>::clear() {
232 if (mListener) {
233 for (Entry* p = mOldest; p != NULL; p = p->child) {
234 (*mListener)(p->key, p->value);
235 }
236 }
237 mYoungest = NULL;
238 mOldest = NULL;
Sergio Girobb58cde2015-09-25 18:03:18 +0100239 for (auto entry : *mSet.get()) {
240 delete entry;
241 }
242 mSet->clear();
Raph Levienb6ea1752012-10-25 23:11:13 -0700243}
244
245template <typename TKey, typename TValue>
246void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
247 if (mYoungest == NULL) {
248 mYoungest = mOldest = &entry;
249 } else {
250 entry.parent = mYoungest;
251 mYoungest->child = &entry;
252 mYoungest = &entry;
253 }
254}
255
256template <typename TKey, typename TValue>
257void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
258 if (entry.parent != NULL) {
259 entry.parent->child = entry.child;
260 } else {
261 mOldest = entry.child;
262 }
263 if (entry.child != NULL) {
264 entry.child->parent = entry.parent;
265 } else {
266 mYoungest = entry.parent;
267 }
268
269 entry.parent = NULL;
270 entry.child = NULL;
271}
272
Raph Levienb6ea1752012-10-25 23:11:13 -0700273}
Romain Guyb3176ac2012-11-28 12:59:40 -0800274#endif // ANDROID_UTILS_LRU_CACHE_H