blob: 36f8707dcccc17ad31ea218b495ee44ee07700cd [file] [log] [blame]
Chen Chenc530a0c2020-02-20 11:29:42 -08001/******************************************************************************
2 *
3 * Copyright 2020 Google, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19#pragma once
20
21#include <functional>
22#include <iterator>
23#include <list>
24#include <mutex>
Jack He106eaca2020-06-17 02:15:23 -070025#include <optional>
Chen Chenc530a0c2020-02-20 11:29:42 -080026#include <thread>
27#include <unordered_map>
28
29#include <base/logging.h>
30
31namespace bluetooth {
32
33namespace common {
34
35template <typename K, typename V>
36class LruCache {
37 public:
38 using Node = std::pair<K, V>;
Chen Chenc530a0c2020-02-20 11:29:42 -080039 /**
40 * Constructor of the cache
41 *
42 * @param capacity maximum size of the cache
43 * @param log_tag, keyword to put at the head of log.
Chen Chenc530a0c2020-02-20 11:29:42 -080044 */
Jack He106eaca2020-06-17 02:15:23 -070045 LruCache(const size_t& capacity, const std::string& log_tag)
46 : capacity_(capacity) {
Chen Chenc530a0c2020-02-20 11:29:42 -080047 if (capacity_ == 0) {
48 // don't allow invalid capacity
49 LOG(FATAL) << log_tag << " unable to have 0 LRU Cache capacity";
50 }
51 }
52
Jack He106eaca2020-06-17 02:15:23 -070053 // delete copy constructor
54 LruCache(LruCache const&) = delete;
55 LruCache& operator=(LruCache const&) = delete;
56
Chen Chenc530a0c2020-02-20 11:29:42 -080057 ~LruCache() { Clear(); }
58
59 /**
60 * Clear the cache
61 */
62 void Clear() {
Jack He106eaca2020-06-17 02:15:23 -070063 std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
Chen Chenc530a0c2020-02-20 11:29:42 -080064 lru_map_.clear();
65 node_list_.clear();
66 }
67
68 /**
Jack He106eaca2020-06-17 02:15:23 -070069 * Same as Get, but return an iterator to the accessed element
70 *
71 * Modifying the returned iterator does not warm up the cache
72 *
73 * @param key
74 * @return pointer to the underlying value to allow in-place modification
75 * nullptr when not found, will be invalidated when the key is evicted
76 */
77 V* Find(const K& key) {
78 std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
79 auto map_iterator = lru_map_.find(key);
80 if (map_iterator == lru_map_.end()) {
81 return nullptr;
82 }
83 node_list_.splice(node_list_.begin(), node_list_, map_iterator->second);
84 return &(map_iterator->second->second);
85 }
86
87 /**
Chen Chenc530a0c2020-02-20 11:29:42 -080088 * Get the value of a key, and move the key to the head of cache, if there is
89 * one
90 *
91 * @param key
92 * @param value, output parameter of value of the key
93 * @return true if the cache has the key
94 */
95 bool Get(const K& key, V* value) {
Jack He106eaca2020-06-17 02:15:23 -070096 CHECK(value != nullptr);
97 std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
98 auto value_ptr = Find(key);
99 if (value_ptr == nullptr) {
Chen Chenc530a0c2020-02-20 11:29:42 -0800100 return false;
101 }
Jack He106eaca2020-06-17 02:15:23 -0700102 *value = *value_ptr;
Chen Chenc530a0c2020-02-20 11:29:42 -0800103 return true;
104 }
105
106 /**
107 * Check if the cache has the input key, move the key to the head
108 * if there is one
109 *
110 * @param key
111 * @return true if the cache has the key
112 */
113 bool HasKey(const K& key) {
Jack He106eaca2020-06-17 02:15:23 -0700114 std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
115 return Find(key) != nullptr;
Chen Chenc530a0c2020-02-20 11:29:42 -0800116 }
117
118 /**
119 * Put a key-value pair to the head of cache
120 *
121 * @param key
122 * @param value
Jack He106eaca2020-06-17 02:15:23 -0700123 * @return evicted node if tail value is popped, std::nullopt if no value
124 * is popped. std::optional can be treated as a boolean as well
Chen Chenc530a0c2020-02-20 11:29:42 -0800125 */
Jack He106eaca2020-06-17 02:15:23 -0700126 std::optional<Node> Put(const K& key, V value) {
127 std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
128 auto value_ptr = Find(key);
129 if (value_ptr != nullptr) {
Chen Chenc530a0c2020-02-20 11:29:42 -0800130 // hasKey() calls get(), therefore already move the node to the head
Jack He106eaca2020-06-17 02:15:23 -0700131 *value_ptr = std::move(value);
132 return std::nullopt;
Chen Chenc530a0c2020-02-20 11:29:42 -0800133 }
134
Chen Chenc530a0c2020-02-20 11:29:42 -0800135 // remove tail
Jack He106eaca2020-06-17 02:15:23 -0700136 std::optional<Node> ret = std::nullopt;
Chen Chenc530a0c2020-02-20 11:29:42 -0800137 if (lru_map_.size() == capacity_) {
138 lru_map_.erase(node_list_.back().first);
Jack He106eaca2020-06-17 02:15:23 -0700139 ret = std::move(node_list_.back());
Chen Chenc530a0c2020-02-20 11:29:42 -0800140 node_list_.pop_back();
Chen Chenc530a0c2020-02-20 11:29:42 -0800141 }
142 // insert to dummy next;
Jack He106eaca2020-06-17 02:15:23 -0700143 node_list_.emplace_front(key, std::move(value));
144 lru_map_.emplace(key, node_list_.begin());
145 return ret;
Chen Chenc530a0c2020-02-20 11:29:42 -0800146 }
147
148 /**
149 * Delete a key from cache
150 *
151 * @param key
Jack He106eaca2020-06-17 02:15:23 -0700152 * @return true if deleted successfully
Chen Chenc530a0c2020-02-20 11:29:42 -0800153 */
154 bool Remove(const K& key) {
Jack He106eaca2020-06-17 02:15:23 -0700155 std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
156 auto map_iterator = lru_map_.find(key);
157 if (map_iterator == lru_map_.end()) {
Chen Chenc530a0c2020-02-20 11:29:42 -0800158 return false;
159 }
160
161 // remove from the list
Jack He106eaca2020-06-17 02:15:23 -0700162 node_list_.erase(map_iterator->second);
Chen Chenc530a0c2020-02-20 11:29:42 -0800163
164 // delete key from map
Jack He106eaca2020-06-17 02:15:23 -0700165 lru_map_.erase(map_iterator);
Chen Chenc530a0c2020-02-20 11:29:42 -0800166
167 return true;
168 }
169
170 /**
171 * Return size of the cache
172 *
173 * @return size of the cache
174 */
175 int Size() const {
Jack He106eaca2020-06-17 02:15:23 -0700176 std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
Chen Chenc530a0c2020-02-20 11:29:42 -0800177 return lru_map_.size();
178 }
179
180 private:
181 std::list<Node> node_list_;
182 size_t capacity_;
183 std::unordered_map<K, typename std::list<Node>::iterator> lru_map_;
Jack He106eaca2020-06-17 02:15:23 -0700184 mutable std::recursive_mutex lru_mutex_;
Chen Chenc530a0c2020-02-20 11:29:42 -0800185};
186
187} // namespace common
188} // namespace bluetooth