/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UTILS_GENERATION_CACHE_H #define ANDROID_UTILS_GENERATION_CACHE_H #include <utils/KeyedVector.h> #include <utils/RefBase.h> namespace android { /** * GenerationCache callback used when an item is removed */ template<typename EntryKey, typename EntryValue> class OnEntryRemoved { public: virtual ~OnEntryRemoved() { }; virtual void operator()(EntryKey& key, EntryValue& value) = 0; }; // class OnEntryRemoved template<typename EntryKey, typename EntryValue> struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > { Entry(const Entry<EntryKey, EntryValue>& e) : key(e.key), value(e.value), parent(e.parent), child(e.child) { } Entry(const EntryKey& key, const EntryValue& value) : key(key), value(value) { } EntryKey key; EntryValue value; sp<Entry<EntryKey, EntryValue> > parent; // next older entry sp<Entry<EntryKey, EntryValue> > child; // next younger entry }; // struct Entry /** * A LRU type cache */ template<typename K, typename V> class GenerationCache { public: GenerationCache(uint32_t maxCapacity); virtual ~GenerationCache(); enum Capacity { kUnlimitedCapacity, }; void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener); size_t size() const; void clear(); bool contains(const K& key) const; const K& getKeyAt(size_t index) const; const V& getValueAt(size_t index) const; const V& get(const K& key); bool put(const K& key, const V& value); void removeAt(ssize_t index); bool remove(const K& key); bool removeOldest(); private: KeyedVector<K, sp<Entry<K, V> > > mCache; uint32_t mMaxCapacity; OnEntryRemoved<K, V>* mListener; sp<Entry<K, V> > mOldest; sp<Entry<K, V> > mYoungest; void attachToCache(const sp<Entry<K, V> >& entry); void detachFromCache(const sp<Entry<K, V> >& entry); const V mNullValue; }; // class GenerationCache template<typename K, typename V> GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL), mNullValue(NULL) { }; template<typename K, typename V> GenerationCache<K, V>::~GenerationCache() { clear(); }; template<typename K, typename V> uint32_t GenerationCache<K, V>::size() const { return mCache.size(); } /** * Should be set by the user of the Cache so that the callback is called whenever an item is * removed from the cache */ template<typename K, typename V> void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) { mListener = listener; } template<typename K, typename V> void GenerationCache<K, V>::clear() { if (mListener) { for (uint32_t i = 0; i < mCache.size(); i++) { sp<Entry<K, V> > entry = mCache.valueAt(i); if (mListener) { (*mListener)(entry->key, entry->value); } } } mCache.clear(); mYoungest.clear(); mOldest.clear(); } template<typename K, typename V> bool GenerationCache<K, V>::contains(const K& key) const { return mCache.indexOfKey(key) >= 0; } template<typename K, typename V> const K& GenerationCache<K, V>::getKeyAt(size_t index) const { return mCache.keyAt(index); } template<typename K, typename V> const V& GenerationCache<K, V>::getValueAt(size_t index) const { return mCache.valueAt(index)->value; } template<typename K, typename V> const V& GenerationCache<K, V>::get(const K& key) { ssize_t index = mCache.indexOfKey(key); if (index >= 0) { const sp<Entry<K, V> >& entry = mCache.valueAt(index); detachFromCache(entry); attachToCache(entry); return entry->value; } return mNullValue; } template<typename K, typename V> bool GenerationCache<K, V>::put(const K& key, const V& value) { if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { removeOldest(); } ssize_t index = mCache.indexOfKey(key); if (index < 0) { sp<Entry<K, V> > entry = new Entry<K, V>(key, value); mCache.add(key, entry); attachToCache(entry); return true; } return false; } template<typename K, typename V> bool GenerationCache<K, V>::remove(const K& key) { ssize_t index = mCache.indexOfKey(key); if (index >= 0) { removeAt(index); return true; } return false; } template<typename K, typename V> void GenerationCache<K, V>::removeAt(ssize_t index) { sp<Entry<K, V> > entry = mCache.valueAt(index); if (mListener) { (*mListener)(entry->key, entry->value); } mCache.removeItemsAt(index, 1); detachFromCache(entry); } template<typename K, typename V> bool GenerationCache<K, V>::removeOldest() { if (mOldest.get()) { ssize_t index = mCache.indexOfKey(mOldest->key); if (index >= 0) { removeAt(index); return true; } ALOGE("GenerationCache: removeOldest failed to find the item in the cache " "with the given key, but we know it must be in there. " "Is the key comparator kaput?"); } return false; } template<typename K, typename V> void GenerationCache<K, V>::attachToCache(const sp<Entry<K, V> >& entry) { if (!mYoungest.get()) { mYoungest = mOldest = entry; } else { entry->parent = mYoungest; mYoungest->child = entry; mYoungest = entry; } } template<typename K, typename V> void GenerationCache<K, V>::detachFromCache(const sp<Entry<K, V> >& entry) { if (entry->parent.get()) { entry->parent->child = entry->child; } else { mOldest = entry->child; } if (entry->child.get()) { entry->child->parent = entry->parent; } else { mYoungest = entry->parent; } entry->parent.clear(); entry->child.clear(); } }; // namespace android #endif // ANDROID_UTILS_GENERATION_CACHE_H