• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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.
~KeyedEntry()64         virtual ~KeyedEntry() {}
65     };
66 
67     class Entry final : public KeyedEntry {
68     public:
69         TKey key;
70         TValue value;
71         Entry* parent;
72         Entry* child;
73 
Entry(TKey _key,TValue _value)74         Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(nullptr), child(nullptr) {
75         }
getKey()76         const TKey& getKey() const final { return key; }
77     };
78 
79     class EntryForSearch : public KeyedEntry {
80     public:
81         const TKey& key;
EntryForSearch(const TKey & key_)82         EntryForSearch(const TKey& key_) : key(key_) {
83         }
getKey()84         const TKey& getKey() const final { return key; }
85     };
86 
87     struct HashForEntry : public std::unary_function<KeyedEntry*, hash_t> {
operatorHashForEntry88         size_t operator() (const KeyedEntry* entry) const {
89             return hash_type(entry->getKey());
90         };
91     };
92 
93     struct EqualityForHashedEntries : public std::unary_function<KeyedEntry*, hash_t> {
operatorEqualityForHashedEntries94         bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {
95             return lhs->getKey() == rhs->getKey();
96         };
97     };
98 
99     // 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;
102 
103     void attachToCache(Entry& entry);
104     void detachFromCache(Entry& entry);
105 
findByKey(const TKey & key)106     typename LruCacheSet::iterator findByKey(const TKey& key) {
107         EntryForSearch entryForSearch(key);
108         typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
109         return result;
110     }
111 
112     std::unique_ptr<LruCacheSet> mSet;
113     OnEntryRemoved<TKey, TValue>* mListener;
114     Entry* mOldest;
115     Entry* mYoungest;
116     uint32_t mMaxCapacity;
117     TValue mNullValue;
118 
119 public:
120     // To be used like:
121     // while (it.next()) {
122     //   it.value(); it.key();
123     // }
124     class Iterator {
125     public:
Iterator(const LruCache<TKey,TValue> & cache)126         Iterator(const LruCache<TKey, TValue>& cache):
127                 mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
128         }
129 
next()130         bool next() {
131             if (mIterator == mCache.mSet->end()) {
132                 return false;
133             }
134             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;
143         }
144 
value()145         const TValue& value() const {
146             // 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;
149         }
150 
key()151         const TKey& key() const {
152             return (*mIterator)->getKey();
153         }
154     private:
155         const LruCache<TKey, TValue>& mCache;
156         typename LruCacheSet::iterator mIterator;
157         bool mBeginReturned;
158     };
159 };
160 
161 // Implementation is here, because it's fully templated
162 template <typename TKey, typename TValue>
LruCache(uint32_t maxCapacity)163 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
164     : mSet(new LruCacheSet())
165     , mListener(nullptr)
166     , mOldest(nullptr)
167     , mYoungest(nullptr)
168     , mMaxCapacity(maxCapacity)
169     , mNullValue(0) {
170     mSet->max_load_factor(1.0);
171 };
172 
173 template <typename TKey, typename TValue>
~LruCache()174 LruCache<TKey, TValue>::~LruCache() {
175     // Need to delete created entries.
176     clear();
177 };
178 
179 template<typename K, typename V>
setOnEntryRemovedListener(OnEntryRemoved<K,V> * listener)180 void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
181     mListener = listener;
182 }
183 
184 template <typename TKey, typename TValue>
size()185 size_t LruCache<TKey, TValue>::size() const {
186     return mSet->size();
187 }
188 
189 template <typename TKey, typename TValue>
get(const TKey & key)190 const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
191     typename LruCacheSet::const_iterator find_result = findByKey(key);
192     if (find_result == mSet->end()) {
193         return mNullValue;
194     }
195     // 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);
198     detachFromCache(*entry);
199     attachToCache(*entry);
200     return entry->value;
201 }
202 
203 template <typename TKey, typename TValue>
put(const TKey & key,const TValue & value)204 bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
205     if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
206         removeOldest();
207     }
208 
209     if (findByKey(key) != mSet->end()) {
210         return false;
211     }
212 
213     Entry* newEntry = new Entry(key, value);
214     mSet->insert(newEntry);
215     attachToCache(*newEntry);
216     return true;
217 }
218 
219 template <typename TKey, typename TValue>
remove(const TKey & key)220 bool LruCache<TKey, TValue>::remove(const TKey& key) {
221     typename LruCacheSet::const_iterator find_result = findByKey(key);
222     if (find_result == mSet->end()) {
223         return false;
224     }
225     // 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);
228     mSet->erase(entry);
229     if (mListener) {
230         (*mListener)(entry->key, entry->value);
231     }
232     detachFromCache(*entry);
233     delete entry;
234     return true;
235 }
236 
237 template <typename TKey, typename TValue>
removeOldest()238 bool LruCache<TKey, TValue>::removeOldest() {
239     if (mOldest != nullptr) {
240         return remove(mOldest->key);
241         // TODO: should probably abort if false
242     }
243     return false;
244 }
245 
246 template <typename TKey, typename TValue>
peekOldestValue()247 const TValue& LruCache<TKey, TValue>::peekOldestValue() {
248     if (mOldest) {
249         return mOldest->value;
250     }
251     return mNullValue;
252 }
253 
254 template <typename TKey, typename TValue>
clear()255 void LruCache<TKey, TValue>::clear() {
256     if (mListener) {
257         for (Entry* p = mOldest; p != nullptr; p = p->child) {
258             (*mListener)(p->key, p->value);
259         }
260     }
261     mYoungest = nullptr;
262     mOldest = nullptr;
263     for (auto entry : *mSet.get()) {
264         delete entry;
265     }
266     mSet->clear();
267 }
268 
269 template <typename TKey, typename TValue>
attachToCache(Entry & entry)270 void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
271     if (mYoungest == nullptr) {
272         mYoungest = mOldest = &entry;
273     } else {
274         entry.parent = mYoungest;
275         mYoungest->child = &entry;
276         mYoungest = &entry;
277     }
278 }
279 
280 template <typename TKey, typename TValue>
detachFromCache(Entry & entry)281 void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
282     if (entry.parent != nullptr) {
283         entry.parent->child = entry.child;
284     } else {
285         mOldest = entry.child;
286     }
287     if (entry.child != nullptr) {
288         entry.child->parent = entry.parent;
289     } else {
290         mYoungest = entry.parent;
291     }
292 
293     entry.parent = nullptr;
294     entry.child = nullptr;
295 }
296 
297 }
298 #endif // ANDROID_UTILS_LRU_CACHE_H
299