/****************************************************************************** * * Copyright 2020 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #pragma once #include #include #include #include #include #include #include #include #include "check.h" namespace bluetooth { namespace common { template class LegacyLruCache { public: using Node = std::pair; /** * Constructor of the cache * * @param capacity maximum size of the cache * @param log_tag, keyword to put at the head of log. */ LegacyLruCache(const size_t& capacity, const std::string& log_tag) : capacity_(capacity) { if (capacity_ == 0) { // don't allow invalid capacity LOG(FATAL) << log_tag << " unable to have 0 LRU Cache capacity"; } } // delete copy constructor LegacyLruCache(LegacyLruCache const&) = delete; LegacyLruCache& operator=(LegacyLruCache const&) = delete; ~LegacyLruCache() { Clear(); } /** * Clear the cache */ void Clear() { std::lock_guard lock(lru_mutex_); lru_map_.clear(); node_list_.clear(); } /** * Same as Get, but return an iterator to the accessed element * * Modifying the returned iterator does not warm up the cache * * @param key * @return pointer to the underlying value to allow in-place modification * nullptr when not found, will be invalidated when the key is evicted */ V* Find(const K& key) { std::lock_guard lock(lru_mutex_); auto map_iterator = lru_map_.find(key); if (map_iterator == lru_map_.end()) { return nullptr; } node_list_.splice(node_list_.begin(), node_list_, map_iterator->second); return &(map_iterator->second->second); } /** * Get the value of a key, and move the key to the head of cache, if there is * one * * @param key * @param value, output parameter of value of the key * @return true if the cache has the key */ bool Get(const K& key, V* value) { CHECK(value != nullptr); std::lock_guard lock(lru_mutex_); auto value_ptr = Find(key); if (value_ptr == nullptr) { return false; } *value = *value_ptr; return true; } /** * Check if the cache has the input key, move the key to the head * if there is one * * @param key * @return true if the cache has the key */ bool HasKey(const K& key) { std::lock_guard lock(lru_mutex_); return Find(key) != nullptr; } /** * Put a key-value pair to the head of cache * * @param key * @param value * @return evicted node if tail value is popped, std::nullopt if no value * is popped. std::optional can be treated as a boolean as well */ std::optional Put(const K& key, V value) { std::lock_guard lock(lru_mutex_); auto value_ptr = Find(key); if (value_ptr != nullptr) { // hasKey() calls get(), therefore already move the node to the head *value_ptr = std::move(value); return std::nullopt; } // remove tail std::optional ret = std::nullopt; if (lru_map_.size() == capacity_) { lru_map_.erase(node_list_.back().first); ret = std::move(node_list_.back()); node_list_.pop_back(); } // insert to dummy next; node_list_.emplace_front(key, std::move(value)); lru_map_.emplace(key, node_list_.begin()); return ret; } /** * Delete a key from cache * * @param key * @return true if deleted successfully */ bool Remove(const K& key) { std::lock_guard lock(lru_mutex_); auto map_iterator = lru_map_.find(key); if (map_iterator == lru_map_.end()) { return false; } // remove from the list node_list_.erase(map_iterator->second); // delete key from map lru_map_.erase(map_iterator); return true; } /** * Return size of the cache * * @return size of the cache */ int Size() const { std::lock_guard lock(lru_mutex_); return lru_map_.size(); } private: std::list node_list_; size_t capacity_; std::unordered_map::iterator> lru_map_; mutable std::recursive_mutex lru_mutex_; }; } // namespace common } // namespace bluetooth