1#![allow(clippy::type_complexity)]
81#![cfg_attr(docsrs, feature(doc_cfg))]
82
83#[cfg(not(fuzzing))]
84mod linked_slab;
85#[cfg(fuzzing)]
86pub mod linked_slab;
87mod options;
88#[cfg(not(feature = "shuttle"))]
89mod rw_lock;
90mod shard;
91mod shim;
92pub mod sync;
94mod sync_placeholder;
95pub mod unsync;
97pub use equivalent::Equivalent;
98
99#[cfg(all(test, feature = "shuttle"))]
100mod shuttle_tests;
101
102pub use options::{Options, OptionsBuilder};
103
104#[cfg(feature = "ahash")]
105pub type DefaultHashBuilder = ahash::RandomState;
106#[cfg(not(feature = "ahash"))]
107pub type DefaultHashBuilder = std::collections::hash_map::RandomState;
108
109pub trait Weighter<Key, Val> {
130 fn weight(&self, key: &Key, val: &Val) -> u64;
145}
146
147#[derive(Debug, Clone, Default)]
149pub struct UnitWeighter;
150
151impl<Key, Val> Weighter<Key, Val> for UnitWeighter {
152 #[inline]
153 fn weight(&self, _key: &Key, _val: &Val) -> u64 {
154 1
155 }
156}
157
158pub trait Lifecycle<Key, Val> {
162 type RequestState;
163
164 #[allow(unused_variables)]
173 #[inline]
174 fn is_pinned(&self, key: &Key, val: &Val) -> bool {
175 false
176 }
177
178 fn begin_request(&self) -> Self::RequestState;
180
181 #[allow(unused_variables)]
189 #[inline]
190 fn before_evict(&self, state: &mut Self::RequestState, key: &Key, val: &mut Val) {}
191
192 fn on_evict(&self, state: &mut Self::RequestState, key: Key, val: Val);
194
195 #[allow(unused_variables)]
202 #[inline]
203 fn end_request(&self, state: Self::RequestState) {}
204}
205
206#[non_exhaustive]
210#[derive(Debug, Copy, Clone)]
211pub struct MemoryUsed {
212 pub entries: usize,
213 pub map: usize,
214}
215
216impl MemoryUsed {
217 pub fn total(&self) -> usize {
218 self.entries + self.map
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use std::{
225 hash::Hash,
226 sync::{atomic::AtomicUsize, Arc},
227 time::Duration,
228 };
229
230 use super::*;
231 #[derive(Clone)]
232 struct StringWeighter;
233
234 impl Weighter<u64, String> for StringWeighter {
235 fn weight(&self, _key: &u64, val: &String) -> u64 {
236 val.len() as u64
237 }
238 }
239
240 #[test]
241 fn test_new() {
242 sync::Cache::<(u64, u64), u64>::new(0);
243 sync::Cache::<(u64, u64), u64>::new(1);
244 sync::Cache::<(u64, u64), u64>::new(2);
245 sync::Cache::<(u64, u64), u64>::new(3);
246 sync::Cache::<(u64, u64), u64>::new(usize::MAX);
247 sync::Cache::<u64, u64>::new(0);
248 sync::Cache::<u64, u64>::new(1);
249 sync::Cache::<u64, u64>::new(2);
250 sync::Cache::<u64, u64>::new(3);
251 sync::Cache::<u64, u64>::new(usize::MAX);
252 }
253
254 #[test]
255 fn test_capacity_one() {
256 let cache = sync::Cache::<u64, u64>::new(1);
258 cache.insert(1, 10);
259 assert_eq!(cache.get(&1), Some(10));
260 cache.insert(2, 20);
262 assert_eq!(cache.get(&2), Some(20));
263 assert_eq!(cache.get(&1), None);
264
265 let mut cache = unsync::Cache::<u64, u64>::new(1);
267 cache.insert(1, 10);
268 assert_eq!(cache.get(&1), Some(&10));
269 cache.insert(2, 20);
270 assert_eq!(cache.get(&2), Some(&20));
271 assert_eq!(cache.get(&1), None);
272
273 let cache = sync::Cache::<u64, u64>::new(0);
275 cache.insert(1, 10);
276 assert_eq!(cache.get(&1), None);
277 }
278
279 #[test]
280 fn test_custom_cost() {
281 let cache = sync::Cache::with_weighter(100, 100_000, StringWeighter);
282 cache.insert(1, "1".to_string());
283 cache.insert(54, "54".to_string());
284 cache.insert(1000, "1000".to_string());
285 assert_eq!(cache.get(&1000).unwrap(), "1000");
286 }
287
288 #[test]
289 fn test_change_get_mut_change_weight() {
290 let mut cache = unsync::Cache::with_weighter(100, 100_000, StringWeighter);
291 cache.insert(1, "1".to_string());
292 assert_eq!(cache.get(&1).unwrap(), "1");
293 assert_eq!(cache.weight(), 1);
294 let _old = {
295 cache
296 .get_mut(&1)
297 .map(|mut v| std::mem::replace(&mut *v, "11".to_string()))
298 };
299 let _old = {
300 cache
301 .get_mut(&1)
302 .map(|mut v| std::mem::replace(&mut *v, "".to_string()))
303 };
304 assert_eq!(cache.get(&1).unwrap(), "");
305 assert_eq!(cache.weight(), 0);
306 cache.validate(false);
307 }
308
309 #[derive(Debug, Hash)]
310 pub struct Pair<A, B>(pub A, pub B);
311
312 impl<A, B, C, D> PartialEq<(A, B)> for Pair<C, D>
313 where
314 C: PartialEq<A>,
315 D: PartialEq<B>,
316 {
317 fn eq(&self, rhs: &(A, B)) -> bool {
318 self.0 == rhs.0 && self.1 == rhs.1
319 }
320 }
321
322 impl<A, B, X> Equivalent<X> for Pair<A, B>
323 where
324 Pair<A, B>: PartialEq<X>,
325 A: Hash + Eq,
326 B: Hash + Eq,
327 {
328 fn equivalent(&self, other: &X) -> bool {
329 *self == *other
330 }
331 }
332
333 #[test]
334 fn test_equivalent() {
335 let mut cache = unsync::Cache::new(5);
336 cache.insert(("square".to_string(), 2022), "blue".to_string());
337 cache.insert(("square".to_string(), 2023), "black".to_string());
338 assert_eq!(cache.get(&Pair("square", 2022)).unwrap(), "blue");
339 }
340
341 #[test]
342 fn test_borrow_keys() {
343 let cache = sync::Cache::<(Vec<u8>, Vec<u8>), u64>::new(0);
344 cache.get(&Pair(&b""[..], &b""[..]));
345 let cache = sync::Cache::<(String, String), u64>::new(0);
346 cache.get(&Pair("", ""));
347 }
348
349 #[test]
350 #[cfg_attr(miri, ignore)]
351 fn test_get_or_insert() {
352 use rand::prelude::*;
353 for _i in 0..2000 {
354 dbg!(_i);
355 let mut entered = AtomicUsize::default();
356 let cache = sync::Cache::<(u64, u64), u64>::new(100);
357 const THREADS: usize = 100;
358 let wg = std::sync::Barrier::new(THREADS);
359 let solve_at = rand::rng().random_range(0..THREADS);
360 std::thread::scope(|s| {
361 for _ in 0..THREADS {
362 s.spawn(|| {
363 wg.wait();
364 let result = cache.get_or_insert_with(&(1, 1), || {
365 let before = entered.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
366 if before == solve_at {
367 Ok(1)
368 } else {
369 Err(())
370 }
371 });
372 assert!(matches!(result, Ok(1) | Err(())));
373 });
374 }
375 });
376 assert_eq!(*entered.get_mut(), solve_at + 1);
377 }
378 }
379
380 #[test]
381 fn test_get_or_insert_unsync() {
382 let mut cache = unsync::Cache::<u64, u64>::new(100);
383 let guard = cache.get_ref_or_guard(&0).unwrap_err();
384 guard.insert(0);
385 assert_eq!(cache.get_ref_or_guard(&0).ok().copied(), Some(0));
386 let guard = cache.get_mut_or_guard(&1).err().unwrap();
387 guard.insert(1);
388 let v = *cache.get_mut_or_guard(&1).ok().unwrap().unwrap();
389 assert_eq!(v, 1);
390 let result = cache.get_or_insert_with::<_, ()>(&0, || panic!());
391 assert_eq!(result, Ok(Some(&0)));
392 let result = cache.get_or_insert_with::<_, ()>(&1, || panic!());
393 assert_eq!(result, Ok(Some(&1)));
394 let result = cache.get_or_insert_with::<_, ()>(&3, || Ok(3));
395 assert_eq!(result, Ok(Some(&3)));
396 let result = cache.get_or_insert_with::<_, ()>(&4, || Err(()));
397 assert_eq!(result, Err(()));
398 }
399
400 #[tokio::test]
401 async fn test_get_or_insert_sync() {
402 use crate::sync::*;
403 let cache = sync::Cache::<u64, u64>::new(100);
404 let GuardResult::Guard(guard) = cache.get_value_or_guard(&0, None) else {
405 panic!();
406 };
407 guard.insert(0).unwrap();
408 let GuardResult::Value(v) = cache.get_value_or_guard(&0, None) else {
409 panic!();
410 };
411 assert_eq!(v, 0);
412 let Err(guard) = cache.get_value_or_guard_async(&1).await else {
413 panic!();
414 };
415 guard.insert(1).unwrap();
416 let Ok(v) = cache.get_value_or_guard_async(&1).await else {
417 panic!();
418 };
419 assert_eq!(v, 1);
420
421 let result = cache.get_or_insert_with::<_, ()>(&0, || panic!());
422 assert_eq!(result, Ok(0));
423 let result = cache.get_or_insert_with::<_, ()>(&3, || Ok(3));
424 assert_eq!(result, Ok(3));
425 let result = cache.get_or_insert_with::<_, ()>(&4, || Err(()));
426 assert_eq!(result, Err(()));
427 let result = cache
428 .get_or_insert_async::<_, ()>(&0, async { panic!() })
429 .await;
430 assert_eq!(result, Ok(0));
431 let result = cache
432 .get_or_insert_async::<_, ()>(&4, async { Err(()) })
433 .await;
434 assert_eq!(result, Err(()));
435 let result = cache
436 .get_or_insert_async::<_, ()>(&4, async { Ok(4) })
437 .await;
438 assert_eq!(result, Ok(4));
439 }
440
441 #[test]
442 fn test_retain_unsync() {
443 let mut cache = unsync::Cache::<u64, u64>::new(100);
444 let ranges = 0..10;
445 for i in ranges.clone() {
446 let guard = cache.get_ref_or_guard(&i).unwrap_err();
447 guard.insert(i);
448 assert_eq!(cache.get_ref_or_guard(&i).ok().copied(), Some(i));
449 }
450 let small = 3;
451 cache.retain(|&key, &val| val > small && key > small);
452 for i in ranges.clone() {
453 let actual = cache.get(&i);
454 if i > small {
455 assert!(actual.is_some());
456 assert_eq!(*actual.unwrap(), i);
457 } else {
458 assert!(actual.is_none());
459 }
460 }
461 let big = 7;
462 cache.retain(|&key, &val| val < big && key < big);
463 for i in ranges {
464 let actual = cache.get(&i);
465 if i > small && i < big {
466 assert!(actual.is_some());
467 assert_eq!(*actual.unwrap(), i);
468 } else {
469 assert!(actual.is_none());
470 }
471 }
472 }
473
474 #[tokio::test]
475 async fn test_retain_sync() {
476 use crate::sync::*;
477 let cache = Cache::<u64, u64>::new(100);
478 let ranges = 0..10;
479 for i in ranges.clone() {
480 let GuardResult::Guard(guard) = cache.get_value_or_guard(&i, None) else {
481 panic!();
482 };
483 guard.insert(i).unwrap();
484 let GuardResult::Value(v) = cache.get_value_or_guard(&i, None) else {
485 panic!();
486 };
487 assert_eq!(v, i);
488 }
489 let small = 4;
490 cache.retain(|&key, &val| val > small && key > small);
491 for i in ranges.clone() {
492 let actual = cache.get(&i);
493 if i > small {
494 assert!(actual.is_some());
495 assert_eq!(actual.unwrap(), i);
496 } else {
497 assert!(actual.is_none());
498 }
499 }
500 let big = 8;
501 cache.retain(|&key, &val| val < big && key < big);
502 for i in ranges {
503 let actual = cache.get(&i);
504 if i > small && i < big {
505 assert!(actual.is_some());
506 assert_eq!(actual.unwrap(), i);
507 } else {
508 assert!(actual.is_none());
509 }
510 }
511 }
512
513 #[test]
514 #[cfg_attr(miri, ignore)]
515 fn test_value_or_guard() {
516 use crate::sync::*;
517 use rand::prelude::*;
518 for _i in 0..2000 {
519 dbg!(_i);
520 let mut entered = AtomicUsize::default();
521 let cache = sync::Cache::<(u64, u64), u64>::new(100);
522 const THREADS: usize = 100;
523 let wg = std::sync::Barrier::new(THREADS);
524 let solve_at = rand::rng().random_range(0..THREADS);
525 std::thread::scope(|s| {
526 for _ in 0..THREADS {
527 s.spawn(|| {
528 wg.wait();
529 loop {
530 match cache.get_value_or_guard(&(1, 1), Some(Duration::from_millis(1)))
531 {
532 GuardResult::Value(v) => assert_eq!(v, 1),
533 GuardResult::Guard(g) => {
534 let before =
535 entered.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
536 if before == solve_at {
537 g.insert(1).unwrap();
538 }
539 }
540 GuardResult::Timeout => continue,
541 }
542 break;
543 }
544 });
545 }
546 });
547 assert_eq!(*entered.get_mut(), solve_at + 1);
548 }
549 }
550
551 #[tokio::test(flavor = "multi_thread")]
552 #[cfg_attr(miri, ignore)]
553 async fn test_get_or_insert_async() {
554 use rand::prelude::*;
555 for _i in 0..5000 {
556 dbg!(_i);
557 let entered = Arc::new(AtomicUsize::default());
558 let cache = Arc::new(sync::Cache::<(u64, u64), u64>::new(100));
559 const TASKS: usize = 100;
560 let wg = Arc::new(tokio::sync::Barrier::new(TASKS));
561 let solve_at = rand::rng().random_range(0..TASKS);
562 let mut tasks = Vec::new();
563 for _ in 0..TASKS {
564 let cache = cache.clone();
565 let wg = wg.clone();
566 let entered = entered.clone();
567 let task = tokio::spawn(async move {
568 wg.wait().await;
569 let result = cache
570 .get_or_insert_async(&(1, 1), async {
571 let before = entered.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
572 if before == solve_at {
573 Ok(1)
574 } else {
575 Err(())
576 }
577 })
578 .await;
579 assert!(matches!(result, Ok(1) | Err(())));
580 });
581 tasks.push(task);
582 }
583 for task in tasks {
584 task.await.unwrap();
585 }
586 assert_eq!(cache.get(&(1, 1)), Some(1));
587 assert_eq!(
588 entered.load(std::sync::atomic::Ordering::Relaxed),
589 solve_at + 1
590 );
591 }
592 }
593
594 #[tokio::test(flavor = "multi_thread")]
595 #[cfg_attr(miri, ignore)]
596 async fn test_value_or_guard_async() {
597 use rand::prelude::*;
598 for _i in 0..5000 {
599 dbg!(_i);
600 let entered = Arc::new(AtomicUsize::default());
601 let cache = Arc::new(sync::Cache::<(u64, u64), u64>::new(100));
602 const TASKS: usize = 100;
603 let wg = Arc::new(tokio::sync::Barrier::new(TASKS));
604 let solve_at = rand::rng().random_range(0..TASKS);
605 let mut tasks = Vec::new();
606 for _ in 0..TASKS {
607 let cache = cache.clone();
608 let wg = wg.clone();
609 let entered = entered.clone();
610 let task = tokio::spawn(async move {
611 wg.wait().await;
612 loop {
613 match tokio::time::timeout(
614 Duration::from_millis(1),
615 cache.get_value_or_guard_async(&(1, 1)),
616 )
617 .await
618 {
619 Ok(Ok(r)) => assert_eq!(r, 1),
620 Ok(Err(g)) => {
621 let before =
622 entered.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
623 if before == solve_at {
624 g.insert(1).unwrap();
625 }
626 }
627 Err(_) => continue,
628 }
629 break;
630 }
631 });
632 tasks.push(task);
633 }
634 for task in tasks {
635 task.await.unwrap();
636 }
637 assert_eq!(cache.get(&(1, 1)), Some(1));
638 assert_eq!(
639 entered.load(std::sync::atomic::Ordering::Relaxed),
640 solve_at + 1
641 );
642 }
643 }
644}