vkdb
A time series database engine in C++.
Loading...
Searching...
No Matches
lru_cache.h
1#ifndef STORAGE_LRU_CACHE_H
2#define STORAGE_LRU_CACHE_H
3
4#include <list>
5#include <unordered_map>
6#include <optional>
7#include <utility>
8#include <concepts>
9#include <mutex>
10#include <vkdb/concepts.h>
11
12namespace vkdb {
19template <RegularNoCVRefQuals TKey, RegularNoCVRefQuals TValue>
20class LRUCache {
21public:
22 using key_type = TKey;
23 using mapped_type = std::optional<TValue>;
24 using value_type = std::pair<const key_type, mapped_type>;
25 using size_type = uint64_t;
26
31 LRUCache() noexcept
32 : capacity_{DEFAULT_CAPACITY}
33 , mutex_{std::make_unique<std::mutex>()} {}
34
42 explicit LRUCache(size_type capacity)
43 : capacity_{capacity}
44 , mutex_{std::make_unique<std::mutex>()} {
45 if (capacity == 0) {
46 throw std::invalid_argument{
47 "LRUCache(): Capacity must be greater than 0."
48 };
49 }
50 }
51
56 LRUCache(LRUCache&&) noexcept = default;
57
62 LRUCache& operator=(LRUCache&&) noexcept = default;
63
68 LRUCache(const LRUCache&) = delete;
69
74 LRUCache& operator=(const LRUCache&) = delete;
75
80 ~LRUCache() noexcept = default;
81
92 template <SameNoCVRefQuals <key_type> K, std::convertible_to<mapped_type> V>
93 void put(K&& key, V&& value) {
94 std::lock_guard lock{*mutex_};
95 const auto it{map_.find(key)};
96 if (it != map_.end()) {
97 it->second->second = std::forward<V>(value);
98 list_.splice(list_.begin(), list_, it->second);
99 return;
100 }
101
102 evict_if_needed();
103 list_.emplace_front(std::forward<K>(key), std::forward<V>(value));
104 map_.try_emplace(list_.front().first, list_.begin());
105 }
106
114 template <SameNoCVRefQuals <key_type> K>
115 [[nodiscard]] mapped_type get(K&& key) {
116 std::lock_guard lock{*mutex_};
117 const auto it{map_.find(std::forward<K>(key))};
118 if (it == map_.end()) {
119 return std::nullopt;
120 }
121 list_.splice(list_.begin(), list_, it->second);
122 return it->second->second;
123 }
124
133 template <SameNoCVRefQuals <key_type> K>
134 [[nodiscard]] bool contains(K&& key) const noexcept {
135 std::lock_guard lock{*mutex_};
136 return map_.find(std::forward<K>(key)) != map_.end();
137 }
138
145 [[nodiscard]] size_type capacity() const noexcept {
146 std::lock_guard lock{*mutex_};
147 return capacity_;
148 }
149
155 [[nodiscard]] size_type size() const noexcept {
156 std::lock_guard lock{*mutex_};
157 return list_.size();
158 }
159
164 void clear() noexcept {
165 std::lock_guard lock{*mutex_};
166 list_.clear();
167 map_.clear();
168 }
169
170private:
175 using CacheList = std::list<value_type>;
176
181 using CacheListIter = typename CacheList::iterator;
182
187 using CacheListIterMap = std::unordered_map<key_type, CacheListIter>;
188
193 static constexpr size_type DEFAULT_CAPACITY{1'000};
194
199 void evict_if_needed() noexcept {
200 if (list_.size() == capacity_) {
201 map_.erase(list_.back().first);
202 list_.pop_back();
203 }
204 }
205
210 size_type capacity_;
211
216 CacheList list_;
217
222 CacheListIterMap map_;
223
228 mutable std::unique_ptr<std::mutex> mutex_;
229};
230} // namespace vkdb
231
232#endif // STORAGE_LRU_CACHE_H
A thread-safe LRU cache.
Definition lru_cache.h:20
LRUCache() noexcept
Construct a new LRUCache object.
Definition lru_cache.h:31
void put(K &&key, V &&value)
Put a key-value pair into the cache.
Definition lru_cache.h:93
LRUCache(LRUCache &&) noexcept=default
Move-construct a LRUCache object.
bool contains(K &&key) const noexcept
Check if the cache contains a key.
Definition lru_cache.h:134
mapped_type get(K &&key)
Get a value from the cache.
Definition lru_cache.h:115
size_type size() const noexcept
Get the size of the cache.
Definition lru_cache.h:155
size_type capacity() const noexcept
Remove a key from the cache.
Definition lru_cache.h:145
LRUCache(size_type capacity)
Construct a new LRUCache object given a capacity.
Definition lru_cache.h:42
void clear() noexcept
Clear the cache.
Definition lru_cache.h:164
Concept for types that are the same after removing cv- and ref-qualifiers.
Definition concepts.h:14
STL namespace.