1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![cfg_attr(not(feature = "std"), no_std)]
4#![allow(clippy::new_without_default)]
5
6#[cfg(feature = "alloc")]
7extern crate alloc;
8
9use core::{
10 cell::UnsafeCell,
11 convert::Infallible,
12 fmt,
13 hash::{BuildHasher, Hash},
14 marker::PhantomData,
15 mem::{self, MaybeUninit},
16 ptr,
17 sync::atomic::{AtomicUsize, Ordering},
18};
19use equivalent::Equivalent;
20
21#[cfg(feature = "stats")]
22mod stats;
23#[cfg(feature = "stats")]
24#[cfg_attr(docsrs, doc(cfg(feature = "stats")))]
25pub use stats::{AnyRef, CountingStatsHandler, Stats, StatsHandler};
26
27const LOCKED_BIT: usize = 1 << 0;
28const ALIVE_BIT: usize = 1 << 1;
29const NEEDED_BITS: usize = 2;
30
31const EPOCH_BITS: usize = 10;
32const EPOCH_SHIFT: usize = NEEDED_BITS;
33const EPOCH_MASK: usize = ((1 << EPOCH_BITS) - 1) << EPOCH_SHIFT;
34const EPOCH_NEEDED_BITS: usize = NEEDED_BITS + EPOCH_BITS;
35
36#[cfg(feature = "rapidhash")]
37type DefaultBuildHasher = core::hash::BuildHasherDefault<rapidhash::fast::RapidHasher<'static>>;
38#[cfg(all(not(feature = "rapidhash"), feature = "std"))]
39type DefaultBuildHasher = std::hash::RandomState;
40#[cfg(all(not(feature = "rapidhash"), not(feature = "std")))]
41type DefaultBuildHasher = core::hash::BuildHasherDefault<NoDefaultHasher>;
42
43#[cfg(all(not(feature = "rapidhash"), not(feature = "std")))]
44#[doc(hidden)]
45pub enum NoDefaultHasher {}
46
47pub trait CacheConfig {
51 const STATS: bool = true;
60
61 const EPOCHS: bool = false;
69}
70
71pub struct DefaultCacheConfig(());
73impl CacheConfig for DefaultCacheConfig {}
74
75pub struct Cache<K, V, S = DefaultBuildHasher, C: CacheConfig = DefaultCacheConfig> {
121 entries: *const [Bucket<(K, V)>],
122 build_hasher: S,
123 #[cfg(feature = "stats")]
124 stats: Option<Stats<K, V>>,
125 #[cfg(feature = "alloc")]
126 drop: bool,
127 epoch: AtomicUsize,
128 _config: PhantomData<C>,
129}
130
131impl<K, V, S, C: CacheConfig> fmt::Debug for Cache<K, V, S, C> {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 f.debug_struct("Cache").finish_non_exhaustive()
134 }
135}
136
137unsafe impl<K: Send, V: Send, S: Send, C: CacheConfig + Send> Send for Cache<K, V, S, C> {}
139unsafe impl<K: Send, V: Send, S: Sync, C: CacheConfig + Sync> Sync for Cache<K, V, S, C> {}
140
141impl<K, V, S, C> Cache<K, V, S, C>
142where
143 K: Hash + Eq,
144 S: BuildHasher,
145 C: CacheConfig,
146{
147 const NEEDS_DROP: bool = Bucket::<(K, V)>::NEEDS_DROP;
148
149 #[cfg(feature = "alloc")]
160 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
161 pub fn new(num: usize, build_hasher: S) -> Self {
162 Self::len_assertion(num);
163 let layout = alloc::alloc::Layout::array::<Bucket<(K, V)>>(num).unwrap();
164 let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
165 if ptr.is_null() {
166 alloc::alloc::handle_alloc_error(layout);
167 }
168 let entries = ptr::slice_from_raw_parts(ptr.cast::<Bucket<(K, V)>>(), num);
169 Self::new_inner(entries, build_hasher, true)
170 }
171
172 #[inline]
178 pub const fn new_static(entries: &'static [Bucket<(K, V)>], build_hasher: S) -> Self {
179 Self::len_assertion(entries.len());
180 Self::new_inner(entries, build_hasher, false)
181 }
182
183 #[cfg(feature = "stats")]
187 #[inline]
188 pub fn with_stats(mut self, stats: Option<Stats<K, V>>) -> Self {
189 self.set_stats(stats);
190 self
191 }
192
193 #[cfg(feature = "stats")]
197 #[inline]
198 pub fn set_stats(&mut self, stats: Option<Stats<K, V>>) {
199 const { assert!(C::STATS, "can only set stats when C::STATS is true") }
200 self.stats = stats;
201 }
202
203 #[inline]
204 const fn new_inner(entries: *const [Bucket<(K, V)>], build_hasher: S, drop: bool) -> Self {
205 let _ = drop;
206 Self {
207 entries,
208 build_hasher,
209 #[cfg(feature = "stats")]
210 stats: None,
211 #[cfg(feature = "alloc")]
212 drop,
213 epoch: AtomicUsize::new(0),
214 _config: PhantomData,
215 }
216 }
217
218 #[inline]
219 const fn len_assertion(len: usize) {
220 let reserved = if C::EPOCHS { EPOCH_NEEDED_BITS } else { NEEDED_BITS };
224 assert!(len.is_power_of_two(), "length must be a power of two");
225 assert!((len & ((1 << reserved) - 1)) == 0, "len must have its bottom N bits set to zero");
226 }
227
228 #[inline]
229 const fn index_mask(&self) -> usize {
230 let n = self.capacity();
231 unsafe { core::hint::assert_unchecked(n.is_power_of_two()) };
232 n - 1
233 }
234
235 #[inline]
236 const fn tag_mask(&self) -> usize {
237 !self.index_mask()
238 }
239
240 #[inline]
244 fn epoch(&self) -> usize {
245 self.epoch.load(Ordering::Relaxed)
246 }
247
248 #[inline]
255 pub fn clear(&self) {
256 const EPOCH_MAX: usize = (1 << EPOCH_BITS) - 1;
257 const { assert!(C::EPOCHS, "can only .clear() when C::EPOCHS is true") }
258 let prev = self.epoch.fetch_add(1, Ordering::Release);
259 if (prev & EPOCH_MAX) == EPOCH_MAX {
261 self.clear_slow();
262 }
263 }
264
265 pub fn clear_slow(&self) {
275 unsafe {
280 ptr::write_bytes(
281 self.entries.cast_mut().cast::<Bucket<(K, V)>>(),
282 0,
283 self.entries.len(),
284 )
285 };
286 }
287
288 #[inline]
290 pub const fn hasher(&self) -> &S {
291 &self.build_hasher
292 }
293
294 #[inline]
296 pub const fn capacity(&self) -> usize {
297 self.entries.len()
298 }
299
300 #[cfg(feature = "stats")]
302 pub const fn stats(&self) -> Option<&Stats<K, V>> {
303 self.stats.as_ref()
304 }
305}
306
307impl<K, V, S, C> Cache<K, V, S, C>
308where
309 K: Hash + Eq,
310 V: Clone,
311 S: BuildHasher,
312 C: CacheConfig,
313{
314 pub fn get<Q: ?Sized + Hash + Equivalent<K>>(&self, key: &Q) -> Option<V> {
316 let (bucket, tag) = self.calc(key);
317 self.get_inner(key, bucket, tag)
318 }
319
320 #[inline]
321 fn get_inner<Q: ?Sized + Hash + Equivalent<K>>(
322 &self,
323 key: &Q,
324 bucket: &Bucket<(K, V)>,
325 tag: usize,
326 ) -> Option<V> {
327 if bucket.try_lock(Some(tag)) {
328 let (ck, v) = unsafe { (*bucket.data.get()).assume_init_ref() };
330 if key.equivalent(ck) {
331 #[cfg(feature = "stats")]
332 if C::STATS
333 && let Some(stats) = &self.stats
334 {
335 stats.record_hit(ck, v);
336 }
337 let v = v.clone();
338 bucket.unlock(tag);
339 return Some(v);
340 }
341 bucket.unlock(tag);
342 }
343 #[cfg(feature = "stats")]
344 if C::STATS
345 && let Some(stats) = &self.stats
346 {
347 stats.record_miss(AnyRef::new(&key));
348 }
349 None
350 }
351
352 pub fn insert(&self, key: K, value: V) {
354 let (bucket, tag) = self.calc(&key);
355 self.insert_inner(bucket, tag, || (key, value));
356 }
357
358 pub fn remove<Q: ?Sized + Hash + Equivalent<K>>(&self, key: &Q) -> Option<V> {
362 let (bucket, tag) = self.calc(key);
363 if bucket.try_lock(Some(tag)) {
364 let data = unsafe { &mut *bucket.data.get() };
366 let (ck, v) = unsafe { data.assume_init_ref() };
367 if key.equivalent(ck) {
368 let v = v.clone();
369 #[cfg(feature = "stats")]
370 if C::STATS
371 && let Some(stats) = &self.stats
372 {
373 stats.record_remove(ck, &v);
374 }
375 if Self::NEEDS_DROP {
376 unsafe { data.assume_init_drop() };
378 }
379 bucket.unlock(0);
380 return Some(v);
381 }
382 bucket.unlock(tag);
383 }
384 None
385 }
386
387 #[inline]
388 fn insert_inner(
389 &self,
390 bucket: &Bucket<(K, V)>,
391 tag: usize,
392 make_entry: impl FnOnce() -> (K, V),
393 ) {
394 #[inline(always)]
395 unsafe fn do_write<T>(ptr: *mut T, f: impl FnOnce() -> T) {
396 unsafe { ptr::write(ptr, f()) };
407 }
408
409 let Some(prev_tag) = bucket.try_lock_ret(None) else {
410 return;
411 };
412
413 unsafe {
415 let is_alive = (prev_tag & !LOCKED_BIT) != 0;
416 let data = bucket.data.get().cast::<(K, V)>();
417
418 if C::STATS && cfg!(feature = "stats") {
419 #[cfg(feature = "stats")]
420 if is_alive {
421 let (old_key, old_value) = ptr::replace(data, make_entry());
422 if let Some(stats) = &self.stats {
423 stats.record_insert(&(*data).0, &(*data).1, Some((&old_key, &old_value)));
424 }
425 } else {
426 do_write(data, make_entry);
427 if let Some(stats) = &self.stats {
428 stats.record_insert(&(*data).0, &(*data).1, None);
429 }
430 }
431 } else {
432 if Self::NEEDS_DROP && is_alive {
433 ptr::drop_in_place(data);
434 }
435 do_write(data, make_entry);
436 }
437 }
438 bucket.unlock(tag);
439 }
440
441 #[inline]
446 pub fn get_or_insert_with<F>(&self, key: K, f: F) -> V
447 where
448 F: FnOnce(&K) -> V,
449 {
450 let Ok(v) = self.get_or_try_insert_with(key, |key| Ok::<_, Infallible>(f(key)));
451 v
452 }
453
454 #[inline]
464 pub fn get_or_insert_with_ref<'a, Q, F, Cvt>(&self, key: &'a Q, f: F, cvt: Cvt) -> V
465 where
466 Q: ?Sized + Hash + Equivalent<K>,
467 F: FnOnce(&'a Q) -> V,
468 Cvt: FnOnce(&'a Q) -> K,
469 {
470 let Ok(v) = self.get_or_try_insert_with_ref(key, |key| Ok::<_, Infallible>(f(key)), cvt);
471 v
472 }
473
474 #[inline]
480 pub fn get_or_try_insert_with<F, E>(&self, key: K, f: F) -> Result<V, E>
481 where
482 F: FnOnce(&K) -> Result<V, E>,
483 {
484 let mut key = mem::ManuallyDrop::new(key);
485 let mut read = false;
486 let r = self.get_or_try_insert_with_ref(&*key, f, |k| {
487 read = true;
488 unsafe { ptr::read(k) }
489 });
490 if !read {
491 unsafe { mem::ManuallyDrop::drop(&mut key) }
492 }
493 r
494 }
495
496 #[inline]
505 pub fn get_or_try_insert_with_ref<'a, Q, F, Cvt, E>(
506 &self,
507 key: &'a Q,
508 f: F,
509 cvt: Cvt,
510 ) -> Result<V, E>
511 where
512 Q: ?Sized + Hash + Equivalent<K>,
513 F: FnOnce(&'a Q) -> Result<V, E>,
514 Cvt: FnOnce(&'a Q) -> K,
515 {
516 let (bucket, tag) = self.calc(key);
517 if let Some(v) = self.get_inner(key, bucket, tag) {
518 return Ok(v);
519 }
520 let value = f(key)?;
521 self.insert_inner(bucket, tag, || (cvt(key), value.clone()));
522 Ok(value)
523 }
524
525 #[inline]
526 fn calc<Q: ?Sized + Hash + Equivalent<K>>(&self, key: &Q) -> (&Bucket<(K, V)>, usize) {
527 let hash = self.hash_key(key);
528 let bucket = unsafe { (&*self.entries).get_unchecked(hash & self.index_mask()) };
530 let mut tag = hash & self.tag_mask();
531 if Self::NEEDS_DROP {
532 tag |= ALIVE_BIT;
533 }
534 if C::EPOCHS {
535 tag = (tag & !EPOCH_MASK) | ((self.epoch() << EPOCH_SHIFT) & EPOCH_MASK);
536 }
537 (bucket, tag)
538 }
539
540 #[inline]
541 fn hash_key<Q: ?Sized + Hash + Equivalent<K>>(&self, key: &Q) -> usize {
542 let hash = self.build_hasher.hash_one(key);
543
544 if cfg!(target_pointer_width = "32") {
545 ((hash >> 32) as usize) ^ (hash as usize)
546 } else {
547 hash as usize
548 }
549 }
550}
551
552impl<K, V, S, C: CacheConfig> Drop for Cache<K, V, S, C> {
553 fn drop(&mut self) {
554 #[cfg(feature = "alloc")]
555 if self.drop {
556 drop(unsafe { alloc::boxed::Box::from_raw(self.entries.cast_mut()) });
558 }
559 }
560}
561
562#[repr(C, align(128))]
571#[doc(hidden)]
572pub struct Bucket<T> {
573 tag: AtomicUsize,
574 data: UnsafeCell<MaybeUninit<T>>,
575}
576
577impl<T> Bucket<T> {
578 const NEEDS_DROP: bool = mem::needs_drop::<T>();
579
580 #[inline]
582 pub const fn new() -> Self {
583 Self { tag: AtomicUsize::new(0), data: UnsafeCell::new(MaybeUninit::zeroed()) }
584 }
585
586 #[inline]
587 fn try_lock(&self, expected: Option<usize>) -> bool {
588 self.try_lock_ret(expected).is_some()
589 }
590
591 #[inline]
592 fn try_lock_ret(&self, expected: Option<usize>) -> Option<usize> {
593 let state = self.tag.load(Ordering::Relaxed);
594 if let Some(expected) = expected {
595 if state != expected {
596 return None;
597 }
598 } else if state & LOCKED_BIT != 0 {
599 return None;
600 }
601 self.tag
602 .compare_exchange(state, state | LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
603 .ok()
604 }
605
606 #[inline]
607 fn is_alive(&self) -> bool {
608 self.tag.load(Ordering::Relaxed) & ALIVE_BIT != 0
609 }
610
611 #[inline]
612 fn unlock(&self, tag: usize) {
613 self.tag.store(tag, Ordering::Release);
614 }
615}
616
617unsafe impl<T: Send> Send for Bucket<T> {}
619unsafe impl<T: Send> Sync for Bucket<T> {}
620
621impl<T> Drop for Bucket<T> {
622 fn drop(&mut self) {
623 if Self::NEEDS_DROP && self.is_alive() {
624 unsafe { self.data.get_mut().assume_init_drop() };
626 }
627 }
628}
629
630#[macro_export]
653macro_rules! static_cache {
654 ($K:ty, $V:ty, $size:expr) => {
655 $crate::static_cache!($K, $V, $size, Default::default())
656 };
657 ($K:ty, $V:ty, $size:expr, $hasher:expr) => {{
658 static ENTRIES: [$crate::Bucket<($K, $V)>; $size] =
659 [const { $crate::Bucket::new() }; $size];
660 $crate::Cache::new_static(&ENTRIES, $hasher)
661 }};
662}
663
664#[cfg(test)]
665mod tests {
666 use super::*;
667 use std::thread;
668
669 const fn iters(n: usize) -> usize {
670 if cfg!(miri) { n / 10 } else { n }
671 }
672
673 type BH = std::hash::BuildHasherDefault<rapidhash::fast::RapidHasher<'static>>;
674 type Cache<K, V> = super::Cache<K, V, BH>;
675
676 struct EpochConfig;
677 impl CacheConfig for EpochConfig {
678 const EPOCHS: bool = true;
679 }
680 type EpochCache<K, V> = super::Cache<K, V, BH, EpochConfig>;
681
682 struct NoStatsConfig;
683 impl CacheConfig for NoStatsConfig {
684 const STATS: bool = false;
685 }
686 type NoStatsCache<K, V> = super::Cache<K, V, BH, NoStatsConfig>;
687
688 fn new_cache<K: Hash + Eq, V: Clone>(size: usize) -> Cache<K, V> {
689 Cache::new(size, Default::default())
690 }
691
692 #[test]
693 fn basic_get_or_insert() {
694 let cache = new_cache(1024);
695
696 let mut computed = false;
697 let value = cache.get_or_insert_with(42, |&k| {
698 computed = true;
699 k * 2
700 });
701 assert!(computed);
702 assert_eq!(value, 84);
703
704 computed = false;
705 let value = cache.get_or_insert_with(42, |&k| {
706 computed = true;
707 k * 2
708 });
709 assert!(!computed);
710 assert_eq!(value, 84);
711 }
712
713 #[test]
714 fn different_keys() {
715 let cache: Cache<String, usize> = new_cache(1024);
716
717 let v1 = cache.get_or_insert_with("hello".to_string(), |s| s.len());
718 let v2 = cache.get_or_insert_with("world!".to_string(), |s| s.len());
719
720 assert_eq!(v1, 5);
721 assert_eq!(v2, 6);
722 }
723
724 #[test]
725 fn new_dynamic_allocation() {
726 let cache: Cache<u32, u32> = new_cache(64);
727 assert_eq!(cache.capacity(), 64);
728
729 cache.insert(1, 100);
730 assert_eq!(cache.get(&1), Some(100));
731 }
732
733 #[test]
734 fn get_miss() {
735 let cache = new_cache::<u64, u64>(64);
736 assert_eq!(cache.get(&999), None);
737 }
738
739 #[test]
740 fn insert_and_get() {
741 let cache: Cache<u64, String> = new_cache(64);
742
743 cache.insert(1, "one".to_string());
744 cache.insert(2, "two".to_string());
745 cache.insert(3, "three".to_string());
746
747 assert_eq!(cache.get(&1), Some("one".to_string()));
748 assert_eq!(cache.get(&2), Some("two".to_string()));
749 assert_eq!(cache.get(&3), Some("three".to_string()));
750 assert_eq!(cache.get(&4), None);
751 }
752
753 #[test]
754 fn insert_twice() {
755 let cache = new_cache(64);
756
757 cache.insert(42, 1);
758 assert_eq!(cache.get(&42), Some(1));
759
760 cache.insert(42, 2);
761 let v = cache.get(&42);
762 assert!(v == Some(1) || v == Some(2));
763 }
764
765 #[test]
766 fn remove_existing() {
767 let cache: Cache<u64, String> = new_cache(64);
768
769 cache.insert(1, "one".to_string());
770 assert_eq!(cache.get(&1), Some("one".to_string()));
771
772 let removed = cache.remove(&1);
773 assert_eq!(removed, Some("one".to_string()));
774 assert_eq!(cache.get(&1), None);
775 }
776
777 #[test]
778 fn remove_nonexistent() {
779 let cache = new_cache::<u64, u64>(64);
780 assert_eq!(cache.remove(&999), None);
781 }
782
783 #[test]
784 fn get_or_insert_with_ref() {
785 let cache: Cache<String, usize> = new_cache(64);
786
787 let key = "hello";
788 let value = cache.get_or_insert_with_ref(key, |s| s.len(), |s| s.to_string());
789 assert_eq!(value, 5);
790
791 let value2 = cache.get_or_insert_with_ref(key, |_| 999, |s| s.to_string());
792 assert_eq!(value2, 5);
793 }
794
795 #[test]
796 fn get_or_insert_with_ref_different_keys() {
797 let cache: Cache<String, usize> = new_cache(1024);
798
799 let v1 = cache.get_or_insert_with_ref("foo", |s| s.len(), |s| s.to_string());
800 let v2 = cache.get_or_insert_with_ref("barbaz", |s| s.len(), |s| s.to_string());
801
802 assert_eq!(v1, 3);
803 assert_eq!(v2, 6);
804 }
805
806 #[test]
807 fn capacity() {
808 let cache = new_cache::<u64, u64>(256);
809 assert_eq!(cache.capacity(), 256);
810
811 let cache2 = new_cache::<u64, u64>(128);
812 assert_eq!(cache2.capacity(), 128);
813 }
814
815 #[test]
816 fn hasher() {
817 let cache = new_cache::<u64, u64>(64);
818 let _ = cache.hasher();
819 }
820
821 #[test]
822 fn debug_impl() {
823 let cache = new_cache::<u64, u64>(64);
824 let debug_str = format!("{:?}", cache);
825 assert!(debug_str.contains("Cache"));
826 }
827
828 #[test]
829 fn bucket_new() {
830 let bucket: Bucket<(u64, u64)> = Bucket::new();
831 assert_eq!(bucket.tag.load(Ordering::Relaxed), 0);
832 }
833
834 #[test]
835 fn many_entries() {
836 let cache: Cache<u64, u64> = new_cache(1024);
837 let n = iters(500);
838
839 for i in 0..n as u64 {
840 cache.insert(i, i * 2);
841 }
842
843 let mut hits = 0;
844 for i in 0..n as u64 {
845 if cache.get(&i) == Some(i * 2) {
846 hits += 1;
847 }
848 }
849 assert!(hits > 0);
850 }
851
852 #[test]
853 fn string_keys() {
854 let cache: Cache<String, i32> = new_cache(1024);
855
856 cache.insert("alpha".to_string(), 1);
857 cache.insert("beta".to_string(), 2);
858 cache.insert("gamma".to_string(), 3);
859
860 assert_eq!(cache.get("alpha"), Some(1));
861 assert_eq!(cache.get("beta"), Some(2));
862 assert_eq!(cache.get("gamma"), Some(3));
863 }
864
865 #[test]
866 fn zero_values() {
867 let cache: Cache<u64, u64> = new_cache(64);
868
869 cache.insert(0, 0);
870 assert_eq!(cache.get(&0), Some(0));
871
872 cache.insert(1, 0);
873 assert_eq!(cache.get(&1), Some(0));
874 }
875
876 #[test]
877 fn clone_value() {
878 #[derive(Clone, PartialEq, Debug)]
879 struct MyValue(u64);
880
881 let cache: Cache<u64, MyValue> = new_cache(64);
882
883 cache.insert(1, MyValue(123));
884 let v = cache.get(&1);
885 assert_eq!(v, Some(MyValue(123)));
886 }
887
888 fn run_concurrent<F>(num_threads: usize, f: F)
889 where
890 F: Fn(usize) + Send + Sync,
891 {
892 thread::scope(|s| {
893 for t in 0..num_threads {
894 let f = &f;
895 s.spawn(move || f(t));
896 }
897 });
898 }
899
900 #[test]
901 fn concurrent_reads() {
902 let cache: Cache<u64, u64> = new_cache(1024);
903 let n = iters(100);
904
905 for i in 0..n as u64 {
906 cache.insert(i, i * 10);
907 }
908
909 run_concurrent(4, |_| {
910 for i in 0..n as u64 {
911 let _ = cache.get(&i);
912 }
913 });
914 }
915
916 #[test]
917 fn concurrent_writes() {
918 let cache: Cache<u64, u64> = new_cache(1024);
919 let n = iters(100);
920
921 run_concurrent(4, |t| {
922 for i in 0..n {
923 cache.insert((t * 1000 + i) as u64, i as u64);
924 }
925 });
926 }
927
928 #[test]
929 fn concurrent_read_write() {
930 let cache: Cache<u64, u64> = new_cache(256);
931 let n = iters(1000);
932
933 run_concurrent(2, |t| {
934 for i in 0..n as u64 {
935 if t == 0 {
936 cache.insert(i % 100, i);
937 } else {
938 let _ = cache.get(&(i % 100));
939 }
940 }
941 });
942 }
943
944 #[test]
945 fn concurrent_get_or_insert() {
946 let cache: Cache<u64, u64> = new_cache(1024);
947 let n = iters(100);
948
949 run_concurrent(8, |_| {
950 for i in 0..n as u64 {
951 let _ = cache.get_or_insert_with(i, |&k| k * 2);
952 }
953 });
954
955 for i in 0..n as u64 {
956 if let Some(v) = cache.get(&i) {
957 assert_eq!(v, i * 2);
958 }
959 }
960 }
961
962 #[test]
963 #[should_panic = "power of two"]
964 fn non_power_of_two() {
965 let _ = new_cache::<u64, u64>(100);
966 }
967
968 #[test]
969 #[should_panic = "len must have its bottom N bits set to zero"]
970 fn small_cache() {
971 let _ = new_cache::<u64, u64>(2);
972 }
973
974 #[test]
975 fn power_of_two_sizes() {
976 for shift in 2..10 {
977 let size = 1 << shift;
978 let cache = new_cache::<u64, u64>(size);
979 assert_eq!(cache.capacity(), size);
980 }
981 }
982
983 #[test]
984 fn equivalent_key_lookup() {
985 let cache: Cache<String, i32> = new_cache(64);
986
987 cache.insert("hello".to_string(), 42);
988
989 assert_eq!(cache.get("hello"), Some(42));
990 }
991
992 #[test]
993 fn large_values() {
994 let cache: Cache<u64, [u8; 1000]> = new_cache(64);
995
996 let large_value = [42u8; 1000];
997 cache.insert(1, large_value);
998
999 assert_eq!(cache.get(&1), Some(large_value));
1000 }
1001
1002 #[test]
1003 fn send_sync() {
1004 fn assert_send<T: Send>() {}
1005 fn assert_sync<T: Sync>() {}
1006
1007 assert_send::<Cache<u64, u64>>();
1008 assert_sync::<Cache<u64, u64>>();
1009 assert_send::<Bucket<(u64, u64)>>();
1010 assert_sync::<Bucket<(u64, u64)>>();
1011 }
1012
1013 #[test]
1014 fn get_or_try_insert_with_ok() {
1015 let cache = new_cache(1024);
1016
1017 let mut computed = false;
1018 let result: Result<u64, &str> = cache.get_or_try_insert_with(42, |&k| {
1019 computed = true;
1020 Ok(k * 2)
1021 });
1022 assert!(computed);
1023 assert_eq!(result, Ok(84));
1024
1025 computed = false;
1026 let result: Result<u64, &str> = cache.get_or_try_insert_with(42, |&k| {
1027 computed = true;
1028 Ok(k * 2)
1029 });
1030 assert!(!computed);
1031 assert_eq!(result, Ok(84));
1032 }
1033
1034 #[test]
1035 fn get_or_try_insert_with_err() {
1036 let cache: Cache<u64, u64> = new_cache(1024);
1037
1038 let result: Result<u64, &str> = cache.get_or_try_insert_with(42, |_| Err("failed"));
1039 assert_eq!(result, Err("failed"));
1040
1041 assert_eq!(cache.get(&42), None);
1042 }
1043
1044 #[test]
1045 fn get_or_try_insert_with_ref_ok() {
1046 let cache: Cache<String, usize> = new_cache(64);
1047
1048 let key = "hello";
1049 let result: Result<usize, &str> =
1050 cache.get_or_try_insert_with_ref(key, |s| Ok(s.len()), |s| s.to_string());
1051 assert_eq!(result, Ok(5));
1052
1053 let result2: Result<usize, &str> =
1054 cache.get_or_try_insert_with_ref(key, |_| Ok(999), |s| s.to_string());
1055 assert_eq!(result2, Ok(5));
1056 }
1057
1058 #[test]
1059 fn get_or_try_insert_with_ref_err() {
1060 let cache: Cache<String, usize> = new_cache(64);
1061
1062 let key = "hello";
1063 let result: Result<usize, &str> =
1064 cache.get_or_try_insert_with_ref(key, |_| Err("failed"), |s| s.to_string());
1065 assert_eq!(result, Err("failed"));
1066
1067 assert_eq!(cache.get(key), None);
1068 }
1069
1070 #[test]
1071 fn drop_on_cache_drop() {
1072 use std::sync::atomic::{AtomicUsize, Ordering};
1073
1074 static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
1075
1076 #[derive(Clone, Hash, Eq, PartialEq)]
1077 struct DropKey(u64);
1078 impl Drop for DropKey {
1079 fn drop(&mut self) {
1080 DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1081 }
1082 }
1083
1084 #[derive(Clone)]
1085 struct DropValue(#[allow(dead_code)] u64);
1086 impl Drop for DropValue {
1087 fn drop(&mut self) {
1088 DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1089 }
1090 }
1091
1092 DROP_COUNT.store(0, Ordering::SeqCst);
1093 {
1094 let cache: super::Cache<DropKey, DropValue, BH> =
1095 super::Cache::new(64, Default::default());
1096 cache.insert(DropKey(1), DropValue(100));
1097 cache.insert(DropKey(2), DropValue(200));
1098 cache.insert(DropKey(3), DropValue(300));
1099 assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0);
1100 }
1101 assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 6);
1103 }
1104
1105 #[test]
1106 fn drop_on_eviction() {
1107 use std::sync::atomic::{AtomicUsize, Ordering};
1108
1109 static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
1110
1111 #[derive(Clone, Hash, Eq, PartialEq)]
1112 struct DropKey(u64);
1113 impl Drop for DropKey {
1114 fn drop(&mut self) {
1115 DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1116 }
1117 }
1118
1119 #[derive(Clone)]
1120 struct DropValue(#[allow(dead_code)] u64);
1121 impl Drop for DropValue {
1122 fn drop(&mut self) {
1123 DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1124 }
1125 }
1126
1127 DROP_COUNT.store(0, Ordering::SeqCst);
1128 {
1129 let cache: super::Cache<DropKey, DropValue, BH> =
1130 super::Cache::new(64, Default::default());
1131 cache.insert(DropKey(1), DropValue(100));
1132 assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0);
1133 cache.insert(DropKey(1), DropValue(200));
1135 assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 2);
1137 }
1138 assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 4);
1140 }
1141
1142 #[test]
1143 fn epoch_clear() {
1144 let cache: EpochCache<u64, u64> = EpochCache::new(4096, Default::default());
1145
1146 assert_eq!(cache.epoch(), 0);
1147
1148 cache.insert(1, 100);
1149 cache.insert(2, 200);
1150 assert_eq!(cache.get(&1), Some(100));
1151 assert_eq!(cache.get(&2), Some(200));
1152
1153 cache.clear();
1154 assert_eq!(cache.epoch(), 1);
1155
1156 assert_eq!(cache.get(&1), None);
1157 assert_eq!(cache.get(&2), None);
1158
1159 cache.insert(1, 101);
1160 assert_eq!(cache.get(&1), Some(101));
1161
1162 cache.clear();
1163 assert_eq!(cache.epoch(), 2);
1164 assert_eq!(cache.get(&1), None);
1165 }
1166
1167 #[test]
1168 fn epoch_wrap_around() {
1169 let cache: EpochCache<u64, u64> = EpochCache::new(4096, Default::default());
1170
1171 for _ in 0..300 {
1172 cache.insert(42, 123);
1173 assert_eq!(cache.get(&42), Some(123));
1174 cache.clear();
1175 assert_eq!(cache.get(&42), None);
1176 }
1177 }
1178
1179 #[test]
1180 fn no_stats_config() {
1181 let cache: NoStatsCache<u64, u64> = NoStatsCache::new(64, Default::default());
1182
1183 cache.insert(1, 100);
1184 assert_eq!(cache.get(&1), Some(100));
1185 assert_eq!(cache.get(&999), None);
1186
1187 cache.insert(1, 200);
1188 assert_eq!(cache.get(&1), Some(200));
1189
1190 cache.remove(&1);
1191 assert_eq!(cache.get(&1), None);
1192
1193 let v = cache.get_or_insert_with(42, |&k| k * 2);
1194 assert_eq!(v, 84);
1195 }
1196
1197 #[test]
1198 fn epoch_wraparound_stays_cleared() {
1199 let cache: EpochCache<u64, u64> = EpochCache::new(4096, Default::default());
1200
1201 cache.insert(42, 123);
1202 assert_eq!(cache.get(&42), Some(123));
1203
1204 for i in 0..2048 {
1205 cache.clear();
1206 assert_eq!(cache.get(&42), None, "failed at clear #{i}");
1207 }
1208 }
1209
1210 #[test]
1211 fn epoch_repeated_wraparound_stays_cleared() {
1212 let cache: EpochCache<u64, u64> = EpochCache::new(4096, Default::default());
1213
1214 for _ in 0..1024 {
1215 cache.clear();
1216 }
1217
1218 cache.insert(42, 123);
1219 assert_eq!(cache.get(&42), Some(123));
1220
1221 for i in 0..1024 {
1222 cache.clear();
1223 assert_eq!(cache.get(&42), None, "failed at clear #{i}");
1224 }
1225 }
1226
1227 #[test]
1228 fn remove_copy_type() {
1229 let cache = new_cache::<u64, u64>(64);
1230
1231 cache.insert(1, 100);
1232 assert_eq!(cache.get(&1), Some(100));
1233
1234 let removed = cache.remove(&1);
1235 assert_eq!(removed, Some(100));
1236 assert_eq!(cache.get(&1), None);
1237
1238 cache.insert(1, 200);
1239 assert_eq!(cache.get(&1), Some(200));
1240 }
1241
1242 #[test]
1243 fn remove_then_reinsert_copy() {
1244 let cache = new_cache::<u64, u64>(64);
1245
1246 for i in 0..100u64 {
1247 cache.insert(1, i);
1248 assert_eq!(cache.get(&1), Some(i));
1249 assert_eq!(cache.remove(&1), Some(i));
1250 assert_eq!(cache.get(&1), None);
1251 }
1252 }
1253
1254 #[test]
1255 fn epoch_with_needs_drop() {
1256 let cache: EpochCache<String, String> = EpochCache::new(4096, Default::default());
1257
1258 cache.insert("key".to_string(), "value".to_string());
1259 assert_eq!(cache.get("key"), Some("value".to_string()));
1260
1261 cache.clear();
1262 assert_eq!(cache.get("key"), None);
1263
1264 cache.insert("key".to_string(), "value2".to_string());
1265 assert_eq!(cache.get("key"), Some("value2".to_string()));
1266 }
1267
1268 #[test]
1269 fn epoch_remove() {
1270 let cache: EpochCache<u64, u64> = EpochCache::new(4096, Default::default());
1271
1272 cache.insert(1, 100);
1273 assert_eq!(cache.remove(&1), Some(100));
1274 assert_eq!(cache.get(&1), None);
1275
1276 cache.insert(1, 200);
1277 assert_eq!(cache.get(&1), Some(200));
1278
1279 cache.clear();
1280 assert_eq!(cache.get(&1), None);
1281 assert_eq!(cache.remove(&1), None);
1282 }
1283
1284 #[test]
1285 fn no_stats_needs_drop() {
1286 let cache: NoStatsCache<String, String> = NoStatsCache::new(64, Default::default());
1287
1288 cache.insert("a".to_string(), "b".to_string());
1289 assert_eq!(cache.get("a"), Some("b".to_string()));
1290
1291 cache.insert("a".to_string(), "c".to_string());
1292 assert_eq!(cache.get("a"), Some("c".to_string()));
1293
1294 cache.remove(&"a".to_string());
1295 assert_eq!(cache.get("a"), None);
1296 }
1297
1298 #[test]
1299 fn no_stats_get_or_insert() {
1300 let cache: NoStatsCache<String, usize> = NoStatsCache::new(64, Default::default());
1301
1302 let v = cache.get_or_insert_with_ref("hello", |s| s.len(), |s| s.to_string());
1303 assert_eq!(v, 5);
1304
1305 let v2 = cache.get_or_insert_with_ref("hello", |_| 999, |s| s.to_string());
1306 assert_eq!(v2, 5);
1307 }
1308
1309 #[test]
1310 fn epoch_concurrent() {
1311 let cache: EpochCache<u64, u64> = EpochCache::new(4096, Default::default());
1312 let n = iters(10_000);
1313
1314 run_concurrent(4, |t| {
1315 for i in 0..n as u64 {
1316 match t {
1317 0 => {
1318 cache.insert(i % 50, i);
1319 }
1320 1 => {
1321 let _ = cache.get(&(i % 50));
1322 }
1323 2 => {
1324 if i % 100 == 0 {
1325 cache.clear();
1326 }
1327 }
1328 _ => {
1329 let _ = cache.remove(&(i % 50));
1330 }
1331 }
1332 }
1333 });
1334 }
1335}