blob: 0649e8e5a79b7c4d9f2e59d2732bd6b01331cece [file] [log] [blame]
Doug Horna176dc02021-04-06 12:46:39 -07001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Doug Horna176dc02021-04-06 12:46:39 -070017#ifndef ANDROID_BASE_MRUCACHE_
18#define ANDROID_BASE_MRUCACHE_
19
Huan Song07af0f22021-04-30 13:57:52 -070020#include <algorithm>
Doug Horna176dc02021-04-06 12:46:39 -070021#include <list>
22#include <map>
23
24namespace android {
25namespace base {
26
27// A trivial MRU cache. Not thread-safe.
28template <class K, class V>
29class MruCache {
30public:
Doug Horn9f4f4892021-04-14 15:18:46 -070031 template <class S>
32 struct EntryWithSize {
33 EntryWithSize(const S&& d) :
34 EntryWithSize(std::move(d), 0) {}
Doug Horna176dc02021-04-06 12:46:39 -070035
Doug Horn9f4f4892021-04-14 15:18:46 -070036 EntryWithSize(const S&& d, const size_t ds) :
37 data(std::move(d)),
38 dataSize(ds) {
39 static_assert(std::is_same<S, K>::value ||
40 std::is_same<S, V>::value, "Cache entry instantiated with invalid types");
41 }
42
43 const S data;
44 size_t dataSize;
45
46 bool operator==(const EntryWithSize& rhs) const { return data == rhs.data; }
47 bool operator<(const EntryWithSize& rhs) const { return data < rhs.data; }
48
49 };
50
51 class MruCacheObserver {
52 public:
53 virtual void cacheChanged() = 0;
54 virtual ~MruCacheObserver() {}
55 };
56
57 class CacheFlattener {
58 public:
59 virtual void handleFlatten(std::map<EntryWithSize<K>, EntryWithSize<V>>& mCache, void* buf, size_t bufSize) = 0;
60 virtual ~CacheFlattener() {}
61 };
62
63 MruCache(size_t maxEntries, CacheFlattener* cacheFlattener) :
64 mMaxEntries(maxEntries),
65 mCacheFlattener(cacheFlattener) {}
66
67 bool put(const K& key, size_t keySize, V&& value, size_t valueSize) {
Doug Horna176dc02021-04-06 12:46:39 -070068 evictIfNecessary();
Doug Horn9f4f4892021-04-14 15:18:46 -070069
70 EntryWithSize<K> cacheKey(std::move(key), keySize);
71 EntryWithSize<V> cacheValue(std::move(value), valueSize);
72
73 auto exists = mCache.find(cacheKey);
Doug Horna176dc02021-04-06 12:46:39 -070074 if (exists != mCache.end()) {
Doug Horn9f4f4892021-04-14 15:18:46 -070075 auto iter = std::find(mMostRecents.begin(), mMostRecents.end(), cacheKey);
Doug Horna176dc02021-04-06 12:46:39 -070076 mMostRecents.splice(mMostRecents.begin(), mMostRecents, iter);
77 mCache.erase(exists);
Doug Horn9f4f4892021-04-14 15:18:46 -070078 }
79 else {
80 mMostRecents.push_front(cacheKey);
81 }
82
83 const auto [_, res] = mCache.insert({ cacheKey, std::move(cacheValue) });
84
85 if (mCacheObserver != nullptr && res) {
86 mCacheObserver->cacheChanged();
Doug Horna176dc02021-04-06 12:46:39 -070087 }
88
Doug Horn9f4f4892021-04-14 15:18:46 -070089 return res;
90 }
91
92 void flatten(void* buf, size_t bufSize) {
93 if (mCacheFlattener) {
94 mCacheFlattener->handleFlatten(mCache, buf, bufSize);
95 }
Doug Horna176dc02021-04-06 12:46:39 -070096 }
97
98 bool get(const K& key, const V** value) {
Doug Horn9f4f4892021-04-14 15:18:46 -070099 EntryWithSize<K> cacheKey(std::move(key));
100 auto res = mCache.find(cacheKey);
101
Doug Horna176dc02021-04-06 12:46:39 -0700102 if (res == mCache.end()) {
103 return false;
104 }
105
Doug Horn9f4f4892021-04-14 15:18:46 -0700106 *value = &(res->second.data);
107 auto iter = std::find(mMostRecents.begin(), mMostRecents.end(), cacheKey);
Doug Horna176dc02021-04-06 12:46:39 -0700108 mMostRecents.splice(mMostRecents.begin(), mMostRecents, iter);
Doug Horn9f4f4892021-04-14 15:18:46 -0700109
Doug Horna176dc02021-04-06 12:46:39 -0700110 return true;
111 }
112
Doug Horn9f4f4892021-04-14 15:18:46 -0700113 void setObserver(MruCacheObserver* observer) {
114 mCacheObserver = observer;
115 }
116
Doug Horna176dc02021-04-06 12:46:39 -0700117private:
Doug Horn9f4f4892021-04-14 15:18:46 -0700118 using MruCacheMap = std::map<EntryWithSize<K>, EntryWithSize<V>>;
119 using MostRecentList = std::list<EntryWithSize<K>>;
Doug Horna176dc02021-04-06 12:46:39 -0700120
121 void evictIfNecessary() {
122 auto entryCount = mMostRecents.size();
123 if (entryCount >= mMaxEntries) {
124 auto threshold = entryCount * 0.9;
125
126 for (int i = mMostRecents.size(); i > threshold; i--) {
Doug Horn9f4f4892021-04-14 15:18:46 -0700127 EntryWithSize<K> key = mMostRecents.front();
Doug Horna176dc02021-04-06 12:46:39 -0700128 mMostRecents.pop_front();
129 mCache.erase(key);
130 }
131 }
132 }
133
134 MruCacheMap mCache;
135 MostRecentList mMostRecents;
Doug Horn9f4f4892021-04-14 15:18:46 -0700136 MruCacheObserver* mCacheObserver;
137 CacheFlattener* mCacheFlattener;
Doug Horna176dc02021-04-06 12:46:39 -0700138 const size_t mMaxEntries;
139};
140
141}
142}
143
Doug Horn9f4f4892021-04-14 15:18:46 -0700144#endif