11#ifndef __BOXES_CACHE_HPP__
12#define __BOXES_CACHE_HPP__
14#include "compiler.hpp"
19#include <unordered_map>
26template <
typename K,
typename V>
class LRU {
28 virtual ~LRU() =
default;
38 LRU(std::size_t maxSize) : maxSize{maxSize} {}
40 using AccessQueue = std::list<K>;
41 using Iterator =
typename AccessQueue::iterator;
51 virtual void clear() { access.clear(); }
53 virtual void onEvict(
const K &, Value &value) { access.erase(value.it); }
55 virtual void onAccess(
const K &key, Value &value) {
56 access.erase(value.it);
57 value.it = access.insert(access.begin(), key);
60 virtual Value onInsert(
const K &key, V value) {
61 return Value{std::move(value), access.insert(access.begin(), key)};
73 virtual K &front() {
return access.front(); }
84 virtual K &back() {
return access.back(); }
94 virtual Iterator begin() {
return access.begin(); }
101 virtual Iterator end() {
return access.end(); }
103 virtual const K &getKey(Iterator it) {
return *it; }
114 virtual const K *elect() {
115 if (access.size() < maxSize) {
118 return &this->access.back();
122 const std::size_t maxSize;
133 using AccessQueue = std::list<K>;
134 using Iterator =
typename AccessQueue::iterator;
146 const K *
elect() {
return nullptr; }
153 void onEvict(
const K &, Value &) {}
155 void onAccess(
const K &, Value &) {}
157 Value onInsert(
const K &key, V value) {
158 return Value{std::move(value), access.insert(access.end(), key)};
169 K &
front() {
return access.front(); }
179 K &
back() {
return access.back(); }
186 Iterator
begin() {
return access.begin(); }
194 Iterator
end() {
return access.end(); }
196 const K &getKey(Iterator it) {
return *it; }
214template <
typename K,
typename V>
class MRU :
public LRU<K, V> {
216 using LRU<K, V>::LRU;
218 void onAccess(
const K &key,
typename LRU<K, V>::Value &value)
override {
219 this->access.erase(value.it);
220 value.it = this->access.insert(this->access.rbegin().base(), key);
223 typename LRU<K, V>::Value onInsert(
const K &key, V value)
override {
224 return typename LRU<K, V>::Value{
226 this->access.insert(this->access.rbegin().base(), key)};
238template <
typename K,
typename V>
class LFU {
240 explicit LFU(std::size_t maxSize) : maxSize{maxSize} {}
242 using FrequencyMap = std::multimap<std::size_t, K>;
243 using Iterator =
typename FrequencyMap::reverse_iterator;
255 void onAccess(
const K &key, Value &value) {
256 auto it = value.it.base();
257 const auto freq = it->first;
259 value.it = std::make_reverse_iterator(freqs.emplace(freq + 1, key));
262 Value onInsert(
const K &key, V &&value) {
263 constexpr std::size_t initialFreq = 0;
264 auto rit = std::make_reverse_iterator(freqs.emplace(initialFreq, key));
265 return Value{std::forward<V>(value), rit};
268 void onEvict(
const K &, Value &value) { freqs.erase(value.it.base()); }
270 const K &getKey(Iterator it) {
return it->second; }
279 K &
front() {
return freqs.rbegin()->second; }
288 K &
back() {
return freqs.begin()->second; }
297 Iterator
begin() {
return freqs.rbegin(); }
305 Iterator
end() {
return freqs.rend(); }
314 if (freqs.size() < maxSize) {
318 return &freqs.begin()->second;
322 const std::size_t maxSize;
335template <
typename K,
typename V,
336 template <
typename,
typename>
class CachePolicy>
339 using Policy = CachePolicy<K, V>;
340 using PolicyIterator =
typename Policy::Iterator;
344 using iterator_category = std::bidirectional_iterator_tag;
345 using value_type = std::pair<K, V>;
346 using difference_type = std::ptrdiff_t;
349 : cache{cache}, it{it} {}
351 iterator &operator++() {
356 iterator operator++(
int) {
357 iterator copy = *
this;
362 iterator &operator--() {
367 iterator operator--(
int) {
368 iterator copy = *
this;
373 value_type &operator*() {
374 const auto &key = cache.policy.getKey(it);
375 const auto &pv = cache.data[key];
376 valueProxy = std::make_pair(key, pv.value);
380 value_type *operator->() {
381 const auto &key = cache.policy.getKey(it);
382 auto &pv = cache.data[key];
383 valueProxy = std::make_pair(key, pv.value);
387 bool operator==(
const iterator &other)
const {
return it == other.it; }
389 bool operator!=(
const iterator &other)
const {
return it != other.it; }
394 std::pair<K, V> valueProxy;
402 explicit Cache(Policy policy) : policy{std::move(policy)} {}
416 std::size_t
size()
const {
return data.size(); }
429 std::size_t
max_size()
const {
return data.max_size(); }
436 bool empty()
const {
return data.empty(); }
457 template <
typename KArg,
typename VArg>
459 if (
auto it = data.find(key); it != data.end()) {
460 policy.onAccess(key, it->second);
464 if (
auto elected = policy.elect()) {
465 if (
auto it = data.find(*elected); it != data.end()) {
466 policy.onEvict(*elected, it->second);
471 data.emplace(key, policy.onInsert(key, std::forward<VArg>(value)));
484 bool contains(
const K &key)
const {
return data.find(key) != data.end(); }
493 iterator
begin() {
return iterator{*
this, policy.begin()}; }
503 iterator
end() {
return iterator{*
this, policy.end()}; }
531 if (
auto it = data.find(key); it != data.end()) {
532 policy.onAccess(key, it->second);
533 return iterator{*
this, it->second.it};
548 V &
at(
const K &key) {
549 if (
auto it = data.find(key); it != data.end()) {
550 policy.onAccess(key, it->second);
551 return it->second.value;
554 throw std::out_of_range{
"Key not found"};
563 const V &
at(
const K &key)
const {
return const_cast<Cache *
>(
this)->
at(key); }
573 K &
front() {
return policy.front(); }
583 K &
back() {
return policy.back(); }
600 using Value =
typename Policy::Value;
601 using Map = std::unordered_map<K, Value>;
604 mutable Policy policy;
615template <
typename K,
typename V>
628template <
typename K,
typename V>
640template <
typename K,
typename V>
653template <
typename K,
typename V>
Cache< K, V, policy::LRU > makeLRU(std::size_t size)
Create a new LRU cache.
Cache< K, V, policy::MRU > makeMRU(std::size_t size)
Create a new MRU cache.
Cache< K, V, policy::NoEviction > makeNoEviction()
Create a new no eviction cache.
Cache< K, V, policy::LFU > makeLFU(std::size_t size)
Create a new LFU cache.
Generic cache with pluggable eviction policies.
iterator begin()
Returns an iterator to the first element in the cache.
iterator end()
Returns an iterator to the element following the last element in the cache.
Cache(Policy policy)
Construct a new Cache object.
const V & at(const K &key) const
Returns a const reference to the value associated with the given key.
void reserve(std::size_t size)
Reserve space for the cache to avoid unnecessary reallocations.
bool contains(const K &key) const
Check if the cache contains a key.
const K & front() const
Returns const reference the first element in the cache.
K & back()
Returns the reference the last element in the cache.
const K & back() const
Returns const reference the last element in the cache.
std::size_t max_size() const
Returns the maximum number of elements the cache can ever hold.
bool insert(KArg &&key, VArg &&value)
Inserts a key-value pair into the cache.
std::size_t size() const
Returns the number of elements in the cache.
iterator cend() const
Returns a const iterator to the element following the last element in the cache.
V & at(const K &key)
Returns a reference to the value associated with the given key.
iterator find(const K &key)
Returns an iterator to the element with the given key.
iterator cbegin() const
Returns a const iterator to the first element in the cache.
K & front()
Returns the reference the first element in the cache.
bool empty() const
Returns whether the cache is empty.
void clear()
Removes all elements from the cache.
Eviction policy that evicts the least frequently used element.
const K * elect()
Elects the least frequently used element for eviction.
Iterator begin()
Returns an iterator to the first element in the cache.
Iterator end()
Returns an iterator to the element following the last element in the cache.
K & back()
Returns the reference to the last element in the cache.
K & front()
Returns the reference to the first element in the cache.
void clear()
Clears the eviction policy.
Constructs a new MRU eviction policy.
Eviction policy that never evicts elements.
K & front()
Returns the reference to the first element in the cache.
Iterator end()
Returns an iterator to the element following the last element in the cache.
Iterator begin()
Returns an iterator to the first element in the cache.
const K * elect()
Never elects an element for eviction.
void clear()
Clears the eviction policy.
K & back()
Returns the reference to the last element in the cache.