e6f43ddce7
See https://android-git.corp.google.com/g/#/c/157220 Bug: 5449033 Change-Id: Ic9c19d30693bd56755f55906127cd6bd7126096c
248 lines
6.4 KiB
C++
248 lines
6.4 KiB
C++
/*
|
|
* 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
|