157 lines
4.0 KiB
C++
157 lines
4.0 KiB
C++
//
|
|
// Copyright 2017 The ANGLE Project Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
//
|
|
// SizedMRUCache.h: A hashing map that stores blobs of sized, untyped data.
|
|
|
|
#ifndef LIBANGLE_SIZED_MRU_CACHE_H_
|
|
#define LIBANGLE_SIZED_MRU_CACHE_H_
|
|
|
|
#include <anglebase/containers/mru_cache.h>
|
|
|
|
namespace angle
|
|
{
|
|
|
|
template <typename Key, typename Value>
|
|
class SizedMRUCache final : angle::NonCopyable
|
|
{
|
|
public:
|
|
SizedMRUCache(size_t maximumTotalSize)
|
|
: mMaximumTotalSize(maximumTotalSize),
|
|
mCurrentSize(0),
|
|
mStore(SizedMRUCacheStore::NO_AUTO_EVICT)
|
|
{}
|
|
|
|
// Returns nullptr on failure.
|
|
const Value *put(const Key &key, Value &&value, size_t size)
|
|
{
|
|
if (size > mMaximumTotalSize)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// Check for existing key.
|
|
eraseByKey(key);
|
|
|
|
auto retVal = mStore.Put(key, ValueAndSize(std::move(value), size));
|
|
mCurrentSize += size;
|
|
|
|
shrinkToSize(mMaximumTotalSize);
|
|
|
|
return &retVal->second.value;
|
|
}
|
|
|
|
bool get(const Key &key, const Value **valueOut)
|
|
{
|
|
const auto &iter = mStore.Get(key);
|
|
if (iter == mStore.end())
|
|
{
|
|
return false;
|
|
}
|
|
*valueOut = &iter->second.value;
|
|
return true;
|
|
}
|
|
|
|
bool getAt(size_t index, const Key **keyOut, const Value **valueOut)
|
|
{
|
|
if (index < mStore.size())
|
|
{
|
|
auto it = mStore.begin();
|
|
std::advance(it, index);
|
|
*keyOut = &it->first;
|
|
*valueOut = &it->second.value;
|
|
return true;
|
|
}
|
|
*valueOut = nullptr;
|
|
return false;
|
|
}
|
|
|
|
bool empty() const { return mStore.empty(); }
|
|
|
|
void clear()
|
|
{
|
|
mStore.Clear();
|
|
mCurrentSize = 0;
|
|
}
|
|
|
|
void eraseByKey(const Key &key)
|
|
{
|
|
// Check for existing key.
|
|
auto existing = mStore.Peek(key);
|
|
if (existing != mStore.end())
|
|
{
|
|
mCurrentSize -= existing->second.size;
|
|
mStore.Erase(existing);
|
|
}
|
|
}
|
|
|
|
size_t entryCount() const { return mStore.size(); }
|
|
|
|
size_t size() const { return mCurrentSize; }
|
|
|
|
// Also discards the cache contents.
|
|
void resize(size_t maximumTotalSize)
|
|
{
|
|
clear();
|
|
mMaximumTotalSize = maximumTotalSize;
|
|
}
|
|
|
|
// Reduce current memory usage.
|
|
size_t shrinkToSize(size_t limit)
|
|
{
|
|
size_t initialSize = mCurrentSize;
|
|
|
|
while (mCurrentSize > limit)
|
|
{
|
|
ASSERT(!mStore.empty());
|
|
auto iter = mStore.rbegin();
|
|
mCurrentSize -= iter->second.size;
|
|
mStore.Erase(iter);
|
|
}
|
|
|
|
return (initialSize - mCurrentSize);
|
|
}
|
|
|
|
size_t maxSize() const { return mMaximumTotalSize; }
|
|
|
|
private:
|
|
struct ValueAndSize
|
|
{
|
|
ValueAndSize() : value(), size(0) {}
|
|
ValueAndSize(Value &&value, size_t size) : value(std::move(value)), size(size) {}
|
|
ValueAndSize(ValueAndSize &&other) : ValueAndSize() { *this = std::move(other); }
|
|
ValueAndSize &operator=(ValueAndSize &&other)
|
|
{
|
|
std::swap(value, other.value);
|
|
std::swap(size, other.size);
|
|
return *this;
|
|
}
|
|
|
|
Value value;
|
|
size_t size;
|
|
};
|
|
|
|
using SizedMRUCacheStore = base::HashingMRUCache<Key, ValueAndSize>;
|
|
|
|
size_t mMaximumTotalSize;
|
|
size_t mCurrentSize;
|
|
SizedMRUCacheStore mStore;
|
|
};
|
|
|
|
// Helper function used in a few places.
|
|
template <typename T>
|
|
void TrimCache(size_t maxStates, size_t gcLimit, const char *name, T *cache)
|
|
{
|
|
const size_t kGarbageCollectionLimit = maxStates / 2 + gcLimit;
|
|
|
|
if (cache->size() >= kGarbageCollectionLimit)
|
|
{
|
|
WARN() << "Overflowed the " << name << " cache limit of " << (maxStates / 2)
|
|
<< " elements, removing the least recently used to make room.";
|
|
cache->ShrinkToSize(maxStates / 2);
|
|
}
|
|
}
|
|
} // namespace angle
|
|
#endif // LIBANGLE_SIZED_MRU_CACHE_H_
|