@@ -83,30 +83,9 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
83
83
84
84
/// Get the value for the given key
85
85
pub fn get ( & mut self , key : & K ) -> Option < V > {
86
- if let Some ( node) = self . cache . get ( key) {
87
- // Move the node to the head of the LRU list
88
- let node = * node;
89
-
90
- if node != self . head {
91
- let prev = self . order [ node] . prev ;
92
- let next = self . order [ node] . next ;
93
-
94
- if node == self . tail {
95
- // If this is the tail, update the tail
96
- self . tail = prev;
97
- } else {
98
- // Else, update the next node's prev pointer
99
- self . order [ next] . prev = prev;
100
- }
101
-
102
- self . order [ prev] . next = next;
103
- self . order [ node] . prev = self . capacity ;
104
- self . order [ node] . next = self . head ;
105
- self . order [ self . head ] . prev = node;
106
- self . head = node;
107
- }
108
-
109
- Some ( self . order [ node] . value )
86
+ if let Some ( & index) = self . cache . get ( key) {
87
+ self . move_to_head ( index) ;
88
+ Some ( self . order [ index] . value )
110
89
} else {
111
90
None
112
91
}
@@ -127,75 +106,60 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
127
106
/// Insert a key-value pair into the cache
128
107
/// Returns `Some((K, V))` if a dirty value was evicted.
129
108
pub fn insert_with_dirty ( & mut self , key : K , value : V , dirty : bool ) -> Option < ( K , V ) > {
130
- let mut evicted = None ;
131
- if let Some ( node) = self . cache . get ( & key) {
132
- // Update the value for the key
133
- let node = * node;
134
- self . order [ node] . value = value;
135
- self . order [ node] . dirty = dirty;
136
-
137
- // Just call get to handle updating the LRU list
138
- self . get ( & key) ;
109
+ if let Some ( & index) = self . cache . get ( & key) {
110
+ // Update an existing node
111
+ self . order [ index] . value = value;
112
+ self . order [ index] . dirty = dirty;
113
+ self . move_to_head ( index) ;
114
+ None
139
115
} else {
116
+ let mut evicted = None ;
117
+ // This is a new key
140
118
let index = if self . cache . len ( ) == self . capacity {
141
- // Take the place of the least recently used element.
142
- // First, remove it from the tail of the LRU list
143
- let index = self . tail ;
144
- let prev = self . order [ index] . prev ;
145
- self . order [ prev] . next = self . capacity ;
146
- self . tail = prev;
147
-
148
- // Remove it from the cache
149
- self . cache . remove ( & self . order [ index] . key ) ;
119
+ // We've reached capacity. Evict the least-recently used value
120
+ // and reuse its node
121
+ let index = self . evict_lru ( ) ;
150
122
151
123
// Replace the key with the new key, saving the old key
152
124
let replaced_key = std:: mem:: replace ( & mut self . order [ index] . key , key. clone ( ) ) ;
153
125
154
- // If it is dirty, save the key-value pair to return
126
+ // Save the evicted key-value pair, if it was dirty
155
127
if self . order [ index] . dirty {
156
128
evicted = Some ( ( replaced_key, self . order [ index] . value ) ) ;
157
- }
158
-
159
- // Insert this new value into the cache
160
- self . cache . insert ( key, index) ;
129
+ } ;
161
130
162
- // Update the node with the new key-value pair, inserting it at
163
- // the head of the LRU list
131
+ // Update the evicted node with the new key-value pair
164
132
self . order [ index] . value = value;
165
133
self . order [ index] . dirty = dirty;
166
- self . order [ index] . next = self . head ;
167
- self . order [ index] . prev = self . capacity ;
134
+
135
+ // Insert the new key-value pair into the cache
136
+ self . cache . insert ( key. clone ( ) , index) ;
168
137
169
138
index
170
139
} else {
171
- // Insert a new key-value pair
140
+ // Create a new node, add it to the cache
141
+ let index = self . order . len ( ) ;
172
142
let node = Node {
173
143
key : key. clone ( ) ,
174
144
value,
175
145
dirty,
176
- next : self . head ,
146
+ next : self . capacity ,
177
147
prev : self . capacity ,
178
148
} ;
179
-
180
- let index = self . order . len ( ) ;
181
149
self . order . push ( node) ;
182
150
self . cache . insert ( key, index) ;
183
-
184
151
index
185
152
} ;
186
153
187
- // Put it at the head of the LRU list
188
- if self . head != self . capacity {
189
- self . order [ self . head ] . prev = index;
190
- } else {
191
- self . tail = index;
192
- }
154
+ // Put the new or reused node at the head of the LRU list
155
+ self . attach_as_head ( index) ;
193
156
194
- self . head = index ;
157
+ evicted
195
158
}
196
- evicted
197
159
}
198
160
161
+ /// Flush all dirty values in the cache, calling the given function, `f`,
162
+ /// for each dirty value.
199
163
pub fn flush < E > ( & mut self , mut f : impl FnMut ( & K , V ) -> Result < ( ) , E > ) -> Result < ( ) , E > {
200
164
let mut index = self . head ;
201
165
while index != self . capacity {
@@ -209,6 +173,72 @@ impl<K: Eq + std::hash::Hash + Clone, V: Copy> LruCache<K, V> {
209
173
}
210
174
Ok ( ( ) )
211
175
}
176
+
177
+ /// Helper function to remove a node from the linked list (by index)
178
+ fn detach_node ( & mut self , index : usize ) {
179
+ if index >= self . order . len ( ) {
180
+ return ;
181
+ }
182
+
183
+ let prev = self . order [ index] . prev ;
184
+ let next = self . order [ index] . next ;
185
+
186
+ if index == self . tail {
187
+ // If this is the last node, update the tail to point to its previous node
188
+ self . tail = prev;
189
+ } else {
190
+ // Else, update the next node to point to the previous node
191
+ self . order [ next] . prev = prev;
192
+ }
193
+
194
+ if index == self . head {
195
+ // If this is the first node, update the head to point to the next node
196
+ self . head = next;
197
+ } else {
198
+ // Else, update the previous node to point to the next node
199
+ self . order [ prev] . next = next;
200
+ }
201
+ }
202
+
203
+ /// Helper function to attach a node as the head of the linked list
204
+ fn attach_as_head ( & mut self , index : usize ) {
205
+ self . order [ index] . prev = self . capacity ;
206
+ self . order [ index] . next = self . head ;
207
+
208
+ if self . head != self . capacity {
209
+ // If there is a head, update its previous pointer to this one
210
+ self . order [ self . head ] . prev = index;
211
+ } else {
212
+ // Else, the list was empty, so update the tail
213
+ self . tail = index;
214
+ }
215
+ self . head = index;
216
+ }
217
+
218
+ /// Helper function to move a node to the head of the linked list
219
+ fn move_to_head ( & mut self , index : usize ) {
220
+ if index == self . head {
221
+ // If the node is already the head, do nothing
222
+ return ;
223
+ }
224
+
225
+ self . detach_node ( index) ;
226
+ self . attach_as_head ( index) ;
227
+ }
228
+
229
+ /// Helper function to evict the least-recently used node, which is the
230
+ /// tail of the linked list
231
+ /// Returns the index of the evicted node
232
+ fn evict_lru ( & mut self ) -> usize {
233
+ let index = self . tail ;
234
+ if index == self . capacity {
235
+ // If the list is empty, do nothing
236
+ return self . capacity ;
237
+ }
238
+ self . detach_node ( index) ;
239
+ self . cache . remove ( & self . order [ index] . key ) ;
240
+ index
241
+ }
212
242
}
213
243
214
244
#[ cfg( test) ]
0 commit comments