1#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
2
3use std::{
4 hash::Hash,
5 sync::atomic::{AtomicBool, Ordering},
6 time::{Duration, Instant},
7};
8
9use crossbeam_queue::ArrayQueue;
10use crossbeam_utils::{atomic::AtomicCell, CachePadded};
11use dashmap::DashMap;
12
13pub struct Value<V> {
17 value: V,
18 expire_at: Instant,
19 is_expired: bool,
20}
21
22impl<V> Value<V> {
23 pub fn get(&self) -> &V {
25 &self.value
26 }
27
28 pub fn get_mut(&mut self) -> &mut V {
30 &mut self.value
31 }
32
33 pub fn into_inner(self) -> V {
35 self.value
36 }
37
38 pub fn is_expired(&self) -> bool {
40 self.is_expired
41 }
42
43 pub fn expire_at(&self) -> Instant {
45 self.expire_at
46 }
47}
48
49impl<V> std::ops::Deref for Value<V> {
50 type Target = V;
51
52 fn deref(&self) -> &Self::Target {
53 &self.value
54 }
55}
56
57impl<V> std::ops::DerefMut for Value<V> {
58 fn deref_mut(&mut self) -> &mut Self::Target {
59 &mut self.value
60 }
61}
62
63pub struct Cache<K, V> {
65 map: DashMap<K, (V, Instant), ahash::RandomState>,
66 ringbuf: ArrayQueue<(K, Instant)>,
67
68 capacity: usize,
69 ttl: Duration,
70
71 expire_started: CachePadded<AtomicBool>,
72 oldest: CachePadded<AtomicCell<Instant>>,
73}
74
75impl<K, V> Cache<K, V>
76where
77 K: Eq + Hash + Clone,
78 V: Clone,
79{
80 pub fn new(capacity: usize, ttl: Duration) -> Self {
82 Self {
83 map: DashMap::with_capacity_and_hasher(capacity, ahash::RandomState::new()),
84 ringbuf: ArrayQueue::new(capacity),
85 capacity,
86 ttl,
87 expire_started: CachePadded::new(AtomicBool::new(false)),
88 oldest: CachePadded::new(AtomicCell::new(Instant::now())),
89 }
90 }
91
92 pub fn len(&self) -> usize {
94 self.ringbuf.len()
95 }
96
97 pub fn capacity(&self) -> usize {
99 self.capacity
100 }
101
102 pub fn get(&self, key: &K) -> Option<Value<V>> {
104 let v = self.map.get(&key);
105 if v.is_none() {
106 return None;
107 }
108 let v = v.unwrap();
109 let now = Instant::now();
110 let value = Value {
111 value: v.0.clone(),
112 expire_at: v.1,
113 is_expired: now > v.1,
114 };
115 drop(v);
117 self.do_expire(now);
118 Some(value)
119 }
120
121 pub fn insert(&self, key: K, value: V) {
125 let now = Instant::now();
126 let expire_at = now + self.ttl;
127 while let Err(_) = self.ringbuf.push((key.clone(), expire_at)) {
128 let (k, e) = self.ringbuf.pop().unwrap();
130 self.map.remove(&k);
131 self.oldest.store(e);
132 }
133 self.map.insert(key, (value, expire_at));
134 self.do_expire(now);
135 }
136
137 pub fn remove(&self, key: &K) -> Option<V> {
138 self.map.remove(key).map(|v| v.1.0)
139 }
140
141 fn do_expire(&self, now: Instant) {
143 if self.oldest.load() > now {
144 return;
146 }
147
148 if self
150 .expire_started
151 .compare_exchange_weak(false, true, Ordering::AcqRel, Ordering::Relaxed)
152 .is_err()
153 {
154 return;
155 }
156
157 while let Some((k, t)) = self.ringbuf.pop() {
158 self.map.remove(&k);
159 if now <= t {
160 self.oldest.store(t);
162 break;
163 }
164 }
165 self.expire_started.store(false, Ordering::Release);
166 }
167
168 pub fn clear(&self) {
169 while let Some((k, _)) = self.ringbuf.pop() {
170 self.map.remove(&k);
171 }
172 self.oldest.store(Instant::now());
173 }
174}
175
176pub struct SizedCache<K, V> {
178 map: DashMap<K, V, ahash::RandomState>,
179 ringbuf: ArrayQueue<K>,
180
181 capacity: usize,
182}
183
184impl<K, V> SizedCache<K, V>
185where
186 K: Eq + Hash + Clone,
187 V: Clone,
188{
189 pub fn new(capacity: usize) -> Self {
191 Self {
192 map: DashMap::with_capacity_and_hasher(capacity, ahash::RandomState::new()),
193 ringbuf: ArrayQueue::new(capacity),
194 capacity,
195 }
196 }
197
198 pub fn len(&self) -> usize {
200 self.ringbuf.len()
201 }
202
203 pub fn capacity(&self) -> usize {
205 self.capacity
206 }
207
208 pub fn get(&self, key: &K) -> Option<V> {
210 self.map.get(&key).map(|v| v.value().clone())
211 }
212
213 pub fn insert(&self, key: K, value: V) {
217 while let Err(_) = self.ringbuf.push(key.clone()) {
218 let k = self.ringbuf.pop().unwrap();
220 self.map.remove(&k);
221 }
222 self.map.insert(key, value);
223 }
224
225 pub fn remove(&self, key: &K) -> Option<V> {
226 self.map.remove(key).map(|v| v.1)
227 }
228
229 pub fn clear(&self) {
230 while let Some(k) = self.ringbuf.pop() {
231 self.map.remove(&k);
232 }
233 }
234}