blob: b3165587a61b94eb67ec706ca7dad493e63dbfb4 [file] [log] [blame]
/*
* 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