Cadabra
Computer algebra system for field theory problems
Loading...
Searching...
No Matches
lru_cache.hh
Go to the documentation of this file.
1// STL-like templated LRU cache.
2//
3// Copyright (C) 2024 Daniel Butter <dbutter@gmail.com>
4// Distributed under the MIT license.
5//
6
16#ifndef lru_cache_hh_
17#define lru_cache_hh_
18
19#include <map>
20#include <list>
21#include <stdexcept>
22#include <iterator>
23
24template <typename Key, typename Value, typename Compare = std::less<Key>>
25class LRUcache {
26public:
27 using value_type = std::pair<Key, Value>;
28 using List = std::list<value_type>;
29 using iterator = typename List::iterator;
30 using const_iterator = typename List::const_iterator;
31 using size_type = size_t;
32
33
34 LRUcache(size_t capacity) : max_size(capacity) {}
35
36 // Touches a list iterator, moving it to the top
37 void touch(iterator& it) {
38 if (it != cache_list.begin()) {
39 cache_list.splice(cache_list.begin(), cache_list, it);
40 }
41 }
42
43 // Access an item by key (possibly touching it)
44 Value& at(const Key& key, bool touching = true) {
45 auto lookup_it = cache_lookup.find(key);
46 if (lookup_it == cache_lookup.end()) {
47 throw std::out_of_range("Key not found");
48 }
49 if (touching) {
50 touch(lookup_it->second);
51 }
52 return lookup_it->second->second;
53 }
54
55 // Insert or update an item (always touches)
56 void insert(const Key& key, const Value& value) {
57 auto lookup_it = cache_lookup.find(key);
58 if (lookup_it != cache_lookup.end()) {
59 touch(lookup_it->second);
60 lookup_it->second->second = value;
61 } else {
62 cache_list.emplace_front(key, value);
63 cache_lookup[key] = cache_list.begin();
64 while (cache_list.size() > max_size) {
65 evict();
66 }
67 }
68 }
69
70 // Access an item (or create it) using operator[] (always touches)
71 Value& operator[](const Key& key) {
72 auto lookup_it = cache_lookup.find(key);
73 if (lookup_it != cache_lookup.end()) {
74 // If found, move it to the front of the list (LRU behavior)
75 touch(lookup_it->second);
76 return lookup_it->second->second;
77 } else {
78 // Create new entry, inserting at the front of the list
79 cache_list.emplace_front(key, Value());
80 iterator new_iter = cache_list.begin();
81 cache_lookup[key] = new_iter;
82 while (cache_list.size() > max_size) {
83 evict();
84 }
85 return new_iter->second;
86 }
87 }
88
89 // Find a key (possibly touching it)
90 iterator find(const Key& key, bool touching = true) {
91 auto lookup_it = cache_lookup.find(key);
92 if (lookup_it == cache_lookup.end()) {
93 return cache_list.end(); // Key not found
94 }
95 if (touching) {
96 touch(lookup_it->second);
97 }
98 return lookup_it->second;
99 }
100
101 // Check if a key exists (possibly touching it)
102 bool contains(const Key& key, bool touching = true) {
103 return find(key, touching) != end();
104 }
105
106 // Remove an item by key
107 size_t erase(const Key& key) {
108 auto lookup_it = cache_lookup.find(key);
109 if (lookup_it != cache_lookup.end()) {
110 cache_list.erase(lookup_it->second);
111 cache_lookup.erase(lookup_it);
112 return 1;
113 }
114 return 0;
115 }
116
117 // Remove an item by iterator
119 cache_lookup.erase(it->first);
120 return cache_list.erase(it);
121 }
123 cache_lookup.erase(it->first);
124 return cache_list.erase(it);
125 }
126
127
128 // Clear the cache
129 void clear() {
130 cache_list.clear();
131 cache_lookup.clear();
132 }
133
134 // Check if the cache is empty
135 bool empty() const {
136 return cache_list.empty();
137 }
138
139 // Get the current size of the cache
140 size_t size() const {
141 assert(cache_list.size() == cache_lookup.size());
142 return cache_list.size();
143 }
144
145 // Resize the cache
146 void resize(size_t new_size) {
147 max_size = new_size;
148 while (cache_list.size() > max_size) {
149 evict();
150 }
151 }
152
153 // Iterators
154 iterator begin() { return cache_list.begin(); }
155 iterator end() { return cache_list.end(); }
156
157
158private:
159
160 List cache_list; // Doubly linked list of key-value pairs
161 std::map<Key, iterator, Compare> cache_lookup; // Map to locate list nodes
162 size_t max_size;
163
164 void evict() {
165 auto lru = cache_list.back().first;
166 cache_lookup.erase(lru);
167 cache_list.pop_back();
168 }
169};
170
171
172#endif
Definition lru_cache.hh:25
typename List::const_iterator const_iterator
Definition lru_cache.hh:30
iterator find(const Key &key, bool touching=true)
Definition lru_cache.hh:90
Value & operator[](const Key &key)
Definition lru_cache.hh:71
iterator erase(iterator &it)
Definition lru_cache.hh:122
size_t size_type
Definition lru_cache.hh:31
void touch(iterator &it)
Definition lru_cache.hh:37
void resize(size_t new_size)
Definition lru_cache.hh:146
bool empty() const
Definition lru_cache.hh:135
void evict()
Definition lru_cache.hh:164
LRUcache(size_t capacity)
Definition lru_cache.hh:34
iterator begin()
Definition lru_cache.hh:154
std::map< Key, iterator, Compare > cache_lookup
Definition lru_cache.hh:161
size_t size() const
Definition lru_cache.hh:140
void insert(const Key &key, const Value &value)
Definition lru_cache.hh:56
size_t erase(const Key &key)
Definition lru_cache.hh:107
void clear()
Definition lru_cache.hh:129
size_t max_size
Definition lru_cache.hh:162
List cache_list
Definition lru_cache.hh:160
iterator end()
Definition lru_cache.hh:155
std::pair< Key, Value > value_type
Definition lru_cache.hh:27
typename List::iterator iterator
Definition lru_cache.hh:29
Value & at(const Key &key, bool touching=true)
Definition lru_cache.hh:44
std::list< value_type > List
Definition lru_cache.hh:28
bool contains(const Key &key, bool touching=true)
Definition lru_cache.hh:102
iterator erase(const_iterator &it)
Definition lru_cache.hh:118