Home | History | Annotate | Download | only in utils
      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:
     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.
     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 
     74         Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
     75         }
     76         const TKey& getKey() const final { return key; }
     77     };
     78 
     79     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());
     90         };
     91     };
     92 
     93     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();
     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 
    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:
    126         Iterator(const LruCache<TKey, TValue>& cache):
    127                 mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
    128         }
    129 
    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 
    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 
    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>
    163 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
    164     : mSet(new LruCacheSet())
    165     , mListener(NULL)
    166     , mOldest(NULL)
    167     , mYoungest(NULL)
    168     , mMaxCapacity(maxCapacity)
    169     , mNullValue(0) {
    170     mSet->max_load_factor(1.0);
    171 };
    172 
    173 template <typename TKey, typename TValue>
    174 LruCache<TKey, TValue>::~LruCache() {
    175     // Need to delete created entries.
    176     clear();
    177 };
    178 
    179 template<typename K, typename V>
    180 void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
    181     mListener = listener;
    182 }
    183 
    184 template <typename TKey, typename TValue>
    185 size_t LruCache<TKey, TValue>::size() const {
    186     return mSet->size();
    187 }
    188 
    189 template <typename TKey, typename TValue>
    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>
    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>
    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>
    238 bool 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 
    246 template <typename TKey, typename TValue>
    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>
    255 void 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;
    263     for (auto entry : *mSet.get()) {
    264         delete entry;
    265     }
    266     mSet->clear();
    267 }
    268 
    269 template <typename TKey, typename TValue>
    270 void 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 
    280 template <typename TKey, typename TValue>
    281 void 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 
    297 }
    298 #endif // ANDROID_UTILS_LRU_CACHE_H
    299