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     struct Entry {
     60         TKey key;
     61         TValue value;
     62         Entry* parent;
     63         Entry* child;
     64 
     65         Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
     66         }
     67         const TKey& getKey() const { return key; }
     68     };
     69 
     70     struct HashForEntry : public std::unary_function<Entry*, hash_t> {
     71         size_t operator() (const Entry* entry) const {
     72             return hash_type(entry->key);
     73         };
     74     };
     75 
     76     struct EqualityForHashedEntries : public std::unary_function<Entry*, hash_t> {
     77         bool operator() (const Entry* lhs, const Entry* rhs) const {
     78             return lhs->key == rhs->key;
     79         };
     80     };
     81 
     82     typedef std::unordered_set<Entry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
     83 
     84     void attachToCache(Entry& entry);
     85     void detachFromCache(Entry& entry);
     86 
     87     typename LruCacheSet::iterator findByKey(const TKey& key) {
     88         Entry entryForSearch(key, mNullValue);
     89         typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
     90         return result;
     91     }
     92 
     93     std::unique_ptr<LruCacheSet> mSet;
     94     OnEntryRemoved<TKey, TValue>* mListener;
     95     Entry* mOldest;
     96     Entry* mYoungest;
     97     uint32_t mMaxCapacity;
     98     TValue mNullValue;
     99 
    100 public:
    101     // To be used like:
    102     // while (it.next()) {
    103     //   it.value(); it.key();
    104     // }
    105     class Iterator {
    106     public:
    107         Iterator(const LruCache<TKey, TValue>& cache):
    108                 mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
    109         }
    110 
    111         bool next() {
    112             if (mIterator == mCache.mSet->end()) {
    113                 return false;
    114             }
    115             if (!mBeginReturned) {
    116                 // mIterator has been initialized to the beginning and
    117                 // hasn't been returned. Do not advance:
    118                 mBeginReturned = true;
    119             } else {
    120                 std::advance(mIterator, 1);
    121             }
    122             bool ret = (mIterator != mCache.mSet->end());
    123             return ret;
    124         }
    125 
    126         const TValue& value() const {
    127             return (*mIterator)->value;
    128         }
    129 
    130         const TKey& key() const {
    131             return (*mIterator)->key;
    132         }
    133     private:
    134         const LruCache<TKey, TValue>& mCache;
    135         typename LruCacheSet::iterator mIterator;
    136         bool mBeginReturned;
    137     };
    138 };
    139 
    140 // Implementation is here, because it's fully templated
    141 template <typename TKey, typename TValue>
    142 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
    143     : mSet(new LruCacheSet())
    144     , mListener(NULL)
    145     , mOldest(NULL)
    146     , mYoungest(NULL)
    147     , mMaxCapacity(maxCapacity)
    148     , mNullValue(NULL) {
    149     mSet->max_load_factor(1.0);
    150 };
    151 
    152 template <typename TKey, typename TValue>
    153 LruCache<TKey, TValue>::~LruCache() {
    154     // Need to delete created entries.
    155     clear();
    156 };
    157 
    158 template<typename K, typename V>
    159 void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
    160     mListener = listener;
    161 }
    162 
    163 template <typename TKey, typename TValue>
    164 size_t LruCache<TKey, TValue>::size() const {
    165     return mSet->size();
    166 }
    167 
    168 template <typename TKey, typename TValue>
    169 const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
    170     typename LruCacheSet::const_iterator find_result = findByKey(key);
    171     if (find_result == mSet->end()) {
    172         return mNullValue;
    173     }
    174     Entry *entry = *find_result;
    175     detachFromCache(*entry);
    176     attachToCache(*entry);
    177     return entry->value;
    178 }
    179 
    180 template <typename TKey, typename TValue>
    181 bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
    182     if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
    183         removeOldest();
    184     }
    185 
    186     if (findByKey(key) != mSet->end()) {
    187         return false;
    188     }
    189 
    190     Entry* newEntry = new Entry(key, value);
    191     mSet->insert(newEntry);
    192     attachToCache(*newEntry);
    193     return true;
    194 }
    195 
    196 template <typename TKey, typename TValue>
    197 bool LruCache<TKey, TValue>::remove(const TKey& key) {
    198     typename LruCacheSet::const_iterator find_result = findByKey(key);
    199     if (find_result == mSet->end()) {
    200         return false;
    201     }
    202     Entry* entry = *find_result;
    203     mSet->erase(entry);
    204     if (mListener) {
    205         (*mListener)(entry->key, entry->value);
    206     }
    207     detachFromCache(*entry);
    208     delete entry;
    209     return true;
    210 }
    211 
    212 template <typename TKey, typename TValue>
    213 bool LruCache<TKey, TValue>::removeOldest() {
    214     if (mOldest != NULL) {
    215         return remove(mOldest->key);
    216         // TODO: should probably abort if false
    217     }
    218     return false;
    219 }
    220 
    221 template <typename TKey, typename TValue>
    222 const TValue& LruCache<TKey, TValue>::peekOldestValue() {
    223     if (mOldest) {
    224         return mOldest->value;
    225     }
    226     return mNullValue;
    227 }
    228 
    229 template <typename TKey, typename TValue>
    230 void LruCache<TKey, TValue>::clear() {
    231     if (mListener) {
    232         for (Entry* p = mOldest; p != NULL; p = p->child) {
    233             (*mListener)(p->key, p->value);
    234         }
    235     }
    236     mYoungest = NULL;
    237     mOldest = NULL;
    238     for (auto entry : *mSet.get()) {
    239         delete entry;
    240     }
    241     mSet->clear();
    242 }
    243 
    244 template <typename TKey, typename TValue>
    245 void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
    246     if (mYoungest == NULL) {
    247         mYoungest = mOldest = &entry;
    248     } else {
    249         entry.parent = mYoungest;
    250         mYoungest->child = &entry;
    251         mYoungest = &entry;
    252     }
    253 }
    254 
    255 template <typename TKey, typename TValue>
    256 void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
    257     if (entry.parent != NULL) {
    258         entry.parent->child = entry.child;
    259     } else {
    260         mOldest = entry.child;
    261     }
    262     if (entry.child != NULL) {
    263         entry.child->parent = entry.parent;
    264     } else {
    265         mYoungest = entry.parent;
    266     }
    267 
    268     entry.parent = NULL;
    269     entry.child = NULL;
    270 }
    271 
    272 }
    273 #endif // ANDROID_UTILS_LRU_CACHE_H
    274