Skip to content

Commit f71e0ec

Browse files
committed
feat: use a proper error type in LruCache
1 parent 3fc8e23 commit f71e0ec

File tree

1 file changed

+31
-20
lines changed

1 file changed

+31
-20
lines changed

stacks-common/src/util/lru_cache.rs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ impl<K: Display, V: Display> Display for Node<K, V> {
4040
}
4141
}
4242

43+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44+
pub struct LruCacheCorrupted;
45+
46+
impl std::fmt::Display for LruCacheCorrupted {
47+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48+
write!(f, "LRU cache is in a corrupted state")
49+
}
50+
}
51+
52+
impl std::error::Error for LruCacheCorrupted {}
53+
4354
/// LRU cache
4455
pub struct LruCache<K, V> {
4556
capacity: usize,
@@ -92,10 +103,10 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
92103

93104
/// Get the value for the given key
94105
/// Returns an error iff the cache is corrupted and should be discarded
95-
pub fn get(&mut self, key: &K) -> Result<Option<V>, ()> {
106+
pub fn get(&mut self, key: &K) -> Result<Option<V>, LruCacheCorrupted> {
96107
if let Some(&index) = self.cache.get(key) {
97108
self.move_to_head(index)?;
98-
let node = self.order.get(index).ok_or(())?;
109+
let node = self.order.get(index).ok_or(LruCacheCorrupted)?;
99110
Ok(Some(node.value))
100111
} else {
101112
Ok(None)
@@ -105,14 +116,14 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
105116
/// Insert a key-value pair into the cache, marking it as dirty.
106117
/// Returns an error iff the cache is corrupted and should be discarded
107118
/// Returns `Ok(Some((K, V)))` if a dirty value was evicted.
108-
pub fn insert(&mut self, key: K, value: V) -> Result<Option<(K, V)>, ()> {
119+
pub fn insert(&mut self, key: K, value: V) -> Result<Option<(K, V)>, LruCacheCorrupted> {
109120
self.insert_with_dirty(key, value, true)
110121
}
111122

112123
/// Insert a key-value pair into the cache, marking it as clean.
113124
/// Returns an error iff the cache is corrupted and should be discarded
114125
/// Returns `Ok(Some((K, V)))` if a dirty value was evicted.
115-
pub fn insert_clean(&mut self, key: K, value: V) -> Result<Option<(K, V)>, ()> {
126+
pub fn insert_clean(&mut self, key: K, value: V) -> Result<Option<(K, V)>, LruCacheCorrupted> {
116127
self.insert_with_dirty(key, value, false)
117128
}
118129

@@ -124,10 +135,10 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
124135
key: K,
125136
value: V,
126137
dirty: bool,
127-
) -> Result<Option<(K, V)>, ()> {
138+
) -> Result<Option<(K, V)>, LruCacheCorrupted> {
128139
if let Some(&index) = self.cache.get(&key) {
129140
// Update an existing node
130-
let node = self.order.get_mut(index).ok_or(())?;
141+
let node = self.order.get_mut(index).ok_or(LruCacheCorrupted)?;
131142
node.value = value;
132143
node.dirty = dirty;
133144
self.move_to_head(index)?;
@@ -139,7 +150,7 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
139150
// We've reached capacity. Evict the least-recently used value
140151
// and reuse its node
141152
let index = self.evict_lru()?;
142-
let tail_node = self.order.get_mut(index).ok_or(())?;
153+
let tail_node = self.order.get_mut(index).ok_or(LruCacheCorrupted)?;
143154

144155
// Replace the key with the new key, saving the old key
145156
let replaced_key = std::mem::replace(&mut tail_node.key, key.clone());
@@ -186,7 +197,7 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
186197
pub fn flush<E>(
187198
&mut self,
188199
mut f: impl FnMut(&K, V) -> Result<(), E>,
189-
) -> Result<Result<(), E>, ()> {
200+
) -> Result<Result<(), E>, LruCacheCorrupted> {
190201
let mut current = self.head;
191202

192203
// Keep track of visited nodes to detect cycles
@@ -195,10 +206,10 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
195206
while current != self.capacity {
196207
// Detect cycles
197208
if !visited.insert(current) {
198-
return Err(());
209+
return Err(LruCacheCorrupted);
199210
}
200211

201-
let node = self.order.get_mut(current).ok_or(())?;
212+
let node = self.order.get_mut(current).ok_or(LruCacheCorrupted)?;
202213
let next = node.next;
203214
if node.dirty {
204215
let value = node.value;
@@ -216,8 +227,8 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
216227
}
217228

218229
/// Helper function to remove a node from the linked list (by index)
219-
fn detach_node(&mut self, index: usize) -> Result<(), ()> {
220-
let node = self.order.get(index).ok_or(())?;
230+
fn detach_node(&mut self, index: usize) -> Result<(), LruCacheCorrupted> {
231+
let node = self.order.get(index).ok_or(LruCacheCorrupted)?;
221232
let prev = node.prev;
222233
let next = node.next;
223234

@@ -226,7 +237,7 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
226237
self.tail = prev;
227238
} else {
228239
// Else, update the next node to point to the previous node
229-
let next_node = self.order.get_mut(next).ok_or(())?;
240+
let next_node = self.order.get_mut(next).ok_or(LruCacheCorrupted)?;
230241
next_node.prev = prev;
231242
}
232243

@@ -235,22 +246,22 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
235246
self.head = next;
236247
} else {
237248
// Else, update the previous node to point to the next node
238-
let prev_node = self.order.get_mut(prev).ok_or(())?;
249+
let prev_node = self.order.get_mut(prev).ok_or(LruCacheCorrupted)?;
239250
prev_node.next = next;
240251
}
241252

242253
Ok(())
243254
}
244255

245256
/// Helper function to attach a node as the head of the linked list
246-
fn attach_as_head(&mut self, index: usize) -> Result<(), ()> {
247-
let node = self.order.get_mut(index).ok_or(())?;
257+
fn attach_as_head(&mut self, index: usize) -> Result<(), LruCacheCorrupted> {
258+
let node = self.order.get_mut(index).ok_or(LruCacheCorrupted)?;
248259
node.prev = self.capacity;
249260
node.next = self.head;
250261

251262
if self.head != self.capacity {
252263
// If there is a head, update its previous pointer to this one
253-
let head_node = self.order.get_mut(self.head).ok_or(())?;
264+
let head_node = self.order.get_mut(self.head).ok_or(LruCacheCorrupted)?;
254265
head_node.prev = index;
255266
} else {
256267
// Else, the list was empty, so update the tail
@@ -261,7 +272,7 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
261272
}
262273

263274
/// Helper function to move a node to the head of the linked list
264-
fn move_to_head(&mut self, index: usize) -> Result<(), ()> {
275+
fn move_to_head(&mut self, index: usize) -> Result<(), LruCacheCorrupted> {
265276
if index == self.head {
266277
// If the node is already the head, do nothing
267278
return Ok(());
@@ -274,14 +285,14 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
274285
/// Helper function to evict the least-recently used node, which is the
275286
/// tail of the linked list
276287
/// Returns the index of the evicted node
277-
fn evict_lru(&mut self) -> Result<usize, ()> {
288+
fn evict_lru(&mut self) -> Result<usize, LruCacheCorrupted> {
278289
let index = self.tail;
279290
if index == self.capacity {
280291
// If the list is empty, do nothing
281292
return Ok(self.capacity);
282293
}
283294
self.detach_node(index)?;
284-
let node = self.order.get(index).ok_or(())?;
295+
let node = self.order.get(index).ok_or(LruCacheCorrupted)?;
285296
self.cache.remove(&node.key);
286297
Ok(index)
287298
}

0 commit comments

Comments
 (0)