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:
Sergio Giro 2015-09-30 13:26:23 +00:00 committed by Android Git Automerger
commit fbb259e836
2 changed files with 83 additions and 72 deletions

View file

@ -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

View file

@ -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) {