Skip to content

Commit 37b59b6

Browse files
authored
docs: enhance NRU cache documentation and implement iterators (#74)
* docs: enhance NRU cache documentation and implement iterators Improved the documentation for `NruCache`, including detailed examples for public methods such as `is_empty`, `iter`, `iter_mut`, `contains`, `len`, `capacity`, `clear`, and `metrics_snapshot`. Added iterator implementations for shared and mutable references, enhancing usability and providing a more intuitive experience when working with the NRU cache policy. This change aims to improve clarity and consistency in the API documentation. * docs: update NRU cache documentation to reference ConcurrentLruCache Revised the NRU cache documentation to clarify the alternative for strict LRU ordering, now pointing to [`ConcurrentLruCache`] instead of [`LruCache`]. This change enhances the accuracy of the documentation and aligns it with the current API structure, ensuring users have the correct guidance when selecting cache policies.
1 parent fc8a111 commit 37b59b6

1 file changed

Lines changed: 295 additions & 7 deletions

File tree

src/policy/nru.rs

Lines changed: 295 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@
120120
//! - Access patterns have temporal locality
121121
//!
122122
//! **Avoid NRU when:**
123-
//! - You need strict LRU ordering (use LRU)
124-
//! - You need O(1) eviction guarantees (use Clock with hand)
125-
//! - You need scan resistance (use S3-FIFO, LRU-K)
123+
//! - You need strict LRU ordering (use [`ConcurrentLruCache`](super::lru::ConcurrentLruCache))
124+
//! - You need O(1) eviction guarantees (use [`ClockCache`](super::clock::ClockCache))
125+
//! - You need scan resistance (use [`S3FifoCache`](super::s3_fifo::S3FifoCache),
126+
//! [`LrukCache`](super::lru_k::LrukCache))
126127
//!
127128
//! ## Example Usage
128129
//!
@@ -225,10 +226,7 @@ struct Entry<V> {
225226
/// # Implementation
226227
///
227228
/// Uses HashMap + Vec for O(1) access with swap-remove for eviction.
228-
pub struct NruCache<K, V>
229-
where
230-
K: Clone + Eq + Hash,
231-
{
229+
pub struct NruCache<K, V> {
232230
/// HashMap for O(1) key lookup
233231
map: FxHashMap<K, Entry<V>>,
234232
/// Dense array of keys for eviction scanning
@@ -267,11 +265,74 @@ where
267265
}
268266

269267
/// Returns `true` if the cache is empty.
268+
///
269+
/// # Example
270+
///
271+
/// ```
272+
/// use cachekit::policy::nru::NruCache;
273+
/// use cachekit::traits::{CoreCache, ReadOnlyCache};
274+
///
275+
/// let mut cache = NruCache::<&str, i32>::new(10);
276+
/// assert!(cache.is_empty());
277+
///
278+
/// cache.insert("a", 1);
279+
/// assert!(!cache.is_empty());
280+
/// ```
270281
#[inline]
271282
pub fn is_empty(&self) -> bool {
272283
self.map.is_empty()
273284
}
274285

286+
/// Returns an iterator over all key-value pairs in the cache.
287+
///
288+
/// Iteration order is unspecified.
289+
///
290+
/// # Example
291+
///
292+
/// ```
293+
/// use cachekit::policy::nru::NruCache;
294+
/// use cachekit::traits::CoreCache;
295+
///
296+
/// let mut cache = NruCache::new(10);
297+
/// cache.insert("a", 1);
298+
/// cache.insert("b", 2);
299+
///
300+
/// let pairs: Vec<_> = cache.iter().collect();
301+
/// assert_eq!(pairs.len(), 2);
302+
/// ```
303+
#[inline]
304+
pub fn iter(&self) -> Iter<'_, K, V> {
305+
Iter {
306+
inner: self.map.iter(),
307+
}
308+
}
309+
310+
/// Returns a mutable iterator over all key-value pairs in the cache.
311+
///
312+
/// Only values are mutable; keys and eviction metadata are not exposed.
313+
/// Iteration order is unspecified.
314+
///
315+
/// # Example
316+
///
317+
/// ```
318+
/// use cachekit::policy::nru::NruCache;
319+
/// use cachekit::traits::CoreCache;
320+
///
321+
/// let mut cache = NruCache::new(10);
322+
/// cache.insert("a", 1);
323+
/// cache.insert("b", 2);
324+
///
325+
/// for (_key, value) in cache.iter_mut() {
326+
/// *value += 10;
327+
/// }
328+
/// ```
329+
#[inline]
330+
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
331+
IterMut {
332+
inner: self.map.iter_mut(),
333+
}
334+
}
335+
275336
/// Evicts an entry using NRU policy.
276337
///
277338
/// First tries to find an unreferenced entry. If all entries are referenced,
@@ -344,18 +405,54 @@ where
344405
/// Returns `true` if the cache contains the key.
345406
///
346407
/// Does not affect the reference bit.
408+
///
409+
/// # Example
410+
///
411+
/// ```
412+
/// use cachekit::policy::nru::NruCache;
413+
/// use cachekit::traits::{CoreCache, ReadOnlyCache};
414+
///
415+
/// let mut cache = NruCache::new(10);
416+
/// cache.insert("key", 1);
417+
///
418+
/// assert!(cache.contains(&"key"));
419+
/// assert!(!cache.contains(&"missing"));
420+
/// ```
347421
#[inline]
348422
fn contains(&self, key: &K) -> bool {
349423
self.map.contains_key(key)
350424
}
351425

352426
/// Returns the number of entries in the cache.
427+
///
428+
/// # Example
429+
///
430+
/// ```
431+
/// use cachekit::policy::nru::NruCache;
432+
/// use cachekit::traits::{CoreCache, ReadOnlyCache};
433+
///
434+
/// let mut cache = NruCache::new(10);
435+
/// assert_eq!(cache.len(), 0);
436+
///
437+
/// cache.insert("a", 1);
438+
/// assert_eq!(cache.len(), 1);
439+
/// ```
353440
#[inline]
354441
fn len(&self) -> usize {
355442
self.map.len()
356443
}
357444

358445
/// Returns the maximum capacity of the cache.
446+
///
447+
/// # Example
448+
///
449+
/// ```
450+
/// use cachekit::policy::nru::NruCache;
451+
/// use cachekit::traits::ReadOnlyCache;
452+
///
453+
/// let cache = NruCache::<String, i32>::new(50);
454+
/// assert_eq!(cache.capacity(), 50);
455+
/// ```
359456
#[inline]
360457
fn capacity(&self) -> usize {
361458
self.capacity
@@ -474,6 +571,20 @@ where
474571
}
475572

476573
/// Clears all entries from the cache.
574+
///
575+
/// # Example
576+
///
577+
/// ```
578+
/// use cachekit::policy::nru::NruCache;
579+
/// use cachekit::traits::{CoreCache, ReadOnlyCache};
580+
///
581+
/// let mut cache = NruCache::new(10);
582+
/// cache.insert("a", 1);
583+
/// cache.insert("b", 2);
584+
///
585+
/// cache.clear();
586+
/// assert!(cache.is_empty());
587+
/// ```
477588
fn clear(&mut self) {
478589
self.map.clear();
479590
self.keys.clear();
@@ -530,6 +641,20 @@ where
530641
K: Clone + Eq + Hash,
531642
{
532643
/// Returns a snapshot of cache metrics.
644+
///
645+
/// # Example
646+
///
647+
/// ```
648+
/// use cachekit::policy::nru::NruCache;
649+
/// use cachekit::traits::CoreCache;
650+
///
651+
/// let mut cache = NruCache::new(10);
652+
/// cache.insert("a", 1);
653+
/// let _ = cache.get(&"a");
654+
///
655+
/// let snap = cache.metrics_snapshot();
656+
/// assert_eq!(snap.get_hits, 1);
657+
/// ```
533658
pub fn metrics_snapshot(&self) -> NruMetricsSnapshot {
534659
NruMetricsSnapshot {
535660
get_calls: self.metrics.get_calls,
@@ -572,11 +697,174 @@ where
572697
}
573698
}
574699

700+
impl<K, V> Clone for NruCache<K, V>
701+
where
702+
K: Clone + Eq + Hash,
703+
V: Clone,
704+
{
705+
fn clone(&self) -> Self {
706+
Self {
707+
map: self.map.clone(),
708+
keys: self.keys.clone(),
709+
capacity: self.capacity,
710+
#[cfg(feature = "metrics")]
711+
metrics: self.metrics,
712+
}
713+
}
714+
}
715+
716+
// ---------------------------------------------------------------------------
717+
// Iterators
718+
// ---------------------------------------------------------------------------
719+
720+
/// Iterator over shared references to key-value pairs in an [`NruCache`].
721+
///
722+
/// Created by [`NruCache::iter`].
723+
pub struct Iter<'a, K, V> {
724+
inner: std::collections::hash_map::Iter<'a, K, Entry<V>>,
725+
}
726+
727+
impl<'a, K, V> Iterator for Iter<'a, K, V> {
728+
type Item = (&'a K, &'a V);
729+
730+
#[inline]
731+
fn next(&mut self) -> Option<Self::Item> {
732+
self.inner.next().map(|(k, e)| (k, &e.value))
733+
}
734+
735+
#[inline]
736+
fn size_hint(&self) -> (usize, Option<usize>) {
737+
self.inner.size_hint()
738+
}
739+
}
740+
741+
impl<K, V> ExactSizeIterator for Iter<'_, K, V> {}
742+
743+
impl<K, V> std::iter::FusedIterator for Iter<'_, K, V> {}
744+
745+
/// Iterator over mutable references to values (with shared key references)
746+
/// in an [`NruCache`].
747+
///
748+
/// Created by [`NruCache::iter_mut`].
749+
pub struct IterMut<'a, K, V> {
750+
inner: std::collections::hash_map::IterMut<'a, K, Entry<V>>,
751+
}
752+
753+
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
754+
type Item = (&'a K, &'a mut V);
755+
756+
#[inline]
757+
fn next(&mut self) -> Option<Self::Item> {
758+
self.inner.next().map(|(k, e)| (k, &mut e.value))
759+
}
760+
761+
#[inline]
762+
fn size_hint(&self) -> (usize, Option<usize>) {
763+
self.inner.size_hint()
764+
}
765+
}
766+
767+
impl<K, V> ExactSizeIterator for IterMut<'_, K, V> {}
768+
769+
impl<K, V> std::iter::FusedIterator for IterMut<'_, K, V> {}
770+
771+
/// Owning iterator over key-value pairs from an [`NruCache`].
772+
///
773+
/// Created by the `IntoIterator` implementation on `NruCache`.
774+
pub struct IntoIter<K, V> {
775+
inner: std::collections::hash_map::IntoIter<K, Entry<V>>,
776+
}
777+
778+
impl<K, V> Iterator for IntoIter<K, V> {
779+
type Item = (K, V);
780+
781+
#[inline]
782+
fn next(&mut self) -> Option<Self::Item> {
783+
self.inner.next().map(|(k, e)| (k, e.value))
784+
}
785+
786+
#[inline]
787+
fn size_hint(&self) -> (usize, Option<usize>) {
788+
self.inner.size_hint()
789+
}
790+
}
791+
792+
impl<K, V> ExactSizeIterator for IntoIter<K, V> {}
793+
794+
impl<K, V> std::iter::FusedIterator for IntoIter<K, V> {}
795+
796+
// ---------------------------------------------------------------------------
797+
// IntoIterator
798+
// ---------------------------------------------------------------------------
799+
800+
impl<K, V> IntoIterator for NruCache<K, V>
801+
where
802+
K: Clone + Eq + Hash,
803+
{
804+
type Item = (K, V);
805+
type IntoIter = IntoIter<K, V>;
806+
807+
fn into_iter(self) -> Self::IntoIter {
808+
IntoIter {
809+
inner: self.map.into_iter(),
810+
}
811+
}
812+
}
813+
814+
impl<'a, K, V> IntoIterator for &'a NruCache<K, V>
815+
where
816+
K: Clone + Eq + Hash,
817+
{
818+
type Item = (&'a K, &'a V);
819+
type IntoIter = Iter<'a, K, V>;
820+
821+
fn into_iter(self) -> Self::IntoIter {
822+
self.iter()
823+
}
824+
}
825+
826+
impl<'a, K, V> IntoIterator for &'a mut NruCache<K, V>
827+
where
828+
K: Clone + Eq + Hash,
829+
{
830+
type Item = (&'a K, &'a mut V);
831+
type IntoIter = IterMut<'a, K, V>;
832+
833+
fn into_iter(self) -> Self::IntoIter {
834+
self.iter_mut()
835+
}
836+
}
837+
838+
// ---------------------------------------------------------------------------
839+
// Extend
840+
// ---------------------------------------------------------------------------
841+
842+
impl<K, V> Extend<(K, V)> for NruCache<K, V>
843+
where
844+
K: Clone + Eq + Hash,
845+
{
846+
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
847+
for (k, v) in iter {
848+
self.insert(k, v);
849+
}
850+
}
851+
}
852+
575853
#[cfg(test)]
576854
mod tests {
577855
use super::*;
578856
use crate::traits::{CoreCache, MutableCache};
579857

858+
#[allow(dead_code)]
859+
const _: () = {
860+
fn assert_send<T: Send>() {}
861+
fn assert_sync<T: Sync>() {}
862+
fn check() {
863+
assert_send::<NruCache<String, i32>>();
864+
assert_sync::<NruCache<String, i32>>();
865+
}
866+
};
867+
580868
#[test]
581869
fn test_new() {
582870
let cache: NruCache<i32, i32> = NruCache::new(10);

0 commit comments

Comments
 (0)