1use alloy_primitives::map::DefaultHashBuilder;
4use core::hash::BuildHasher;
5use itertools::Itertools;
6use std::{fmt, hash::Hash};
7
8pub use reth_lru::LruMap;
9
10pub struct LruCache<T: Hash + Eq + fmt::Debug, S = DefaultHashBuilder>
15where
16 S: BuildHasher,
17{
18 inner: LruMap<T, (), S>,
19}
20
21impl<T: Hash + Eq + fmt::Debug> LruCache<T> {
22 pub fn new(limit: u32) -> Self {
24 Self::with_hasher(limit, Default::default())
25 }
26}
27
28impl<T: Hash + Eq + fmt::Debug, S: BuildHasher> LruCache<T, S> {
29 pub const fn with_hasher(limit: u32, hash_builder: S) -> Self {
31 Self { inner: LruMap::with_hasher(limit, hash_builder) }
32 }
33
34 pub fn insert(&mut self, entry: T) -> bool {
45 let mut is_new = false;
46 self.inner.get_or_insert(entry, || {
47 is_new = true;
48 });
49 is_new
50 }
51
52 pub fn insert_and_get_evicted(&mut self, entry: T) -> (bool, Option<T>) {
55 if self.inner.get(&entry).is_some() {
56 return (false, None)
57 }
58 (true, self.inner.insert(entry, ()).map(|(evicted, ())| evicted))
59 }
60
61 pub fn get(&mut self, entry: &T) -> Option<&T> {
63 self.inner.get(entry)?;
64 self.inner.peek_newest().map(|(key, ())| key)
65 }
66
67 pub fn find(&self, entry: &T) -> Option<&T> {
74 self.iter().find(|key| *key == entry)
75 }
76
77 pub fn remove(&mut self, value: &T) -> bool {
79 self.inner.remove(value).is_some()
80 }
81
82 pub fn contains(&self, value: &T) -> bool {
84 self.inner.contains(value)
85 }
86
87 pub fn iter(&self) -> impl Iterator<Item = &T> + '_ {
89 self.inner.iter().map(|(k, ())| k)
90 }
91
92 pub const fn len(&self) -> usize {
94 self.inner.len()
95 }
96
97 pub const fn is_empty(&self) -> bool {
99 self.inner.is_empty()
100 }
101}
102
103impl<T, S> Extend<T> for LruCache<T, S>
104where
105 T: Eq + Hash + fmt::Debug,
106 S: BuildHasher,
107{
108 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
109 for item in iter {
110 _ = self.insert(item);
111 }
112 }
113}
114
115impl<T, S> fmt::Debug for LruCache<T, S>
116where
117 T: fmt::Debug + Hash + Eq,
118 S: BuildHasher,
119{
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 let mut debug_struct = f.debug_struct("LruCache");
122
123 debug_struct.field("limit", &self.inner.max_length());
124
125 debug_struct.field(
126 "ret %iter",
127 &format_args!("Iter: {{{} }}", self.iter().map(|k| format!(" {k:?}")).format(",")),
128 );
129
130 debug_struct.finish()
131 }
132}
133
134#[cfg(test)]
135mod test {
136 use super::*;
137 use derive_more::Constructor;
138 use std::hash::Hasher;
139
140 #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
141 struct Key(i8);
142
143 #[derive(Debug, Eq, Constructor, Clone, Copy)]
144 struct CompoundKey {
145 id: i8,
147 other: i8,
148 }
149
150 impl PartialEq for CompoundKey {
151 fn eq(&self, other: &Self) -> bool {
152 self.id == other.id
153 }
154 }
155
156 impl Hash for CompoundKey {
157 fn hash<H: Hasher>(&self, state: &mut H) {
158 self.id.hash(state)
159 }
160 }
161
162 #[test]
163 fn test_cache_should_insert_into_empty_set() {
164 let mut cache = LruCache::new(5);
165 let entry = "entry";
166 assert!(cache.insert(entry));
167 assert!(cache.contains(&entry));
168 }
169
170 #[test]
171 fn test_cache_should_not_insert_same_value_twice() {
172 let mut cache = LruCache::new(5);
173 let entry = "entry";
174 assert!(cache.insert(entry));
175 assert!(!cache.insert(entry));
176 }
177
178 #[test]
179 fn test_cache_should_remove_oldest_element_when_exceeding_limit() {
180 let mut cache = LruCache::new(1);
181 let old_entry = "old_entry";
182 let new_entry = "new_entry";
183 cache.insert(old_entry);
184 cache.insert("entry");
185 cache.insert(new_entry);
186 assert!(cache.contains(&new_entry));
187 assert!(!cache.contains(&old_entry));
188 }
189
190 #[test]
191 fn test_cache_should_extend_an_array() {
192 let mut cache = LruCache::new(5);
193 let entries = ["some_entry", "another_entry"];
194 cache.extend(entries);
195 for e in entries {
196 assert!(cache.contains(&e));
197 }
198 }
199
200 #[test]
201 #[expect(dead_code)]
202 fn test_debug_impl_lru_map() {
203 #[derive(Debug)]
204 struct Value(i8);
205
206 let mut cache = LruMap::new(2);
207 let key_1 = Key(1);
208 let value_1 = Value(11);
209 cache.insert(key_1, value_1);
210 let key_2 = Key(2);
211 let value_2 = Value(22);
212 cache.insert(key_2, value_2);
213
214 assert_eq!("LruMap { len: 2, max_length: 2, .. }", format!("{cache:?}"))
215 }
216
217 #[test]
218 fn test_debug_impl_lru_cache() {
219 let mut cache = LruCache::new(2);
220 let key_1 = Key(1);
221 cache.insert(key_1);
222 let key_2 = Key(2);
223 cache.insert(key_2);
224
225 assert_eq!(
226 "LruCache { limit: 2, ret %iter: Iter: { Key(2), Key(1) } }",
227 format!("{cache:?}")
228 )
229 }
230
231 #[test]
232 fn get() {
233 let mut cache = LruCache::new(2);
234 let key_1 = Key(1);
235 cache.insert(key_1);
236 let key_2 = Key(2);
237 cache.insert(key_2);
238
239 _ = cache.get(&key_1);
241
242 assert_eq!(
243 "LruCache { limit: 2, ret %iter: Iter: { Key(1), Key(2) } }",
244 format!("{cache:?}")
245 )
246 }
247
248 #[test]
249 fn get_ty_custom_eq_impl() {
250 let mut cache = LruCache::new(2);
251 let key_1 = CompoundKey::new(1, 11);
252 cache.insert(key_1);
253 let key_2 = CompoundKey::new(2, 22);
254 cache.insert(key_2);
255
256 let key = cache.get(&key_1);
257
258 assert_eq!(key_1.other, key.unwrap().other)
259 }
260
261 #[test]
262 fn peek() {
263 let mut cache = LruCache::new(2);
264 let key_1 = Key(1);
265 cache.insert(key_1);
266 let key_2 = Key(2);
267 cache.insert(key_2);
268
269 _ = cache.find(&key_1);
271
272 assert_eq!(
273 "LruCache { limit: 2, ret %iter: Iter: { Key(2), Key(1) } }",
274 format!("{cache:?}")
275 )
276 }
277
278 #[test]
279 fn peek_ty_custom_eq_impl() {
280 let mut cache = LruCache::new(2);
281 let key_1 = CompoundKey::new(1, 11);
282 cache.insert(key_1);
283 let key_2 = CompoundKey::new(2, 22);
284 cache.insert(key_2);
285
286 let key = cache.find(&key_1);
287
288 assert_eq!(key_1.other, key.unwrap().other)
289 }
290
291 #[test]
292 fn test_insert_methods() {
293 let mut cache = LruCache::new(2);
294
295 assert!(cache.insert("first")); assert!(!cache.insert("first")); assert!(cache.insert("second")); let (is_new, evicted) = cache.insert_and_get_evicted("third");
302 assert!(is_new); assert_eq!(evicted, Some("first")); assert!(cache.contains(&"second"));
306 assert!(cache.contains(&"third"));
307 assert!(!cache.contains(&"first"));
308
309 let (is_new, evicted) = cache.insert_and_get_evicted("second");
311 assert!(!is_new); assert_eq!(evicted, None); }
314}