Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright (C) 2010 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_GENERATION_CACHE_H
     18 #define ANDROID_UTILS_GENERATION_CACHE_H
     19 
     20 #include <utils/KeyedVector.h>
     21 #include <utils/RefBase.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:
     31     virtual ~OnEntryRemoved() { };
     32     virtual void operator()(EntryKey& key, EntryValue& value) = 0;
     33 }; // class OnEntryRemoved
     34 
     35 template<typename EntryKey, typename EntryValue>
     36 struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > {
     37     Entry(const Entry<EntryKey, EntryValue>& e) :
     38             key(e.key), value(e.value),
     39             parent(e.parent), child(e.child) { }
     40     Entry(const EntryKey& key, const EntryValue& value) :
     41             key(key), value(value) { }
     42 
     43     EntryKey key;
     44     EntryValue value;
     45 
     46     sp<Entry<EntryKey, EntryValue> > parent; // next older entry
     47     sp<Entry<EntryKey, EntryValue> > child;  // next younger entry
     48 }; // struct Entry
     49 
     50 /**
     51  * A LRU type cache
     52  */
     53 template<typename K, typename V>
     54 class GenerationCache {
     55 public:
     56     GenerationCache(uint32_t maxCapacity);
     57     virtual ~GenerationCache();
     58 
     59     enum Capacity {
     60         kUnlimitedCapacity,
     61     };
     62 
     63     void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
     64 
     65     size_t size() const;
     66 
     67     void clear();
     68 
     69     bool contains(const K& key) const;
     70     const K& getKeyAt(size_t index) const;
     71     const V& getValueAt(size_t index) const;
     72 
     73     const V& get(const K& key);
     74     bool put(const K& key, const V& value);
     75 
     76     void removeAt(ssize_t index);
     77     bool remove(const K& key);
     78     bool removeOldest();
     79 
     80 private:
     81     KeyedVector<K, sp<Entry<K, V> > > mCache;
     82     uint32_t mMaxCapacity;
     83 
     84     OnEntryRemoved<K, V>* mListener;
     85 
     86     sp<Entry<K, V> > mOldest;
     87     sp<Entry<K, V> > mYoungest;
     88 
     89     void attachToCache(const sp<Entry<K, V> >& entry);
     90     void detachFromCache(const sp<Entry<K, V> >& entry);
     91 
     92     const V mNullValue;
     93 }; // class GenerationCache
     94 
     95 template<typename K, typename V>
     96 GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity),
     97     mListener(NULL), mNullValue(NULL) {
     98 };
     99 
    100 template<typename K, typename V>
    101 GenerationCache<K, V>::~GenerationCache() {
    102     clear();
    103 };
    104 
    105 template<typename K, typename V>
    106 uint32_t GenerationCache<K, V>::size() const {
    107     return mCache.size();
    108 }
    109 
    110 /**
    111  * Should be set by the user of the Cache so that the callback is called whenever an item is
    112  * removed from the cache
    113  */
    114 template<typename K, typename V>
    115 void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
    116     mListener = listener;
    117 }
    118 
    119 template<typename K, typename V>
    120 void GenerationCache<K, V>::clear() {
    121     if (mListener) {
    122         for (uint32_t i = 0; i < mCache.size(); i++) {
    123             sp<Entry<K, V> > entry = mCache.valueAt(i);
    124             if (mListener) {
    125                 (*mListener)(entry->key, entry->value);
    126             }
    127         }
    128     }
    129     mCache.clear();
    130     mYoungest.clear();
    131     mOldest.clear();
    132 }
    133 
    134 template<typename K, typename V>
    135 bool GenerationCache<K, V>::contains(const K& key) const {
    136     return mCache.indexOfKey(key) >= 0;
    137 }
    138 
    139 template<typename K, typename V>
    140 const K& GenerationCache<K, V>::getKeyAt(size_t index) const {
    141     return mCache.keyAt(index);
    142 }
    143 
    144 template<typename K, typename V>
    145 const V& GenerationCache<K, V>::getValueAt(size_t index) const {
    146     return mCache.valueAt(index)->value;
    147 }
    148 
    149 template<typename K, typename V>
    150 const V& GenerationCache<K, V>::get(const K& key) {
    151     ssize_t index = mCache.indexOfKey(key);
    152     if (index >= 0) {
    153         const sp<Entry<K, V> >& entry = mCache.valueAt(index);
    154         detachFromCache(entry);
    155         attachToCache(entry);
    156         return entry->value;
    157     }
    158 
    159     return mNullValue;
    160 }
    161 
    162 template<typename K, typename V>
    163 bool GenerationCache<K, V>::put(const K& key, const V& value) {
    164     if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) {
    165         removeOldest();
    166     }
    167 
    168     ssize_t index = mCache.indexOfKey(key);
    169     if (index < 0) {
    170         sp<Entry<K, V> > entry = new Entry<K, V>(key, value);
    171         mCache.add(key, entry);
    172         attachToCache(entry);
    173         return true;
    174     }
    175 
    176     return false;
    177 }
    178 
    179 template<typename K, typename V>
    180 bool GenerationCache<K, V>::remove(const K& key) {
    181     ssize_t index = mCache.indexOfKey(key);
    182     if (index >= 0) {
    183         removeAt(index);
    184         return true;
    185     }
    186 
    187     return false;
    188 }
    189 
    190 template<typename K, typename V>
    191 void GenerationCache<K, V>::removeAt(ssize_t index) {
    192     sp<Entry<K, V> > entry = mCache.valueAt(index);
    193     if (mListener) {
    194         (*mListener)(entry->key, entry->value);
    195     }
    196     mCache.removeItemsAt(index, 1);
    197     detachFromCache(entry);
    198 }
    199 
    200 template<typename K, typename V>
    201 bool GenerationCache<K, V>::removeOldest() {
    202     if (mOldest.get()) {
    203         ssize_t index = mCache.indexOfKey(mOldest->key);
    204         if (index >= 0) {
    205             removeAt(index);
    206             return true;
    207         }
    208         ALOGE("GenerationCache: removeOldest failed to find the item in the cache "
    209                 "with the given key, but we know it must be in there.  "
    210                 "Is the key comparator kaput?");
    211     }
    212 
    213     return false;
    214 }
    215 
    216 template<typename K, typename V>
    217 void GenerationCache<K, V>::attachToCache(const sp<Entry<K, V> >& entry) {
    218     if (!mYoungest.get()) {
    219         mYoungest = mOldest = entry;
    220     } else {
    221         entry->parent = mYoungest;
    222         mYoungest->child = entry;
    223         mYoungest = entry;
    224     }
    225 }
    226 
    227 template<typename K, typename V>
    228 void GenerationCache<K, V>::detachFromCache(const sp<Entry<K, V> >& entry) {
    229     if (entry->parent.get()) {
    230         entry->parent->child = entry->child;
    231     } else {
    232         mOldest = entry->child;
    233     }
    234 
    235     if (entry->child.get()) {
    236         entry->child->parent = entry->parent;
    237     } else {
    238         mYoungest = entry->parent;
    239     }
    240 
    241     entry->parent.clear();
    242     entry->child.clear();
    243 }
    244 
    245 }; // namespace android
    246 
    247 #endif // ANDROID_UTILS_GENERATION_CACHE_H
    248