step20
Loading...
Searching...
No Matches
least_frequently_used.hpp
Go to the documentation of this file.
1// Andrew Naplavkov
2
3#ifndef STEP20_LEAST_FREQUENTLY_USED_HPP
4#define STEP20_LEAST_FREQUENTLY_USED_HPP
5
6#include <algorithm>
7#include <list>
8#include <unordered_map>
9
11
13template <class Key,
14 class T,
15 class Hash = std::hash<Key>,
16 class KeyEqual = std::equal_to<Key>>
17class cache {
18 struct item_type;
19 using item_list = std::list<item_type>;
20 using item_iterator = typename item_list::iterator;
21 struct freq_type;
22 using freq_list = std::list<freq_type>;
23 using freq_iterator = typename freq_list::iterator;
24
25 struct item_type {
26 freq_iterator parent;
27 Key key;
28 T val;
29 };
30
31 struct freq_type {
32 std::size_t n;
33 item_list items;
34 };
35
36 freq_list list_;
37 std::unordered_map<Key, item_iterator, Hash, KeyEqual> map_;
38 std::size_t capacity_;
39
40 bool equal(freq_iterator it, std::size_t n) const
41 {
42 return it != list_.end() && it->n == n;
43 }
44
45public:
46 explicit cache(std::size_t capacity) : capacity_(capacity) {}
47 cache(cache&&) = default;
48 cache& operator=(cache&&) = default;
49 cache(const cache&) = delete;
50 cache& operator=(const cache&) = delete;
51 virtual ~cache() = default;
52
54 const T* find(const Key& key)
55 {
56 auto it = map_.find(key);
57 if (it == map_.end())
58 return nullptr;
59 auto item = it->second;
60 auto freq = item->parent;
61 auto next = std::next(freq);
62 if (!equal(next, freq->n + 1)) {
63 if (freq->items.size() == 1) {
64 ++freq->n;
65 return std::addressof(item->val);
66 }
67 next = list_.emplace(next, freq->n + 1);
68 }
69 try {
70 next->items.splice(next->items.end(), freq->items, item);
71 }
72 catch (...) {
73 if (next->items.empty())
74 list_.erase(next);
75 throw;
76 }
77 item->parent = next;
78 if (freq->items.empty())
79 list_.erase(freq);
80 return std::addressof(item->val);
81 }
82
85 void insert_or_assign(const Key& key, const T& val)
86 {
87 if (auto ptr = find(key)) {
88 *const_cast<T*>(ptr) = val;
89 return;
90 }
91 if (map_.size() >= std::max<std::size_t>(1, capacity_)) {
92 auto freq = list_.begin();
93 auto item = freq->items.begin();
94 map_.erase(item->key);
95 freq->items.erase(item);
96 if (freq->items.empty())
97 list_.erase(freq);
98 }
99 auto freq = list_.begin();
100 auto exists = equal(freq, 1);
101 freq = exists ? freq : list_.emplace(freq, 1);
102 auto item = freq->items.end();
103 try {
104 item = freq->items.emplace(item, freq, key, val);
105 map_.emplace(key, item);
106 }
107 catch (...) {
108 if (!exists)
109 list_.erase(freq);
110 else if (item != freq->items.end())
111 freq->items.erase(item);
112 throw;
113 }
114 }
115};
116
117} // namespace step20::least_frequently_used
118
119#endif // STEP20_LEAST_FREQUENTLY_USED_HPP
An O(1) algorithm for implementing the LFU cache eviction scheme.
Definition least_frequently_used.hpp:17
cache & operator=(const cache &)=delete
const T * find(const Key &key)
Definition least_frequently_used.hpp:54
cache & operator=(cache &&)=default
void insert_or_assign(const Key &key, const T &val)
Definition least_frequently_used.hpp:85
cache(std::size_t capacity)
Definition least_frequently_used.hpp:46
Definition least_frequently_used.hpp:10