1*8975f5c5SAndroid Build Coastguard Worker //
2*8975f5c5SAndroid Build Coastguard Worker // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3*8975f5c5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker // found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker //
6*8975f5c5SAndroid Build Coastguard Worker // SizedMRUCache.h: A hashing map that stores blobs of sized, untyped data.
7*8975f5c5SAndroid Build Coastguard Worker
8*8975f5c5SAndroid Build Coastguard Worker #ifndef LIBANGLE_SIZED_MRU_CACHE_H_
9*8975f5c5SAndroid Build Coastguard Worker #define LIBANGLE_SIZED_MRU_CACHE_H_
10*8975f5c5SAndroid Build Coastguard Worker
11*8975f5c5SAndroid Build Coastguard Worker #include <anglebase/containers/mru_cache.h>
12*8975f5c5SAndroid Build Coastguard Worker
13*8975f5c5SAndroid Build Coastguard Worker namespace angle
14*8975f5c5SAndroid Build Coastguard Worker {
15*8975f5c5SAndroid Build Coastguard Worker
16*8975f5c5SAndroid Build Coastguard Worker template <typename Key, typename Value>
17*8975f5c5SAndroid Build Coastguard Worker class SizedMRUCache final : angle::NonCopyable
18*8975f5c5SAndroid Build Coastguard Worker {
19*8975f5c5SAndroid Build Coastguard Worker public:
SizedMRUCache(size_t maximumTotalSize)20*8975f5c5SAndroid Build Coastguard Worker SizedMRUCache(size_t maximumTotalSize)
21*8975f5c5SAndroid Build Coastguard Worker : mMaximumTotalSize(maximumTotalSize),
22*8975f5c5SAndroid Build Coastguard Worker mCurrentSize(0),
23*8975f5c5SAndroid Build Coastguard Worker mStore(SizedMRUCacheStore::NO_AUTO_EVICT)
24*8975f5c5SAndroid Build Coastguard Worker {}
25*8975f5c5SAndroid Build Coastguard Worker
26*8975f5c5SAndroid Build Coastguard Worker // Returns nullptr on failure.
put(const Key & key,Value && value,size_t size)27*8975f5c5SAndroid Build Coastguard Worker const Value *put(const Key &key, Value &&value, size_t size)
28*8975f5c5SAndroid Build Coastguard Worker {
29*8975f5c5SAndroid Build Coastguard Worker if (size > mMaximumTotalSize)
30*8975f5c5SAndroid Build Coastguard Worker {
31*8975f5c5SAndroid Build Coastguard Worker return nullptr;
32*8975f5c5SAndroid Build Coastguard Worker }
33*8975f5c5SAndroid Build Coastguard Worker
34*8975f5c5SAndroid Build Coastguard Worker // Check for existing key.
35*8975f5c5SAndroid Build Coastguard Worker eraseByKey(key);
36*8975f5c5SAndroid Build Coastguard Worker
37*8975f5c5SAndroid Build Coastguard Worker auto retVal = mStore.Put(key, ValueAndSize(std::move(value), size));
38*8975f5c5SAndroid Build Coastguard Worker mCurrentSize += size;
39*8975f5c5SAndroid Build Coastguard Worker
40*8975f5c5SAndroid Build Coastguard Worker shrinkToSize(mMaximumTotalSize);
41*8975f5c5SAndroid Build Coastguard Worker
42*8975f5c5SAndroid Build Coastguard Worker return &retVal->second.value;
43*8975f5c5SAndroid Build Coastguard Worker }
44*8975f5c5SAndroid Build Coastguard Worker
get(const Key & key,const Value ** valueOut)45*8975f5c5SAndroid Build Coastguard Worker bool get(const Key &key, const Value **valueOut)
46*8975f5c5SAndroid Build Coastguard Worker {
47*8975f5c5SAndroid Build Coastguard Worker const auto &iter = mStore.Get(key);
48*8975f5c5SAndroid Build Coastguard Worker if (iter == mStore.end())
49*8975f5c5SAndroid Build Coastguard Worker {
50*8975f5c5SAndroid Build Coastguard Worker return false;
51*8975f5c5SAndroid Build Coastguard Worker }
52*8975f5c5SAndroid Build Coastguard Worker *valueOut = &iter->second.value;
53*8975f5c5SAndroid Build Coastguard Worker return true;
54*8975f5c5SAndroid Build Coastguard Worker }
55*8975f5c5SAndroid Build Coastguard Worker
getAt(size_t index,const Key ** keyOut,const Value ** valueOut)56*8975f5c5SAndroid Build Coastguard Worker bool getAt(size_t index, const Key **keyOut, const Value **valueOut)
57*8975f5c5SAndroid Build Coastguard Worker {
58*8975f5c5SAndroid Build Coastguard Worker if (index < mStore.size())
59*8975f5c5SAndroid Build Coastguard Worker {
60*8975f5c5SAndroid Build Coastguard Worker auto it = mStore.begin();
61*8975f5c5SAndroid Build Coastguard Worker std::advance(it, index);
62*8975f5c5SAndroid Build Coastguard Worker *keyOut = &it->first;
63*8975f5c5SAndroid Build Coastguard Worker *valueOut = &it->second.value;
64*8975f5c5SAndroid Build Coastguard Worker return true;
65*8975f5c5SAndroid Build Coastguard Worker }
66*8975f5c5SAndroid Build Coastguard Worker *valueOut = nullptr;
67*8975f5c5SAndroid Build Coastguard Worker return false;
68*8975f5c5SAndroid Build Coastguard Worker }
69*8975f5c5SAndroid Build Coastguard Worker
empty()70*8975f5c5SAndroid Build Coastguard Worker bool empty() const { return mStore.empty(); }
71*8975f5c5SAndroid Build Coastguard Worker
clear()72*8975f5c5SAndroid Build Coastguard Worker void clear()
73*8975f5c5SAndroid Build Coastguard Worker {
74*8975f5c5SAndroid Build Coastguard Worker mStore.Clear();
75*8975f5c5SAndroid Build Coastguard Worker mCurrentSize = 0;
76*8975f5c5SAndroid Build Coastguard Worker }
77*8975f5c5SAndroid Build Coastguard Worker
eraseByKey(const Key & key)78*8975f5c5SAndroid Build Coastguard Worker void eraseByKey(const Key &key)
79*8975f5c5SAndroid Build Coastguard Worker {
80*8975f5c5SAndroid Build Coastguard Worker // Check for existing key.
81*8975f5c5SAndroid Build Coastguard Worker auto existing = mStore.Peek(key);
82*8975f5c5SAndroid Build Coastguard Worker if (existing != mStore.end())
83*8975f5c5SAndroid Build Coastguard Worker {
84*8975f5c5SAndroid Build Coastguard Worker mCurrentSize -= existing->second.size;
85*8975f5c5SAndroid Build Coastguard Worker mStore.Erase(existing);
86*8975f5c5SAndroid Build Coastguard Worker }
87*8975f5c5SAndroid Build Coastguard Worker }
88*8975f5c5SAndroid Build Coastguard Worker
entryCount()89*8975f5c5SAndroid Build Coastguard Worker size_t entryCount() const { return mStore.size(); }
90*8975f5c5SAndroid Build Coastguard Worker
size()91*8975f5c5SAndroid Build Coastguard Worker size_t size() const { return mCurrentSize; }
92*8975f5c5SAndroid Build Coastguard Worker
93*8975f5c5SAndroid Build Coastguard Worker // Also discards the cache contents.
resize(size_t maximumTotalSize)94*8975f5c5SAndroid Build Coastguard Worker void resize(size_t maximumTotalSize)
95*8975f5c5SAndroid Build Coastguard Worker {
96*8975f5c5SAndroid Build Coastguard Worker clear();
97*8975f5c5SAndroid Build Coastguard Worker mMaximumTotalSize = maximumTotalSize;
98*8975f5c5SAndroid Build Coastguard Worker }
99*8975f5c5SAndroid Build Coastguard Worker
100*8975f5c5SAndroid Build Coastguard Worker // Reduce current memory usage.
shrinkToSize(size_t limit)101*8975f5c5SAndroid Build Coastguard Worker size_t shrinkToSize(size_t limit)
102*8975f5c5SAndroid Build Coastguard Worker {
103*8975f5c5SAndroid Build Coastguard Worker size_t initialSize = mCurrentSize;
104*8975f5c5SAndroid Build Coastguard Worker
105*8975f5c5SAndroid Build Coastguard Worker while (mCurrentSize > limit)
106*8975f5c5SAndroid Build Coastguard Worker {
107*8975f5c5SAndroid Build Coastguard Worker ASSERT(!mStore.empty());
108*8975f5c5SAndroid Build Coastguard Worker auto iter = mStore.rbegin();
109*8975f5c5SAndroid Build Coastguard Worker mCurrentSize -= iter->second.size;
110*8975f5c5SAndroid Build Coastguard Worker mStore.Erase(iter);
111*8975f5c5SAndroid Build Coastguard Worker }
112*8975f5c5SAndroid Build Coastguard Worker
113*8975f5c5SAndroid Build Coastguard Worker return (initialSize - mCurrentSize);
114*8975f5c5SAndroid Build Coastguard Worker }
115*8975f5c5SAndroid Build Coastguard Worker
maxSize()116*8975f5c5SAndroid Build Coastguard Worker size_t maxSize() const { return mMaximumTotalSize; }
117*8975f5c5SAndroid Build Coastguard Worker
118*8975f5c5SAndroid Build Coastguard Worker private:
119*8975f5c5SAndroid Build Coastguard Worker struct ValueAndSize
120*8975f5c5SAndroid Build Coastguard Worker {
ValueAndSizeValueAndSize121*8975f5c5SAndroid Build Coastguard Worker ValueAndSize() : value(), size(0) {}
ValueAndSizeValueAndSize122*8975f5c5SAndroid Build Coastguard Worker ValueAndSize(Value &&value, size_t size) : value(std::move(value)), size(size) {}
ValueAndSizeValueAndSize123*8975f5c5SAndroid Build Coastguard Worker ValueAndSize(ValueAndSize &&other) : ValueAndSize() { *this = std::move(other); }
124*8975f5c5SAndroid Build Coastguard Worker ValueAndSize &operator=(ValueAndSize &&other)
125*8975f5c5SAndroid Build Coastguard Worker {
126*8975f5c5SAndroid Build Coastguard Worker std::swap(value, other.value);
127*8975f5c5SAndroid Build Coastguard Worker std::swap(size, other.size);
128*8975f5c5SAndroid Build Coastguard Worker return *this;
129*8975f5c5SAndroid Build Coastguard Worker }
130*8975f5c5SAndroid Build Coastguard Worker
131*8975f5c5SAndroid Build Coastguard Worker Value value;
132*8975f5c5SAndroid Build Coastguard Worker size_t size;
133*8975f5c5SAndroid Build Coastguard Worker };
134*8975f5c5SAndroid Build Coastguard Worker
135*8975f5c5SAndroid Build Coastguard Worker using SizedMRUCacheStore = base::HashingMRUCache<Key, ValueAndSize>;
136*8975f5c5SAndroid Build Coastguard Worker
137*8975f5c5SAndroid Build Coastguard Worker size_t mMaximumTotalSize;
138*8975f5c5SAndroid Build Coastguard Worker size_t mCurrentSize;
139*8975f5c5SAndroid Build Coastguard Worker SizedMRUCacheStore mStore;
140*8975f5c5SAndroid Build Coastguard Worker };
141*8975f5c5SAndroid Build Coastguard Worker
142*8975f5c5SAndroid Build Coastguard Worker // Helper function used in a few places.
143*8975f5c5SAndroid Build Coastguard Worker template <typename T>
TrimCache(size_t maxStates,size_t gcLimit,const char * name,T * cache)144*8975f5c5SAndroid Build Coastguard Worker void TrimCache(size_t maxStates, size_t gcLimit, const char *name, T *cache)
145*8975f5c5SAndroid Build Coastguard Worker {
146*8975f5c5SAndroid Build Coastguard Worker const size_t kGarbageCollectionLimit = maxStates / 2 + gcLimit;
147*8975f5c5SAndroid Build Coastguard Worker
148*8975f5c5SAndroid Build Coastguard Worker if (cache->size() >= kGarbageCollectionLimit)
149*8975f5c5SAndroid Build Coastguard Worker {
150*8975f5c5SAndroid Build Coastguard Worker WARN() << "Overflowed the " << name << " cache limit of " << (maxStates / 2)
151*8975f5c5SAndroid Build Coastguard Worker << " elements, removing the least recently used to make room.";
152*8975f5c5SAndroid Build Coastguard Worker cache->ShrinkToSize(maxStates / 2);
153*8975f5c5SAndroid Build Coastguard Worker }
154*8975f5c5SAndroid Build Coastguard Worker }
155*8975f5c5SAndroid Build Coastguard Worker } // namespace angle
156*8975f5c5SAndroid Build Coastguard Worker #endif // LIBANGLE_SIZED_MRU_CACHE_H_
157