* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* 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.
*/
#ifndef FUNCTIONSYSTEM_COMMON_LRU_LRU_CACHE_H
#define FUNCTIONSYSTEM_COMMON_LRU_LRU_CACHE_H
#include <cstddef>
#include <stdexcept>
#include <functional>
#include <list>
#include <optional>
#include <unordered_map>
#include <utility>
namespace functionsystem {
template <typename K, typename V>
class LruCache {
public:
using EvictionCallback = std::function<void(const K&, V&&)>;
explicit LruCache(size_t capacity, EvictionCallback onEvict = nullptr)
: member_capacity(capacity), member_onEvict(std::move(onEvict))
{
if (capacity == 0) {
throw std::invalid_argument("LruCache capacity must be greater than 0");
}
}
LruCache(const LruCache&) = delete;
LruCache& operator=(const LruCache&) = delete;
LruCache(LruCache&&) noexcept = default;
LruCache& operator=(LruCache&&) noexcept = default;
~LruCache() = default;
bool Put(const K& key, V value)
{
auto it = member_map.find(key);
if (it != member_map.end()) {
it->second->second = std::move(value);
member_list.splice(member_list.begin(), member_list, it->second);
return false;
}
if (member_list.size() >= member_capacity) {
EvictLru();
}
member_list.emplace_front(key, std::move(value));
member_map[key] = member_list.begin();
return true;
}
std::optional<std::reference_wrapper<V>> Get(const K& key)
{
auto it = member_map.find(key);
if (it == member_map.end()) {
return std::nullopt;
}
member_list.splice(member_list.begin(), member_list, it->second);
return std::ref(it->second->second);
}
bool Contains(const K& key) const
{
return member_map.count(key) > 0;
}
std::optional<std::reference_wrapper<const V>> Peek(const K& key) const
{
auto it = member_map.find(key);
if (it == member_map.end()) {
return std::nullopt;
}
return std::cref(it->second->second);
}
bool Remove(const K& key)
{
auto it = member_map.find(key);
if (it == member_map.end()) {
return false;
}
member_list.erase(it->second);
member_map.erase(it);
return true;
}
void Clear()
{
member_list.clear();
member_map.clear();
}
size_t Size() const { return member_map.size(); }
size_t Capacity() const { return member_capacity; }
private:
using ListType = std::list<std::pair<K, V>>;
using ListIterator = typename ListType::iterator;
void EvictLru()
{
auto& back = member_list.back();
K evictedKey = back.first;
V evictedValue = std::move(back.second);
member_map.erase(evictedKey);
member_list.pop_back();
if (member_onEvict) {
try {
member_onEvict(evictedKey, std::move(evictedValue));
} catch (...) {
}
}
}
size_t member_capacity;
ListType member_list;
std::unordered_map<K, ListIterator> member_map;
EvictionCallback member_onEvict;
};
}
#endif