• 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 <UniquePtr.h>
21 #include <utils/BasicHashtable.h>
22 
23 namespace android {
24 
25 /**
26  * GenerationCache callback used when an item is removed
27  */
28 template<typename EntryKey, typename EntryValue>
29 class OnEntryRemoved {
30 public:
~OnEntryRemoved()31     virtual ~OnEntryRemoved() { };
32     virtual void operator()(EntryKey& key, EntryValue& value) = 0;
33 }; // class OnEntryRemoved
34 
35 template <typename TKey, typename TValue>
36 class LruCache {
37 public:
38     explicit LruCache(uint32_t maxCapacity);
39 
40     enum Capacity {
41         kUnlimitedCapacity,
42     };
43 
44     void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
45     size_t size() const;
46     const TValue& get(const TKey& key);
47     bool put(const TKey& key, const TValue& value);
48     bool remove(const TKey& key);
49     bool removeOldest();
50     void clear();
51     const TValue& peekOldestValue();
52 
53     class Iterator {
54     public:
Iterator(const LruCache<TKey,TValue> & cache)55         Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) {
56         }
57 
next()58         bool next() {
59             mIndex = mCache.mTable->next(mIndex);
60             return (ssize_t)mIndex != -1;
61         }
62 
index()63         size_t index() const {
64             return mIndex;
65         }
66 
value()67         const TValue& value() const {
68             return mCache.mTable->entryAt(mIndex).value;
69         }
70 
key()71         const TKey& key() const {
72             return mCache.mTable->entryAt(mIndex).key;
73         }
74     private:
75         const LruCache<TKey, TValue>& mCache;
76         size_t mIndex;
77     };
78 
79 private:
80     LruCache(const LruCache& that);  // disallow copy constructor
81 
82     struct Entry {
83         TKey key;
84         TValue value;
85         Entry* parent;
86         Entry* child;
87 
EntryEntry88         Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
89         }
getKeyEntry90         const TKey& getKey() const { return key; }
91     };
92 
93     void attachToCache(Entry& entry);
94     void detachFromCache(Entry& entry);
95     void rehash(size_t newCapacity);
96 
97     UniquePtr<BasicHashtable<TKey, Entry> > mTable;
98     OnEntryRemoved<TKey, TValue>* mListener;
99     Entry* mOldest;
100     Entry* mYoungest;
101     uint32_t mMaxCapacity;
102     TValue mNullValue;
103 };
104 
105 // Implementation is here, because it's fully templated
106 template <typename TKey, typename TValue>
LruCache(uint32_t maxCapacity)107 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
108     : mTable(new BasicHashtable<TKey, Entry>)
109     , mListener(NULL)
110     , mOldest(NULL)
111     , mYoungest(NULL)
112     , mMaxCapacity(maxCapacity)
113     , mNullValue(NULL) {
114 };
115 
116 template<typename K, typename V>
setOnEntryRemovedListener(OnEntryRemoved<K,V> * listener)117 void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
118     mListener = listener;
119 }
120 
121 template <typename TKey, typename TValue>
size()122 size_t LruCache<TKey, TValue>::size() const {
123     return mTable->size();
124 }
125 
126 template <typename TKey, typename TValue>
get(const TKey & key)127 const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
128     hash_t hash = hash_type(key);
129     ssize_t index = mTable->find(-1, hash, key);
130     if (index == -1) {
131         return mNullValue;
132     }
133     Entry& entry = mTable->editEntryAt(index);
134     detachFromCache(entry);
135     attachToCache(entry);
136     return entry.value;
137 }
138 
139 template <typename TKey, typename TValue>
put(const TKey & key,const TValue & value)140 bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
141     if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
142         removeOldest();
143     }
144 
145     hash_t hash = hash_type(key);
146     ssize_t index = mTable->find(-1, hash, key);
147     if (index >= 0) {
148         return false;
149     }
150     if (!mTable->hasMoreRoom()) {
151         rehash(mTable->capacity() * 2);
152     }
153 
154     // Would it be better to initialize a blank entry and assign key, value?
155     Entry initEntry(key, value);
156     index = mTable->add(hash, initEntry);
157     Entry& entry = mTable->editEntryAt(index);
158     attachToCache(entry);
159     return true;
160 }
161 
162 template <typename TKey, typename TValue>
remove(const TKey & key)163 bool LruCache<TKey, TValue>::remove(const TKey& key) {
164     hash_t hash = hash_type(key);
165     ssize_t index = mTable->find(-1, hash, key);
166     if (index < 0) {
167         return false;
168     }
169     Entry& entry = mTable->editEntryAt(index);
170     if (mListener) {
171         (*mListener)(entry.key, entry.value);
172     }
173     detachFromCache(entry);
174     mTable->removeAt(index);
175     return true;
176 }
177 
178 template <typename TKey, typename TValue>
removeOldest()179 bool LruCache<TKey, TValue>::removeOldest() {
180     if (mOldest != NULL) {
181         return remove(mOldest->key);
182         // TODO: should probably abort if false
183     }
184     return false;
185 }
186 
187 template <typename TKey, typename TValue>
peekOldestValue()188 const TValue& LruCache<TKey, TValue>::peekOldestValue() {
189     if (mOldest) {
190         return mOldest->value;
191     }
192     return mNullValue;
193 }
194 
195 template <typename TKey, typename TValue>
clear()196 void LruCache<TKey, TValue>::clear() {
197     if (mListener) {
198         for (Entry* p = mOldest; p != NULL; p = p->child) {
199             (*mListener)(p->key, p->value);
200         }
201     }
202     mYoungest = NULL;
203     mOldest = NULL;
204     mTable->clear();
205 }
206 
207 template <typename TKey, typename TValue>
attachToCache(Entry & entry)208 void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
209     if (mYoungest == NULL) {
210         mYoungest = mOldest = &entry;
211     } else {
212         entry.parent = mYoungest;
213         mYoungest->child = &entry;
214         mYoungest = &entry;
215     }
216 }
217 
218 template <typename TKey, typename TValue>
detachFromCache(Entry & entry)219 void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
220     if (entry.parent != NULL) {
221         entry.parent->child = entry.child;
222     } else {
223         mOldest = entry.child;
224     }
225     if (entry.child != NULL) {
226         entry.child->parent = entry.parent;
227     } else {
228         mYoungest = entry.parent;
229     }
230 
231     entry.parent = NULL;
232     entry.child = NULL;
233 }
234 
235 template <typename TKey, typename TValue>
rehash(size_t newCapacity)236 void LruCache<TKey, TValue>::rehash(size_t newCapacity) {
237     UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release());
238     Entry* oldest = mOldest;
239 
240     mOldest = NULL;
241     mYoungest = NULL;
242     mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity));
243     for (Entry* p = oldest; p != NULL; p = p->child) {
244         put(p->key, p->value);
245     }
246 }
247 
248 }
249 
250 #endif // ANDROID_UTILS_LRU_CACHE_H
251