1use std::{
5 hash::Hash,
6 mem,
7 sync::atomic::{AtomicU64, Ordering},
8};
9
10use dashmap::DashMap;
11
12pub struct LruCache<K, V> {
17 map: DashMap<K, Entry<V>>,
18 capacity: usize,
19 access_counter: AtomicU64,
20}
21
22struct Entry<V> {
23 value: V,
24 last_access: u64,
25}
26
27impl<K: Hash + Eq + Clone, V: Clone> LruCache<K, V> {
28 pub fn new(capacity: usize) -> Self {
29 assert!(capacity > 0, "LRU cache capacity must be greater than 0");
30 Self {
31 map: DashMap::with_capacity(capacity),
32 capacity,
33 access_counter: AtomicU64::new(0),
34 }
35 }
36
37 pub fn get(&self, key: &K) -> Option<V> {
39 if let Some(mut entry) = self.map.get_mut(key) {
40 entry.last_access = self.access_counter.fetch_add(1, Ordering::Relaxed);
41 Some(entry.value.clone())
42 } else {
43 None
44 }
45 }
46
47 pub fn put(&self, key: K, value: V) -> Option<V> {
49 let access = self.access_counter.fetch_add(1, Ordering::Relaxed);
50
51 if let Some(mut entry) = self.map.get_mut(&key) {
53 let old_value = mem::replace(&mut entry.value, value);
54 entry.last_access = access;
55 return Some(old_value);
56 }
57
58 if self.map.len() >= self.capacity {
60 self.evict_lru();
61 }
62
63 self.map.insert(
64 key,
65 Entry {
66 value,
67 last_access: access,
68 },
69 );
70 None
71 }
72
73 pub fn remove(&self, key: &K) -> Option<V> {
75 self.map.remove(key).map(|(_, entry)| entry.value)
76 }
77
78 pub fn contains_key(&self, key: &K) -> bool {
80 self.map.contains_key(key)
81 }
82
83 pub fn clear(&self) {
85 self.map.clear();
86 }
87
88 pub fn len(&self) -> usize {
90 self.map.len()
91 }
92
93 pub fn is_empty(&self) -> bool {
95 self.map.is_empty()
96 }
97
98 pub fn capacity(&self) -> usize {
100 self.capacity
101 }
102
103 fn evict_lru(&self) {
105 let mut oldest_key: Option<K> = None;
106 let mut oldest_access = u64::MAX;
107
108 for entry in self.map.iter() {
109 if entry.last_access < oldest_access {
110 oldest_access = entry.last_access;
111 oldest_key = Some(entry.key().clone());
112 }
113 }
114
115 if let Some(key) = oldest_key {
116 self.map.remove(&key);
117 }
118 }
119}
120
121#[cfg(test)]
122pub mod tests {
123
124 mod lru {
125 use crate::util::lru::LruCache;
126
127 #[test]
128 fn test_basic_operations() {
129 let cache = LruCache::new(2);
130
131 assert_eq!(cache.put(1, "a"), None);
132 assert_eq!(cache.put(2, "b"), None);
133 assert_eq!(cache.get(&1), Some("a"));
134 assert_eq!(cache.get(&2), Some("b"));
135 assert_eq!(cache.len(), 2);
136 }
137
138 #[test]
139 fn test_eviction() {
140 let cache = LruCache::new(2);
141
142 cache.put(1, "a");
143 cache.put(2, "b");
144 let evicted = cache.put(3, "c");
145
146 assert_eq!(evicted, None);
149 assert_eq!(cache.get(&1), None);
150 assert_eq!(cache.get(&2), Some("b"));
151 assert_eq!(cache.get(&3), Some("c"));
152 }
153
154 #[test]
155 fn test_lru_order() {
156 let cache = LruCache::new(2);
157
158 cache.put(1, "a");
159 cache.put(2, "b");
160 cache.get(&1); cache.put(3, "c"); assert_eq!(cache.get(&1), Some("a"));
164 assert_eq!(cache.get(&2), None);
165 assert_eq!(cache.get(&3), Some("c"));
166 }
167
168 #[test]
169 fn test_update_existing() {
170 let cache = LruCache::new(2);
171
172 cache.put(1, "a");
173 let old = cache.put(1, "b");
174
175 assert_eq!(old, Some("a"));
176 assert_eq!(cache.get(&1), Some("b"));
177 assert_eq!(cache.len(), 1);
178 }
179
180 #[test]
181 fn test_remove() {
182 let cache = LruCache::new(2);
183
184 cache.put(1, "a");
185 cache.put(2, "b");
186 let removed = cache.remove(&1);
187
188 assert_eq!(removed, Some("a"));
189 assert_eq!(cache.get(&1), None);
190 assert_eq!(cache.len(), 1);
191 }
192
193 #[test]
194 fn test_clear() {
195 let cache = LruCache::new(2);
196
197 cache.put(1, "a");
198 cache.put(2, "b");
199 cache.clear();
200
201 assert_eq!(cache.len(), 0);
202 assert!(cache.is_empty());
203 }
204
205 #[test]
206 fn test_contains_key() {
207 let cache = LruCache::new(2);
208
209 cache.put(1, "a");
210 assert!(cache.contains_key(&1));
211 assert!(!cache.contains_key(&2));
212 }
213 }
214}