1#[cfg(any(
24 feature = "arc",
25 feature = "tinylfu",
26 feature = "weighted",
27 feature = "concurrent-shards",
28 feature = "metrics",
29))]
30pub mod features;
31
32#[cfg(feature = "arc")]
33pub use features::arc::ArcCache;
34#[cfg(feature = "concurrent-shards")]
35pub use features::concurrent_shards::ShardedCache;
36#[cfg(feature = "metrics")]
37pub use features::metrics::{CacheMetrics, MetricsCache};
38#[cfg(feature = "tinylfu")]
39pub use features::tinylfu::TinyLfuCache;
40#[cfg(feature = "weighted")]
41pub use features::weighted::WeightedCache;
42
43use std::collections::HashMap;
44
45pub struct BlockCache<K, V> {
46 capacity: usize,
47 slots: Vec<Option<Slot<K, V>>>,
49 index: HashMap<K, usize>,
51 hand: usize,
53}
54
55struct Slot<K, V> {
56 key: K,
57 value: V,
58 referenced: bool,
59}
60
61impl<K: std::hash::Hash + Eq + Clone, V> BlockCache<K, V> {
62 pub fn with_capacity(capacity: usize) -> Self {
63 let cap = capacity.max(1);
64 let mut slots = Vec::with_capacity(cap);
65 for _ in 0..cap {
66 slots.push(None);
67 }
68 Self {
69 capacity: cap,
70 slots,
71 index: HashMap::with_capacity(cap),
72 hand: 0,
73 }
74 }
75
76 pub fn capacity(&self) -> usize {
77 self.capacity
78 }
79 pub fn len(&self) -> usize {
80 self.index.len()
81 }
82 pub fn is_empty(&self) -> bool {
83 self.index.is_empty()
84 }
85
86 pub fn get(&mut self, key: &K) -> Option<&V> {
89 let idx = *self.index.get(key)?;
90 let slot = self.slots[idx].as_mut().expect("indexed slot is populated");
91 slot.referenced = true;
92 Some(&self.slots[idx].as_ref().unwrap().value)
93 }
94
95 pub fn put(&mut self, key: K, value: V) -> Option<(K, V)> {
98 if let Some(&idx) = self.index.get(&key) {
99 let slot = self.slots[idx].as_mut().unwrap();
101 slot.value = value;
102 slot.referenced = true;
103 return None;
104 }
105
106 if self.index.len() < self.capacity {
107 for i in 0..self.capacity {
109 if self.slots[i].is_none() {
110 self.slots[i] = Some(Slot {
111 key: key.clone(),
112 value,
113 referenced: true,
114 });
115 self.index.insert(key, i);
116 return None;
117 }
118 }
119 unreachable!("under capacity but no empty slot");
120 }
121
122 loop {
124 let idx = self.hand;
125 self.hand = (self.hand + 1) % self.capacity;
126 let slot = self.slots[idx].as_mut().expect("populated");
127 if slot.referenced {
128 slot.referenced = false;
129 continue;
130 }
131 let old = self.slots[idx].take().unwrap();
133 self.index.remove(&old.key);
134 self.slots[idx] = Some(Slot {
135 key: key.clone(),
136 value,
137 referenced: true,
138 });
139 self.index.insert(key, idx);
140 return Some((old.key, old.value));
141 }
142 }
143}
144
145#[cfg(feature = "harness")]
146pub mod recipe;