Liar's Dice 0.1
Loading...
Searching...
No Matches
lru_cache.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <boost/multi_index/hashed_index.hpp>
4#include <boost/multi_index/member.hpp>
5#include <boost/multi_index/sequenced_index.hpp>
6#include <boost/multi_index_container.hpp>
7#include <boost/optional.hpp>
8#include <chrono>
9#include <functional>
10
11namespace liarsdice::data_structures {
12
13 namespace mi = boost::multi_index;
14
15 /**
16 * @brief High-performance LRU cache using boost::multi_index
17 *
18 * Provides O(1) access and automatic eviction of least recently used items.
19 * Uses both sequenced and hashed indices for efficient operations.
20 *
21 * @tparam Key Key type
22 * @tparam Value Value type
23 * @tparam Hash Hash function for Key (default: std::hash<Key>)
24 * @tparam KeyEqual Equality comparator for Key (default: std::equal_to<Key>)
25 */
26 template <typename Key, typename Value, typename Hash = std::hash<Key>,
27 typename KeyEqual = std::equal_to<Key>>
28 class LRUCache {
29 private:
30 struct CacheEntry {
31 Key key;
32 Value value;
33 std::chrono::steady_clock::time_point timestamp;
34 mutable size_t access_count;
35
36 CacheEntry(const Key& k, const Value& v)
38 };
39
40 // Tags for multi_index
41 struct by_sequence {};
42 struct by_key {};
43
47 // Sequenced index for LRU ordering
49 // Hashed index for O(1) lookup
51 KeyEqual>>>;
52
53 public:
55
56 /**
57 * @brief Construct LRU cache with maximum capacity
58 * @param max_size Maximum number of entries
59 */
60 explicit LRUCache(size_type max_size)
62 if (max_size == 0) {
63 throw std::invalid_argument("LRUCache: max_size must be > 0");
64 }
65 }
66
67 /**
68 * @brief Insert or update a key-value pair
69 * @param key The key
70 * @param value The value
71 * @return True if a new entry was created, false if updated
72 */
73 bool put(const Key& key, const Value& value) {
74 auto& key_index = cache_.template get<by_key>();
75 auto it = key_index.find(key);
76
77 if (it != key_index.end()) {
78 // Update existing entry and move to front
79 key_index.modify(it, [&value](CacheEntry& entry) {
80 entry.value = value;
81 entry.timestamp = std::chrono::steady_clock::now();
82 entry.access_count++;
83 });
84
85 // Move to front (most recently used)
86 auto& seq_index = cache_.template get<by_sequence>();
87 seq_index.relocate(seq_index.begin(), cache_.template project<by_sequence>(it));
88
89 return false;
90 }
91
92 // Insert new entry
93 if (cache_.size() >= max_size_) {
94 // Evict least recently used (back of sequence)
95 auto& seq_index = cache_.template get<by_sequence>();
96 seq_index.pop_back();
97 evictions_++;
98 }
99
100 cache_.push_front(CacheEntry(key, value));
101 return true;
102 }
103
104 /**
105 * @brief Get value by key
106 * @param key The key to lookup
107 * @return Optional containing the value if found
108 */
109 [[nodiscard]] boost::optional<Value> get(const Key& key) {
110 auto& key_index = cache_.template get<by_key>();
111 auto it = key_index.find(key);
112
113 if (it == key_index.end()) {
114 misses_++;
115 return boost::none;
116 }
117
118 hits_++;
119
120 // Update access info and move to front
121 key_index.modify(it, [](CacheEntry& entry) {
122 entry.timestamp = std::chrono::steady_clock::now();
123 entry.access_count++;
124 });
125
126 // Move to front (most recently used)
127 auto& seq_index = cache_.template get<by_sequence>();
128 seq_index.relocate(seq_index.begin(), cache_.template project<by_sequence>(it));
129
130 return it->value;
131 }
132
133 /**
134 * @brief Check if key exists without updating LRU order
135 * @param key The key to check
136 * @return True if key exists
137 */
138 [[nodiscard]] bool contains(const Key& key) const {
139 const auto& key_index = cache_.template get<by_key>();
140 return key_index.find(key) != key_index.end();
141 }
142
143 /**
144 * @brief Remove entry by key
145 * @param key The key to remove
146 * @return True if entry was removed
147 */
148 bool erase(const Key& key) {
149 auto& key_index = cache_.template get<by_key>();
150 auto it = key_index.find(key);
151
152 if (it == key_index.end()) {
153 return false;
154 }
155
156 key_index.erase(it);
157 return true;
158 }
159
160 /**
161 * @brief Clear all entries
162 */
163 void clear() {
164 cache_.clear();
165 hits_ = 0;
166 misses_ = 0;
167 evictions_ = 0;
168 }
169
170 /**
171 * @brief Get current number of entries
172 * @return Number of cached entries
173 */
174 [[nodiscard]] size_type size() const { return cache_.size(); }
175
176 /**
177 * @brief Check if cache is empty
178 * @return True if no entries cached
179 */
180 [[nodiscard]] bool empty() const { return cache_.empty(); }
181
182 /**
183 * @brief Check if cache is full
184 * @return True if at maximum capacity
185 */
186 [[nodiscard]] bool full() const { return cache_.size() >= max_size_; }
187
188 /**
189 * @brief Get maximum capacity
190 * @return Maximum number of entries
191 */
192 [[nodiscard]] size_type capacity() const { return max_size_; }
193
194 /**
195 * @brief Get cache statistics
196 * @return Tuple of (hits, misses, evictions, hit_rate)
197 */
198 [[nodiscard]] std::tuple<size_t, size_t, size_t, double> get_stats() const {
199 size_t total = hits_ + misses_;
200 double hit_rate = total > 0 ? static_cast<double>(hits_) / total : 0.0;
201 return {hits_, misses_, evictions_, hit_rate};
202 }
203
204 /**
205 * @brief Get all cached keys in LRU order
206 * @return Vector of keys (most recently used first)
207 */
209 std::vector<Key> keys;
210 keys.reserve(cache_.size());
211
212 const auto& seq_index = cache_.template get<by_sequence>();
213 for (const auto& entry : seq_index) {
214 keys.push_back(entry.key);
215 }
216
217 return keys;
218 }
219
220 /**
221 * @brief Apply function to all entries in LRU order
222 * @tparam Func Function type
223 * @param func Function to apply (key, value, access_count)
224 */
225 template <typename Func> void for_each(Func func) const {
226 const auto& seq_index = cache_.template get<by_sequence>();
227 for (const auto& entry : seq_index) {
228 func(entry.key, entry.value, entry.access_count);
229 }
230 }
231
232 /**
233 * @brief Resize cache capacity
234 * @param new_size New maximum capacity
235 *
236 * If new_size < current size, least recently used entries are evicted
237 */
238 void resize(size_type new_size) {
239 if (new_size == 0) {
240 throw std::invalid_argument("LRUCache: new_size must be > 0");
241 }
242
243 max_size_ = new_size;
244
245 // Evict entries if necessary
246 while (cache_.size() > max_size_) {
247 auto& seq_index = cache_.template get<by_sequence>();
248 seq_index.pop_back();
249 evictions_++;
250 }
251 }
252
253 private:
256 mutable size_t hits_;
257 mutable size_t misses_;
258 mutable size_t evictions_;
259 };
260
261 /**
262 * @brief Specialized LRU cache for game state lookups
263 *
264 * Caches frequently accessed game states for AI analysis
265 */
266 template <typename StateKey, typename GameState> using GameStateCache
267 = LRUCache<StateKey, GameState>;
268
269 /**
270 * @brief Specialized LRU cache for pattern matching results
271 *
272 * Caches expensive pattern matching computations
273 */
274 template <typename PatternKey> using PatternCache = LRUCache<PatternKey, std::vector<double>>;
275
276} // namespace liarsdice::data_structures
High-performance LRU cache using boost::multi_index.
Definition lru_cache.hpp:28
void for_each(Func func) const
Apply function to all entries in LRU order.
Definition lru_cache.hpp:225
size_type size() const
Get current number of entries.
Definition lru_cache.hpp:174
void resize(size_type new_size)
Resize cache capacity.
Definition lru_cache.hpp:238
size_t hits_
Definition lru_cache.hpp:256
size_type capacity() const
Get maximum capacity.
Definition lru_cache.hpp:192
std::vector< Key > get_keys() const
Get all cached keys in LRU order.
Definition lru_cache.hpp:208
bool erase(const Key &key)
Remove entry by key.
Definition lru_cache.hpp:148
cache_container cache_
Definition lru_cache.hpp:254
void clear()
Clear all entries.
Definition lru_cache.hpp:163
bool put(const Key &key, const Value &value)
Insert or update a key-value pair.
Definition lru_cache.hpp:73
std::tuple< size_t, size_t, size_t, double > get_stats() const
Get cache statistics.
Definition lru_cache.hpp:198
size_t evictions_
Definition lru_cache.hpp:258
size_t misses_
Definition lru_cache.hpp:257
bool contains(const Key &key) const
Check if key exists without updating LRU order.
Definition lru_cache.hpp:138
size_type max_size_
Definition lru_cache.hpp:255
boost::optional< Value > get(const Key &key)
Get value by key.
Definition lru_cache.hpp:109
LRUCache(size_type max_size)
Construct LRU cache with maximum capacity.
Definition lru_cache.hpp:60
bool empty() const
Check if cache is empty.
Definition lru_cache.hpp:180
bool full() const
Check if cache is full.
Definition lru_cache.hpp:186
Value value
Definition lru_cache.hpp:32
std::chrono::steady_clock::time_point timestamp
Definition lru_cache.hpp:33
size_t access_count
Definition lru_cache.hpp:34
CacheEntry(const Key &k, const Value &v)
Definition lru_cache.hpp:36