| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| #ifndef COMMON_CACHE_LRU_CACHE_H |
| #define COMMON_CACHE_LRU_CACHE_H |
| |
| #include <algorithm> |
| #include <list> |
| #include <unordered_map> |
| |
| #include "utils/errno_define.h" |
| |
| namespace common { |
| |
| template <typename K, typename V> |
| struct KeyValuePair { |
| public: |
| K key; |
| V value; |
| |
| KeyValuePair(K k, V v) : key(std::move(k)), value(std::move(v)) {} |
| }; |
| |
| /** |
| * The LRU Cache class templated by |
| * Key - key type |
| * Value - value type |
| * MapType - an associative container like std::unordered_map |
| */ |
| template <class Key, class Value, |
| class Map = std::unordered_map< |
| Key, typename std::list<KeyValuePair<Key, Value>>::iterator>> |
| class Cache { |
| public: |
| typedef KeyValuePair<Key, Value> node_type; |
| typedef std::list<KeyValuePair<Key, Value>> list_type; |
| typedef Map map_type; |
| /** |
| * the maxSize is the soft limit of entries and (maxSize + elasticity) is the |
| * hard limit |
| * the cache is allowed to grow till (maxSize + elasticity) and is pruned |
| * back to maxSize entries |
| * set maxSize = 0 for an unbounded cache (but in that |
| * case, you're better off using a std::unordered_map directly anyway! :) |
| */ |
| explicit Cache(size_t maxSize = 64, size_t elasticity = 10) |
| : maxSize_(maxSize), elasticity_(elasticity) {} |
| virtual ~Cache() = default; |
| size_t size() const { return cache_.size(); } |
| bool empty() const { return cache_.empty(); } |
| void clear() { |
| cache_.clear(); |
| entries_.clear(); |
| } |
| void insert(const Key& k, Value v) { |
| const auto iter = cache_.find(k); |
| if (iter != cache_.end()) { |
| iter->second->value = v; |
| entries_.splice(entries_.begin(), entries_, iter->second); |
| return; |
| } |
| |
| entries_.emplace_front(k, std::move(v)); |
| cache_[k] = entries_.begin(); |
| prune(); |
| } |
| /** |
| for backward compatibity. redirects to tryGetCopy() |
| */ |
| bool tryGet(const Key& kIn, Value& vOut) { return tryGetCopy(kIn, vOut); } |
| |
| bool tryGetCopy(const Key& kIn, Value& vOut) { |
| Value tmp; |
| if (!tryGetRef_nolock(kIn, tmp)) { |
| return false; |
| } |
| vOut = tmp; |
| return true; |
| } |
| |
| bool tryGetRef(const Key& kIn, Value& vOut) { |
| return tryGetRef_nolock(kIn, vOut); |
| } |
| /** |
| * The const reference returned here is only |
| * guaranteed to be valid till the next insert/delete |
| * in multi-threaded apps use getCopy() to be threadsafe |
| */ |
| const Value& getRef(const Key& k) { return get_nolock(k); } |
| |
| /** |
| added for backward compatibility |
| */ |
| Value get(const Key& k) { return getCopy(k); } |
| /** |
| * returns a copy of the stored object (if found) |
| * safe to use/recommended in multi-threaded apps |
| */ |
| Value getCopy(const Key& k) { return get_nolock(k); } |
| |
| bool remove(const Key& k) { |
| auto iter = cache_.find(k); |
| if (iter == cache_.end()) { |
| return false; |
| } |
| entries_.erase(iter->second); |
| cache_.erase(iter); |
| return true; |
| } |
| bool contains(const Key& k) const { return cache_.find(k) != cache_.end(); } |
| |
| size_t getMaxSize() const { return maxSize_; } |
| size_t getElasticity() const { return elasticity_; } |
| size_t getMaxAllowedSize() const { return maxSize_ + elasticity_; } |
| template <typename F> |
| void cwalk(F& f) const { |
| std::for_each(entries_.begin(), entries_.end(), f); |
| } |
| |
| protected: |
| bool tryGetRef_nolock(const Key& kIn, Value& vOut) { |
| const auto iter = cache_.find(kIn); |
| if (iter == cache_.end()) { |
| return false; |
| } |
| entries_.splice(entries_.begin(), entries_, iter->second); |
| vOut = iter->second->value; |
| return true; |
| } |
| size_t prune() { |
| size_t maxAllowed = maxSize_ + elasticity_; |
| if (maxSize_ == 0 || cache_.size() < maxAllowed) { |
| return 0; |
| } |
| size_t count = 0; |
| while (cache_.size() > maxSize_) { |
| cache_.erase(entries_.back().key); |
| entries_.pop_back(); |
| ++count; |
| } |
| return count; |
| } |
| |
| private: |
| // Disallow copying. |
| Cache(const Cache&) = delete; |
| Cache& operator=(const Cache&) = delete; |
| |
| Map cache_; |
| list_type entries_; |
| size_t maxSize_; |
| size_t elasticity_; |
| }; |
| } // namespace common |
| |
| #endif // COMMON_CACHE_LRU_CACHE_H |