am 8e2c8208: am 730fdbb1: Merge "system/core: change LruCache to use unordered_set instead of BasicHashTable"
* commit '8e2c8208198c1c73c7192b1bd5e87868b53297c6': system/core: change LruCache to use unordered_set instead of BasicHashTable
This commit is contained in:
commit
fbb259e836
2 changed files with 83 additions and 72 deletions
|
|
@ -17,8 +17,11 @@
|
||||||
#ifndef ANDROID_UTILS_LRU_CACHE_H
|
#ifndef ANDROID_UTILS_LRU_CACHE_H
|
||||||
#define ANDROID_UTILS_LRU_CACHE_H
|
#define ANDROID_UTILS_LRU_CACHE_H
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <UniquePtr.h>
|
#include <UniquePtr.h>
|
||||||
#include <utils/BasicHashtable.h>
|
|
||||||
|
#include "utils/TypeHelpers.h" // hash_t
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
|
|
@ -36,6 +39,7 @@ template <typename TKey, typename TValue>
|
||||||
class LruCache {
|
class LruCache {
|
||||||
public:
|
public:
|
||||||
explicit LruCache(uint32_t maxCapacity);
|
explicit LruCache(uint32_t maxCapacity);
|
||||||
|
virtual ~LruCache();
|
||||||
|
|
||||||
enum Capacity {
|
enum Capacity {
|
||||||
kUnlimitedCapacity,
|
kUnlimitedCapacity,
|
||||||
|
|
@ -50,32 +54,6 @@ public:
|
||||||
void clear();
|
void clear();
|
||||||
const TValue& peekOldestValue();
|
const TValue& peekOldestValue();
|
||||||
|
|
||||||
class Iterator {
|
|
||||||
public:
|
|
||||||
Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool next() {
|
|
||||||
mIndex = mCache.mTable->next(mIndex);
|
|
||||||
return (ssize_t)mIndex != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index() const {
|
|
||||||
return mIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TValue& value() const {
|
|
||||||
return mCache.mTable->entryAt(mIndex).value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TKey& key() const {
|
|
||||||
return mCache.mTable->entryAt(mIndex).key;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
const LruCache<TKey, TValue>& mCache;
|
|
||||||
size_t mIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LruCache(const LruCache& that); // disallow copy constructor
|
LruCache(const LruCache& that); // disallow copy constructor
|
||||||
|
|
||||||
|
|
@ -90,27 +68,79 @@ private:
|
||||||
const TKey& getKey() const { return key; }
|
const TKey& getKey() const { return key; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HashForEntry : public std::unary_function<Entry*, hash_t> {
|
||||||
|
size_t operator() (const Entry* entry) const {
|
||||||
|
return hash_type(entry->key);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EqualityForHashedEntries : public std::unary_function<Entry*, hash_t> {
|
||||||
|
bool operator() (const Entry* lhs, const Entry* rhs) const {
|
||||||
|
return lhs->key == rhs->key;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unordered_set<Entry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
|
||||||
|
|
||||||
void attachToCache(Entry& entry);
|
void attachToCache(Entry& entry);
|
||||||
void detachFromCache(Entry& entry);
|
void detachFromCache(Entry& entry);
|
||||||
void rehash(size_t newCapacity);
|
|
||||||
|
|
||||||
UniquePtr<BasicHashtable<TKey, Entry> > mTable;
|
typename LruCacheSet::iterator findByKey(const TKey& key) {
|
||||||
|
Entry entryForSearch(key, mNullValue);
|
||||||
|
typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniquePtr<LruCacheSet> mSet;
|
||||||
OnEntryRemoved<TKey, TValue>* mListener;
|
OnEntryRemoved<TKey, TValue>* mListener;
|
||||||
Entry* mOldest;
|
Entry* mOldest;
|
||||||
Entry* mYoungest;
|
Entry* mYoungest;
|
||||||
uint32_t mMaxCapacity;
|
uint32_t mMaxCapacity;
|
||||||
TValue mNullValue;
|
TValue mNullValue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIterator(mCache.mSet->begin()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool next() {
|
||||||
|
if (mIterator == mCache.mSet->end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::advance(mIterator, 1);
|
||||||
|
return mIterator != mCache.mSet->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const TValue& value() const {
|
||||||
|
return (*mIterator)->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TKey& key() const {
|
||||||
|
return (*mIterator)->key;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
const LruCache<TKey, TValue>& mCache;
|
||||||
|
typename LruCacheSet::iterator mIterator;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Implementation is here, because it's fully templated
|
// Implementation is here, because it's fully templated
|
||||||
template <typename TKey, typename TValue>
|
template <typename TKey, typename TValue>
|
||||||
LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
|
LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
|
||||||
: mTable(new BasicHashtable<TKey, Entry>)
|
: mSet(new LruCacheSet())
|
||||||
, mListener(NULL)
|
, mListener(NULL)
|
||||||
, mOldest(NULL)
|
, mOldest(NULL)
|
||||||
, mYoungest(NULL)
|
, mYoungest(NULL)
|
||||||
, mMaxCapacity(maxCapacity)
|
, mMaxCapacity(maxCapacity)
|
||||||
, mNullValue(NULL) {
|
, mNullValue(NULL) {
|
||||||
|
mSet->max_load_factor(1.0);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TKey, typename TValue>
|
||||||
|
LruCache<TKey, TValue>::~LruCache() {
|
||||||
|
// Need to delete created entries.
|
||||||
|
clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename K, typename V>
|
template<typename K, typename V>
|
||||||
|
|
@ -120,20 +150,19 @@ void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
|
||||||
|
|
||||||
template <typename TKey, typename TValue>
|
template <typename TKey, typename TValue>
|
||||||
size_t LruCache<TKey, TValue>::size() const {
|
size_t LruCache<TKey, TValue>::size() const {
|
||||||
return mTable->size();
|
return mSet->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TValue>
|
template <typename TKey, typename TValue>
|
||||||
const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
|
const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
|
||||||
hash_t hash = hash_type(key);
|
typename LruCacheSet::const_iterator find_result = findByKey(key);
|
||||||
ssize_t index = mTable->find(-1, hash, key);
|
if (find_result == mSet->end()) {
|
||||||
if (index == -1) {
|
|
||||||
return mNullValue;
|
return mNullValue;
|
||||||
}
|
}
|
||||||
Entry& entry = mTable->editEntryAt(index);
|
Entry *entry = *find_result;
|
||||||
detachFromCache(entry);
|
detachFromCache(*entry);
|
||||||
attachToCache(entry);
|
attachToCache(*entry);
|
||||||
return entry.value;
|
return entry->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TValue>
|
template <typename TKey, typename TValue>
|
||||||
|
|
@ -142,36 +171,29 @@ bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
|
||||||
removeOldest();
|
removeOldest();
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_t hash = hash_type(key);
|
if (findByKey(key) != mSet->end()) {
|
||||||
ssize_t index = mTable->find(-1, hash, key);
|
|
||||||
if (index >= 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!mTable->hasMoreRoom()) {
|
|
||||||
rehash(mTable->capacity() * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Would it be better to initialize a blank entry and assign key, value?
|
Entry* newEntry = new Entry(key, value);
|
||||||
Entry initEntry(key, value);
|
mSet->insert(newEntry);
|
||||||
index = mTable->add(hash, initEntry);
|
attachToCache(*newEntry);
|
||||||
Entry& entry = mTable->editEntryAt(index);
|
|
||||||
attachToCache(entry);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TValue>
|
template <typename TKey, typename TValue>
|
||||||
bool LruCache<TKey, TValue>::remove(const TKey& key) {
|
bool LruCache<TKey, TValue>::remove(const TKey& key) {
|
||||||
hash_t hash = hash_type(key);
|
typename LruCacheSet::const_iterator find_result = findByKey(key);
|
||||||
ssize_t index = mTable->find(-1, hash, key);
|
if (find_result == mSet->end()) {
|
||||||
if (index < 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Entry& entry = mTable->editEntryAt(index);
|
Entry* entry = *find_result;
|
||||||
if (mListener) {
|
if (mListener) {
|
||||||
(*mListener)(entry.key, entry.value);
|
(*mListener)(entry->key, entry->value);
|
||||||
}
|
}
|
||||||
detachFromCache(entry);
|
detachFromCache(*entry);
|
||||||
mTable->removeAt(index);
|
mSet->erase(entry);
|
||||||
|
delete entry;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,7 +223,10 @@ void LruCache<TKey, TValue>::clear() {
|
||||||
}
|
}
|
||||||
mYoungest = NULL;
|
mYoungest = NULL;
|
||||||
mOldest = NULL;
|
mOldest = NULL;
|
||||||
mTable->clear();
|
for (auto entry : *mSet.get()) {
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
mSet->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TValue>
|
template <typename TKey, typename TValue>
|
||||||
|
|
@ -232,19 +257,5 @@ void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
|
||||||
entry.child = NULL;
|
entry.child = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TValue>
|
|
||||||
void LruCache<TKey, TValue>::rehash(size_t newCapacity) {
|
|
||||||
UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release());
|
|
||||||
Entry* oldest = mOldest;
|
|
||||||
|
|
||||||
mOldest = NULL;
|
|
||||||
mYoungest = NULL;
|
|
||||||
mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity));
|
|
||||||
for (Entry* p = oldest; p != NULL; p = p->child) {
|
|
||||||
put(p->key, p->value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ANDROID_UTILS_LRU_CACHE_H
|
#endif // ANDROID_UTILS_LRU_CACHE_H
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ TEST_F(LruCacheTest, NoLeak) {
|
||||||
cache.put(ComplexKey(0), ComplexValue(0));
|
cache.put(ComplexKey(0), ComplexValue(0));
|
||||||
cache.put(ComplexKey(1), ComplexValue(1));
|
cache.put(ComplexKey(1), ComplexValue(1));
|
||||||
EXPECT_EQ(2U, cache.size());
|
EXPECT_EQ(2U, cache.size());
|
||||||
assertInstanceCount(2, 3); // the null value counts as an instance
|
assertInstanceCount(2, 3); // the member mNullValue counts as an instance
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LruCacheTest, Clear) {
|
TEST_F(LruCacheTest, Clear) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue