@@ -40,6 +40,17 @@ impl<K: Display, V: Display> Display for Node<K, V> {
40
40
}
41
41
}
42
42
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
+
43
54
/// LRU cache
44
55
pub struct LruCache < K , V > {
45
56
capacity : usize ,
@@ -92,10 +103,10 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
92
103
93
104
/// Get the value for the given key
94
105
/// 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 > {
96
107
if let Some ( & index) = self . cache . get ( key) {
97
108
self . move_to_head ( index) ?;
98
- let node = self . order . get ( index) . ok_or ( ( ) ) ?;
109
+ let node = self . order . get ( index) . ok_or ( LruCacheCorrupted ) ?;
99
110
Ok ( Some ( node. value ) )
100
111
} else {
101
112
Ok ( None )
@@ -105,14 +116,14 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
105
116
/// Insert a key-value pair into the cache, marking it as dirty.
106
117
/// Returns an error iff the cache is corrupted and should be discarded
107
118
/// 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 > {
109
120
self . insert_with_dirty ( key, value, true )
110
121
}
111
122
112
123
/// Insert a key-value pair into the cache, marking it as clean.
113
124
/// Returns an error iff the cache is corrupted and should be discarded
114
125
/// 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 > {
116
127
self . insert_with_dirty ( key, value, false )
117
128
}
118
129
@@ -124,10 +135,10 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
124
135
key : K ,
125
136
value : V ,
126
137
dirty : bool ,
127
- ) -> Result < Option < ( K , V ) > , ( ) > {
138
+ ) -> Result < Option < ( K , V ) > , LruCacheCorrupted > {
128
139
if let Some ( & index) = self . cache . get ( & key) {
129
140
// 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 ) ?;
131
142
node. value = value;
132
143
node. dirty = dirty;
133
144
self . move_to_head ( index) ?;
@@ -139,7 +150,7 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
139
150
// We've reached capacity. Evict the least-recently used value
140
151
// and reuse its node
141
152
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 ) ?;
143
154
144
155
// Replace the key with the new key, saving the old key
145
156
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> {
186
197
pub fn flush < E > (
187
198
& mut self ,
188
199
mut f : impl FnMut ( & K , V ) -> Result < ( ) , E > ,
189
- ) -> Result < Result < ( ) , E > , ( ) > {
200
+ ) -> Result < Result < ( ) , E > , LruCacheCorrupted > {
190
201
let mut current = self . head ;
191
202
192
203
// Keep track of visited nodes to detect cycles
@@ -195,10 +206,10 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
195
206
while current != self . capacity {
196
207
// Detect cycles
197
208
if !visited. insert ( current) {
198
- return Err ( ( ) ) ;
209
+ return Err ( LruCacheCorrupted ) ;
199
210
}
200
211
201
- let node = self . order . get_mut ( current) . ok_or ( ( ) ) ?;
212
+ let node = self . order . get_mut ( current) . ok_or ( LruCacheCorrupted ) ?;
202
213
let next = node. next ;
203
214
if node. dirty {
204
215
let value = node. value ;
@@ -216,8 +227,8 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
216
227
}
217
228
218
229
/// 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 ) ?;
221
232
let prev = node. prev ;
222
233
let next = node. next ;
223
234
@@ -226,7 +237,7 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
226
237
self . tail = prev;
227
238
} else {
228
239
// 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 ) ?;
230
241
next_node. prev = prev;
231
242
}
232
243
@@ -235,22 +246,22 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
235
246
self . head = next;
236
247
} else {
237
248
// 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 ) ?;
239
250
prev_node. next = next;
240
251
}
241
252
242
253
Ok ( ( ) )
243
254
}
244
255
245
256
/// 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 ) ?;
248
259
node. prev = self . capacity ;
249
260
node. next = self . head ;
250
261
251
262
if self . head != self . capacity {
252
263
// 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 ) ?;
254
265
head_node. prev = index;
255
266
} else {
256
267
// 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> {
261
272
}
262
273
263
274
/// 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 > {
265
276
if index == self . head {
266
277
// If the node is already the head, do nothing
267
278
return Ok ( ( ) ) ;
@@ -274,14 +285,14 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
274
285
/// Helper function to evict the least-recently used node, which is the
275
286
/// tail of the linked list
276
287
/// Returns the index of the evicted node
277
- fn evict_lru ( & mut self ) -> Result < usize , ( ) > {
288
+ fn evict_lru ( & mut self ) -> Result < usize , LruCacheCorrupted > {
278
289
let index = self . tail ;
279
290
if index == self . capacity {
280
291
// If the list is empty, do nothing
281
292
return Ok ( self . capacity ) ;
282
293
}
283
294
self . detach_node ( index) ?;
284
- let node = self . order . get ( index) . ok_or ( ( ) ) ?;
295
+ let node = self . order . get ( index) . ok_or ( LruCacheCorrupted ) ?;
285
296
self . cache . remove ( & node. key ) ;
286
297
Ok ( index)
287
298
}
0 commit comments