@@ -12,19 +12,40 @@ use likely_stable::unlikely;
1212const MAX_CAPACITY : u16 = u16:: MAX - 1 ;
1313
1414/// Intrusive node stored in the TinyVec/heap storage.
15+ ///
16+ /// Contains the key-value pair and doubly-linked list pointers for LRU ordering.
1517#[ derive( Default , Clone ) ]
1618pub struct Entry < K , V >
1719where
1820 K : Default + Clone ,
1921 V : Default ,
2022{
23+ /// The cache key
2124 pub key : K ,
25+ /// The cached value
2226 pub val : V ,
27+ /// Next entry index in LRU order (u16::MAX if tail)
2328 pub next : u16 ,
29+ /// Previous entry index in LRU order (u16::MAX if head)
2430 pub prev : u16 ,
2531}
2632
2733/// LRU cache with inline-then-spill storage.
34+ ///
35+ /// For small working sets (≤ N), entries are stored inline on the stack for maximum performance.
36+ /// Once capacity exceeds N, it transparently spills to heap-backed storage with O(1) operations.
37+ ///
38+ /// # Example
39+ /// ```
40+ /// use tiny_lru::TinyLru;
41+ ///
42+ /// let mut cache = TinyLru::<i32, &str, 4>::new();
43+ /// cache.push(1, "value");
44+ /// cache.push(2, "another");
45+ ///
46+ /// assert_eq!(cache.get(&1), Some(&"value"));
47+ /// assert_eq!(cache.len(), 2);
48+ /// ```
2849#[ derive( Clone ) ]
2950pub struct TinyLru < K , V , const N : usize >
3051where
6081 K : Eq + Hash + Default + Clone ,
6182 V : Default ,
6283{
63- /// Create with capacity = N.
84+ /// Create a new cache with capacity = N.
85+ ///
86+ /// # Example
87+ /// ```
88+ /// use tiny_lru::TinyLru;
89+ ///
90+ /// let cache = TinyLru::<i32, &str, 8>::new();
91+ /// assert_eq!(cache.capacity(), 8);
92+ /// ```
6493 #[ inline]
6594 pub fn new ( ) -> Self {
6695 assert_capacity_limit :: < N > ( ) ;
@@ -74,7 +103,18 @@ where
74103 }
75104 }
76105
77- /// Create with specified capacity (must be >= N).
106+ /// Create a new cache with specified capacity (must be >= N).
107+ ///
108+ /// # Panics
109+ /// Panics if `cap < N`.
110+ ///
111+ /// # Example
112+ /// ```
113+ /// use tiny_lru::TinyLru;
114+ ///
115+ /// let cache = TinyLru::<i32, &str, 4>::with_capacity(16);
116+ /// assert_eq!(cache.capacity(), 16);
117+ /// ```
78118 #[ inline]
79119 pub fn with_capacity ( cap : u16 ) -> Self {
80120 assert_capacity_limit :: < N > ( ) ;
@@ -91,7 +131,22 @@ where
91131 }
92132 }
93133
94- /// Insert or update; promotes on hit.
134+ /// Insert or update a key-value pair, promoting to MRU on hit.
135+ ///
136+ /// If the key already exists, updates the value and promotes to most recently used.
137+ /// If the cache is at capacity, removes the least recently used entry first.
138+ ///
139+ /// # Example
140+ /// ```
141+ /// use tiny_lru::TinyLru;
142+ ///
143+ /// let mut cache = TinyLru::<i32, String, 2>::new();
144+ /// cache.push(1, "first".to_string());
145+ /// cache.push(2, "second".to_string());
146+ /// cache.push(1, "updated".to_string()); // Updates existing key
147+ ///
148+ /// assert_eq!(cache.get(&1), Some(&"updated".to_string()));
149+ /// ```
95150 #[ inline]
96151 pub fn push ( & mut self , key : K , value : V ) {
97152 // If key exists: update value and promote to MRU
@@ -113,7 +168,22 @@ where
113168 self . insert ( key, value) ;
114169 }
115170
116- /// Pop and return the LRU entry.
171+ /// Remove and return the least recently used entry.
172+ ///
173+ /// Returns `None` if the cache is empty.
174+ ///
175+ /// # Example
176+ /// ```
177+ /// use tiny_lru::TinyLru;
178+ ///
179+ /// let mut cache = TinyLru::<i32, String, 2>::new();
180+ /// cache.push(1, "first".to_string());
181+ /// cache.push(2, "second".to_string());
182+ ///
183+ /// let (key, value) = cache.pop().unwrap();
184+ /// assert_eq!(key, 1); // LRU entry
185+ /// assert_eq!(value, "first");
186+ /// ```
117187 #[ inline]
118188 pub fn pop ( & mut self ) -> Option < ( K , V ) > {
119189 if self . is_empty ( ) {
@@ -167,7 +237,20 @@ where
167237 Some ( ( key, value) )
168238 }
169239
170- /// Get by key, promoting to MRU on hit.
240+ /// Get a value by key, promoting to MRU on hit.
241+ ///
242+ /// Returns `None` if the key is not found.
243+ ///
244+ /// # Example
245+ /// ```
246+ /// use tiny_lru::TinyLru;
247+ ///
248+ /// let mut cache = TinyLru::<i32, String, 2>::new();
249+ /// cache.push(1, "value".to_string());
250+ ///
251+ /// assert_eq!(cache.get(&1), Some(&"value".to_string()));
252+ /// assert_eq!(cache.get(&2), None);
253+ /// ```
171254 #[ inline]
172255 pub fn get ( & mut self , key : & K ) -> Option < & V > {
173256 if let Some ( index) = self . find_key_index ( key) {
@@ -178,7 +261,22 @@ where
178261 }
179262 }
180263
181- /// Get mutable by key, promoting to MRU on hit.
264+ /// Get a mutable reference by key, promoting to MRU on hit.
265+ ///
266+ /// Returns `None` if the key is not found.
267+ ///
268+ /// # Example
269+ /// ```
270+ /// use tiny_lru::TinyLru;
271+ ///
272+ /// let mut cache = TinyLru::<i32, String, 2>::new();
273+ /// cache.push(1, "value".to_string());
274+ ///
275+ /// if let Some(val) = cache.get_mut(&1) {
276+ /// val.push_str(" updated");
277+ /// }
278+ /// assert_eq!(cache.get(&1), Some(&"value updated".to_string()));
279+ /// ```
182280 #[ inline]
183281 pub fn get_mut ( & mut self , key : & K ) -> Option < & mut V > {
184282 if let Some ( index) = self . find_key_index ( key) {
@@ -189,13 +287,44 @@ where
189287 }
190288 }
191289
192- /// Peek without promotion.
290+ /// Peek at a value by key without promoting to MRU.
291+ ///
292+ /// Returns `None` if the key is not found. Unlike `get()`, this does not
293+ /// affect the LRU ordering.
294+ ///
295+ /// # Example
296+ /// ```
297+ /// use tiny_lru::TinyLru;
298+ ///
299+ /// let mut cache = TinyLru::<i32, String, 2>::new();
300+ /// cache.push(1, "first".to_string());
301+ /// cache.push(2, "second".to_string());
302+ ///
303+ /// // Peek doesn't change LRU order
304+ /// assert_eq!(cache.peek(&1), Some(&"first".to_string()));
305+ /// cache.pop(); // Still removes key 1 (LRU)
306+ /// ```
193307 #[ inline]
194308 pub fn peek ( & self , key : & K ) -> Option < & V > {
195309 self . find_key_index ( key) . map ( |index| & self . store [ index] . val )
196310 }
197311
198- /// Remove by key and return owned pair.
312+ /// Remove a key-value pair and return the owned values.
313+ ///
314+ /// Returns `None` if the key is not found.
315+ ///
316+ /// # Example
317+ /// ```
318+ /// use tiny_lru::TinyLru;
319+ ///
320+ /// let mut cache = TinyLru::<i32, String, 2>::new();
321+ /// cache.push(1, "value".to_string());
322+ ///
323+ /// let (key, value) = cache.remove(&1).unwrap();
324+ /// assert_eq!(key, 1);
325+ /// assert_eq!(value, "value");
326+ /// assert!(cache.is_empty());
327+ /// ```
199328 #[ inline]
200329 pub fn remove ( & mut self , key : & K ) -> Option < ( K , V ) > {
201330 // Find the key index
@@ -250,7 +379,20 @@ where
250379 Some ( ( key, value) )
251380 }
252381
253- /// Clear all entries.
382+ /// Clear all entries from the cache.
383+ ///
384+ /// Resets the cache to its initial state, including returning to pre-spill
385+ /// mode if it was previously spilled to heap.
386+ ///
387+ /// # Example
388+ /// ```
389+ /// use tiny_lru::TinyLru;
390+ ///
391+ /// let mut cache = TinyLru::<i32, String, 2>::new();
392+ /// cache.push(1, "value".to_string());
393+ /// cache.clear();
394+ /// assert!(cache.is_empty());
395+ /// ```
254396 #[ inline]
255397 pub fn clear ( & mut self ) {
256398 // Clear the store efficiently
@@ -264,7 +406,19 @@ where
264406 self . index = None ;
265407 }
266408
267- /// Adjust capacity. Requires new_cap > size and new_cap >= N.
409+ /// Adjust the cache capacity.
410+ ///
411+ /// # Panics
412+ /// Panics if `new_cap <= current_size` or `new_cap < N`.
413+ ///
414+ /// # Example
415+ /// ```
416+ /// use tiny_lru::TinyLru;
417+ ///
418+ /// let mut cache = TinyLru::<i32, String, 4>::new();
419+ /// cache.set_capacity(16);
420+ /// assert_eq!(cache.capacity(), 16);
421+ /// ```
268422 #[ inline]
269423 pub fn set_capacity ( & mut self , new_cap : u16 ) {
270424 // Validate requirements
@@ -276,44 +430,129 @@ where
276430 self . capacity = new_cap;
277431 }
278432
279- /// Current number of items.
433+ /// Returns the current number of items in the cache.
434+ ///
435+ /// # Example
436+ /// ```
437+ /// use tiny_lru::TinyLru;
438+ ///
439+ /// let mut cache = TinyLru::<i32, String, 4>::new();
440+ /// assert_eq!(cache.len(), 0);
441+ /// cache.push(1, "value".to_string());
442+ /// assert_eq!(cache.len(), 1);
443+ /// ```
280444 #[ inline]
281445 pub fn len ( & self ) -> u16 {
282446 self . store . len ( ) as u16
283447 }
284448
285- /// Whether the cache is empty.
449+ /// Returns `true` if the cache contains no items.
450+ ///
451+ /// # Example
452+ /// ```
453+ /// use tiny_lru::TinyLru;
454+ ///
455+ /// let mut cache = TinyLru::<i32, String, 4>::new();
456+ /// assert!(cache.is_empty());
457+ /// cache.push(1, "value".to_string());
458+ /// assert!(!cache.is_empty());
459+ /// ```
286460 #[ inline]
287461 pub fn is_empty ( & self ) -> bool {
288462 self . store . is_empty ( )
289463 }
290464
291- /// Current capacity.
465+ /// Returns the current capacity of the cache.
466+ ///
467+ /// # Example
468+ /// ```
469+ /// use tiny_lru::TinyLru;
470+ ///
471+ /// let cache = TinyLru::<i32, String, 8>::new();
472+ /// assert_eq!(cache.capacity(), 8);
473+ /// ```
292474 #[ inline]
293475 pub fn capacity ( & self ) -> u16 {
294476 self . capacity
295477 }
296478
297- /// Contains key.
479+ /// Returns `true` if the cache contains the specified key.
480+ ///
481+ /// # Example
482+ /// ```
483+ /// use tiny_lru::TinyLru;
484+ ///
485+ /// let mut cache = TinyLru::<i32, String, 4>::new();
486+ /// cache.push(1, "value".to_string());
487+ ///
488+ /// assert!(cache.contains_key(&1));
489+ /// assert!(!cache.contains_key(&2));
490+ /// ```
298491 #[ inline]
299492 pub fn contains_key ( & self , key : & K ) -> bool {
300493 self . find_key_index ( key) . is_some ( )
301494 }
302495
303- /// Check if the cache is currently spilled to heap.
496+ /// Returns `true` if the cache has spilled to heap-backed storage.
497+ ///
498+ /// This happens when the cache size exceeds the inline capacity N.
499+ ///
500+ /// # Example
501+ /// ```
502+ /// use tiny_lru::TinyLru;
503+ ///
504+ /// let mut cache = TinyLru::<i32, String, 2>::with_capacity(4);
505+ /// assert!(!cache.is_spilled());
506+ ///
507+ /// cache.push(1, "a".to_string());
508+ /// cache.push(2, "b".to_string());
509+ /// cache.push(3, "c".to_string()); // Triggers spill
510+ /// assert!(cache.is_spilled());
511+ /// ```
304512 #[ inline]
305513 pub fn is_spilled ( & self ) -> bool {
306514 self . index . is_some ( )
307515 }
308516
309- /// Check if unspill is currently possible.
517+ /// Returns `true` if the cache can be unspilled back to inline storage.
518+ ///
519+ /// This is possible when the cache is spilled but the current size ≤ N.
520+ ///
521+ /// # Example
522+ /// ```
523+ /// use tiny_lru::TinyLru;
524+ ///
525+ /// let mut cache = TinyLru::<i32, String, 2>::with_capacity(4);
526+ /// cache.push(1, "a".to_string());
527+ /// cache.push(2, "b".to_string());
528+ /// cache.push(3, "c".to_string()); // Spills
529+ /// cache.pop(); // Size back to 2
530+ ///
531+ /// assert!(cache.can_unspill());
532+ /// ```
310533 #[ inline]
311534 pub fn can_unspill ( & self ) -> bool {
312535 self . is_spilled ( ) && self . store . len ( ) <= N
313536 }
314537
315538 /// Attempt to unspill from heap back to inline storage.
316- /// Returns true if unspill was successful, false if not possible.
539+ ///
540+ /// Returns `true` if unspill was successful, `false` if not possible.
541+ /// This can free heap memory when the cache size drops below N.
542+ ///
543+ /// # Example
544+ /// ```
545+ /// use tiny_lru::TinyLru;
546+ ///
547+ /// let mut cache = TinyLru::<i32, String, 2>::with_capacity(4);
548+ /// cache.push(1, "a".to_string());
549+ /// cache.push(2, "b".to_string());
550+ /// cache.push(3, "c".to_string()); // Spills
551+ /// cache.pop(); // Size back to 2
552+ ///
553+ /// assert!(cache.unspill()); // Returns to inline storage
554+ /// assert!(!cache.is_spilled());
555+ /// ```
317556 #[ inline]
318557 pub fn unspill ( & mut self ) -> bool {
319558 if !self . can_unspill ( ) {
0 commit comments