1use super::{
2 deques::Deques, AccessTime, CacheBuilder, Iter, KeyDate, KeyHashDate, ValueEntry, Weigher,
3};
4use crate::{
5 common::{
6 self,
7 deque::{DeqNode, Deque},
8 frequency_sketch::FrequencySketch,
9 time::{CheckedTimeOps, Clock, Instant},
10 CacheRegion,
11 },
12 Policy,
13};
14
15use smallvec::SmallVec;
16use std::{
17 borrow::Borrow,
18 collections::{hash_map::RandomState, HashMap},
19 fmt,
20 hash::{BuildHasher, Hash, Hasher},
21 ptr::NonNull,
22 rc::Rc,
23 time::Duration,
24};
25
26const EVICTION_BATCH_SIZE: usize = 100;
27
28type CacheStore<K, V, S> = std::collections::HashMap<Rc<K>, ValueEntry<K, V>, S>;
29
30pub struct Cache<K, V, S = RandomState> {
168 max_capacity: Option<u64>,
169 entry_count: u64,
170 weighted_size: u64,
171 cache: CacheStore<K, V, S>,
172 build_hasher: S,
173 weigher: Option<Weigher<K, V>>,
174 deques: Deques<K>,
175 frequency_sketch: FrequencySketch,
176 frequency_sketch_enabled: bool,
177 time_to_live: Option<Duration>,
178 time_to_idle: Option<Duration>,
179 expiration_clock: Option<Clock>,
180}
181
182impl<K, V, S> fmt::Debug for Cache<K, V, S>
183where
184 K: fmt::Debug + Eq + Hash,
185 V: fmt::Debug,
186 S: BuildHasher + Clone,
188{
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 let mut d_map = f.debug_map();
191
192 for (k, v) in self.iter() {
193 d_map.entry(&k, &v);
194 }
195
196 d_map.finish()
197 }
198}
199
200impl<K, V> Cache<K, V, RandomState>
201where
202 K: Hash + Eq,
203{
204 pub fn new(max_capacity: u64) -> Self {
211 let build_hasher = RandomState::default();
212 Self::with_everything(Some(max_capacity), None, build_hasher, None, None, None)
213 }
214
215 pub fn builder() -> CacheBuilder<K, V, Cache<K, V, RandomState>> {
220 CacheBuilder::default()
221 }
222}
223
224impl<K, V, S> Cache<K, V, S> {
228 pub fn policy(&self) -> Policy {
233 Policy::new(self.max_capacity, self.time_to_live, self.time_to_idle)
234 }
235
236 pub fn entry_count(&self) -> u64 {
257 self.entry_count
258 }
259
260 pub fn weighted_size(&self) -> u64 {
264 self.weighted_size
265 }
266}
267
268impl<K, V, S> Cache<K, V, S>
269where
270 K: Hash + Eq,
271 S: BuildHasher + Clone,
272{
273 pub(crate) fn with_everything(
274 max_capacity: Option<u64>,
275 initial_capacity: Option<usize>,
276 build_hasher: S,
277 weigher: Option<Weigher<K, V>>,
278 time_to_live: Option<Duration>,
279 time_to_idle: Option<Duration>,
280 ) -> Self {
281 let cache = HashMap::with_capacity_and_hasher(
282 initial_capacity.unwrap_or_default(),
283 build_hasher.clone(),
284 );
285
286 Self {
287 max_capacity,
288 entry_count: 0,
289 weighted_size: 0,
290 cache,
291 build_hasher,
292 weigher,
293 deques: Default::default(),
294 frequency_sketch: Default::default(),
295 frequency_sketch_enabled: false,
296 time_to_live,
297 time_to_idle,
298 expiration_clock: None,
299 }
300 }
301
302 pub fn contains_key<Q>(&mut self, key: &Q) -> bool
311 where
312 Rc<K>: Borrow<Q>,
313 Q: Hash + Eq + ?Sized,
314 {
315 let timestamp = self.evict_expired_if_needed();
316 self.evict_lru_entries();
317
318 match (self.cache.get(key), timestamp) {
319 (None, _) => false,
321 (Some(_), None) => true,
323 (Some(entry), Some(ts)) => {
325 !Self::is_expired_entry_wo(&self.time_to_live, entry, ts)
326 && !Self::is_expired_entry_ao(&self.time_to_idle, entry, ts)
327 }
328 }
329 }
330
331 pub fn get<Q>(&mut self, key: &Q) -> Option<&V>
336 where
337 Rc<K>: Borrow<Q>,
338 Q: Hash + Eq + ?Sized,
339 {
340 let timestamp = self.evict_expired_if_needed();
341 self.evict_lru_entries();
342 self.frequency_sketch.increment(self.hash(key));
343
344 match (self.cache.get_mut(key), timestamp, &mut self.deques) {
345 (None, _, _) => None,
347 (Some(entry), None, deqs) => {
349 Self::record_hit(deqs, entry, None);
350 Some(&entry.value)
351 }
352 (Some(entry), Some(ts), deqs) => {
354 if Self::is_expired_entry_wo(&self.time_to_live, entry, ts)
355 || Self::is_expired_entry_ao(&self.time_to_idle, entry, ts)
356 {
357 None
358 } else {
359 Self::record_hit(deqs, entry, timestamp);
360 Some(&entry.value)
361 }
362 }
363 }
364 }
365
366 pub(crate) fn is_expired_entry(&self, entry: &ValueEntry<K, V>) -> bool {
367 let now = self.current_time_from_expiration_clock();
368 Self::is_expired_entry_wo(&self.time_to_live, entry, now)
369 || Self::is_expired_entry_ao(&self.time_to_idle, entry, now)
370 }
371
372 pub fn insert(&mut self, key: K, value: V) {
376 let timestamp = self.evict_expired_if_needed();
377 self.evict_lru_entries();
378 let policy_weight = weigh(&mut self.weigher, &key, &value);
379 let key = Rc::new(key);
380 let entry = ValueEntry::new(value, policy_weight);
381
382 if let Some(old_entry) = self.cache.insert(Rc::clone(&key), entry) {
383 self.handle_update(key, timestamp, policy_weight, old_entry);
384 } else {
385 let hash = self.hash(&key);
386 self.handle_insert(key, hash, policy_weight, timestamp);
387 }
388 }
389
390 pub fn invalidate<Q>(&mut self, key: &Q)
395 where
396 Rc<K>: Borrow<Q>,
397 Q: Hash + Eq + ?Sized,
398 {
399 self.evict_expired_if_needed();
400 self.evict_lru_entries();
401
402 if let Some(mut entry) = self.cache.remove(key) {
403 let weight = entry.policy_weight();
404 self.deques.unlink_ao(&mut entry);
405 Deques::unlink_wo(&mut self.deques.write_order, &mut entry);
406 self.saturating_sub_from_total_weight(weight as u64);
407 }
408 }
409
410 pub fn invalidate_all(&mut self) {
416 self.cache.clear();
417 self.deques.clear();
418 self.weighted_size = 0;
419 }
420
421 #[allow(clippy::needless_collect)]
435 pub fn invalidate_entries_if(&mut self, mut predicate: impl FnMut(&K, &V) -> bool) {
436 let Self { cache, deques, .. } = self;
437
438 let keys_to_invalidate = cache
444 .iter()
445 .filter(|(key, entry)| (predicate)(key, &entry.value))
446 .map(|(key, _)| Rc::clone(key))
447 .collect::<Vec<_>>();
448
449 let mut invalidated = 0u64;
450
451 keys_to_invalidate.into_iter().for_each(|k| {
452 if let Some(mut entry) = cache.remove(&k) {
453 let weight = entry.policy_weight();
454 deques.unlink_ao(&mut entry);
455 Deques::unlink_wo(&mut deques.write_order, &mut entry);
456 invalidated = invalidated.saturating_sub(weight as u64);
457 }
458 });
459 self.saturating_sub_from_total_weight(invalidated);
460 }
461
462 pub fn iter(&self) -> Iter<'_, K, V, S> {
485 Iter::new(self, self.cache.iter())
486 }
487}
488
489impl<K, V, S> Cache<K, V, S>
493where
494 K: Hash + Eq,
495 S: BuildHasher + Clone,
496{
497 #[inline]
498 fn hash<Q>(&self, key: &Q) -> u64
499 where
500 Rc<K>: Borrow<Q>,
501 Q: Hash + Eq + ?Sized,
502 {
503 let mut hasher = self.build_hasher.build_hasher();
504 key.hash(&mut hasher);
505 hasher.finish()
506 }
507
508 #[inline]
509 fn has_expiry(&self) -> bool {
510 self.time_to_live.is_some() || self.time_to_idle.is_some()
511 }
512
513 #[inline]
514 fn evict_expired_if_needed(&mut self) -> Option<Instant> {
515 if self.has_expiry() {
516 let ts = self.current_time_from_expiration_clock();
517 self.evict_expired(ts);
518 Some(ts)
519 } else {
520 None
521 }
522 }
523
524 #[inline]
525 fn current_time_from_expiration_clock(&self) -> Instant {
526 if let Some(clock) = &self.expiration_clock {
527 Instant::new(clock.now())
528 } else {
529 Instant::now()
530 }
531 }
532
533 #[inline]
534 fn is_expired_entry_ao(
535 time_to_idle: &Option<Duration>,
536 entry: &impl AccessTime,
537 now: Instant,
538 ) -> bool {
539 if let (Some(ts), Some(tti)) = (entry.last_accessed(), time_to_idle) {
540 let checked_add = ts.checked_add(*tti);
541 if checked_add.is_none() {
542 panic!("ttl overflow")
543 }
544 return checked_add.unwrap() <= now;
545 }
546 false
547 }
548
549 #[inline]
550 fn is_expired_entry_wo(
551 time_to_live: &Option<Duration>,
552 entry: &impl AccessTime,
553 now: Instant,
554 ) -> bool {
555 if let (Some(ts), Some(ttl)) = (entry.last_modified(), time_to_live) {
556 let checked_add = ts.checked_add(*ttl);
557 if checked_add.is_none() {
558 panic!("ttl overflow")
559 }
560 return checked_add.unwrap() <= now;
561 }
562 false
563 }
564
565 fn record_hit(deques: &mut Deques<K>, entry: &mut ValueEntry<K, V>, ts: Option<Instant>) {
566 if let Some(ts) = ts {
567 entry.set_last_accessed(ts);
568 }
569 deques.move_to_back_ao(entry)
570 }
571
572 fn has_enough_capacity(&self, candidate_weight: u32, ws: u64) -> bool {
573 self.max_capacity
574 .map(|limit| ws + candidate_weight as u64 <= limit)
575 .unwrap_or(true)
576 }
577
578 fn weights_to_evict(&self) -> u64 {
579 self.max_capacity
580 .map(|limit| self.weighted_size.saturating_sub(limit))
581 .unwrap_or_default()
582 }
583
584 #[inline]
585 fn should_enable_frequency_sketch(&self) -> bool {
586 if self.frequency_sketch_enabled {
587 false
588 } else if let Some(max_cap) = self.max_capacity {
589 self.weighted_size >= max_cap / 2
590 } else {
591 false
592 }
593 }
594
595 #[inline]
596 fn enable_frequency_sketch(&mut self) {
597 if let Some(max_cap) = self.max_capacity {
598 let cap = if self.weigher.is_none() {
599 max_cap
600 } else {
601 (self.entry_count as f64 * (self.weighted_size as f64 / max_cap as f64)) as u64
602 };
603 self.do_enable_frequency_sketch(cap);
604 }
605 }
606
607 #[cfg(test)]
608 fn enable_frequency_sketch_for_testing(&mut self) {
609 if let Some(max_cap) = self.max_capacity {
610 self.do_enable_frequency_sketch(max_cap);
611 }
612 }
613
614 #[inline]
615 fn do_enable_frequency_sketch(&mut self, cache_capacity: u64) {
616 let skt_capacity = common::sketch_capacity(cache_capacity);
617 self.frequency_sketch.ensure_capacity(skt_capacity);
618 self.frequency_sketch_enabled = true;
619 }
620
621 fn saturating_add_to_total_weight(&mut self, weight: u64) {
622 let total = &mut self.weighted_size;
623 *total = total.saturating_add(weight);
624 }
625
626 fn saturating_sub_from_total_weight(&mut self, weight: u64) {
627 let total = &mut self.weighted_size;
628 *total = total.saturating_sub(weight);
629 }
630
631 #[inline]
632 fn handle_insert(
633 &mut self,
634 key: Rc<K>,
635 hash: u64,
636 policy_weight: u32,
637 timestamp: Option<Instant>,
638 ) {
639 let has_free_space = self.has_enough_capacity(policy_weight, self.weighted_size);
640 let (cache, deqs, freq) = (&mut self.cache, &mut self.deques, &self.frequency_sketch);
641
642 if has_free_space {
643 let key = Rc::clone(&key);
645 let entry = cache.get_mut(&key).unwrap();
646 deqs.push_back_ao(
647 CacheRegion::MainProbation,
648 KeyHashDate::new(Rc::clone(&key), hash, timestamp),
649 entry,
650 );
651 if self.time_to_live.is_some() {
652 deqs.push_back_wo(KeyDate::new(key, timestamp), entry);
653 }
654 self.entry_count += 1;
655 self.saturating_add_to_total_weight(policy_weight as u64);
656
657 if self.should_enable_frequency_sketch() {
658 self.enable_frequency_sketch();
659 }
660
661 return;
662 }
663
664 if let Some(max) = self.max_capacity {
665 if policy_weight as u64 > max {
666 cache.remove(&Rc::clone(&key));
668 return;
669 }
670 }
671
672 let mut candidate = EntrySizeAndFrequency::new(policy_weight as u64);
673 candidate.add_frequency(freq, hash);
674
675 match Self::admit(&candidate, cache, deqs, freq, &mut self.weigher) {
676 AdmissionResult::Admitted {
677 victim_nodes,
678 victims_weight,
679 } => {
680 for victim in victim_nodes {
682 let mut vic_entry = cache
684 .remove(unsafe { &victim.as_ref().element.key })
685 .expect("Cannot remove a victim from the hash map");
686 deqs.unlink_ao(&mut vic_entry);
688 Deques::unlink_wo(&mut deqs.write_order, &mut vic_entry);
689 self.entry_count -= 1;
690 }
691
692 let entry = cache.get_mut(&key).unwrap();
694 let key = Rc::clone(&key);
695 deqs.push_back_ao(
696 CacheRegion::MainProbation,
697 KeyHashDate::new(Rc::clone(&key), hash, timestamp),
698 entry,
699 );
700 if self.time_to_live.is_some() {
701 deqs.push_back_wo(KeyDate::new(key, timestamp), entry);
702 }
703
704 self.entry_count += 1;
705 Self::saturating_sub_from_total_weight(self, victims_weight);
706 Self::saturating_add_to_total_weight(self, policy_weight as u64);
707
708 if self.should_enable_frequency_sketch() {
709 self.enable_frequency_sketch();
710 }
711 }
712 AdmissionResult::Rejected => {
713 cache.remove(&key);
715 }
716 }
717 }
718
719 #[inline]
737 fn admit(
738 candidate: &EntrySizeAndFrequency,
739 cache: &CacheStore<K, V, S>,
740 deqs: &Deques<K>,
741 freq: &FrequencySketch,
742 weigher: &mut Option<Weigher<K, V>>,
743 ) -> AdmissionResult<K> {
744 let mut victims = EntrySizeAndFrequency::default();
745 let mut victim_nodes = SmallVec::default();
746
747 let mut next_victim = deqs.probation.peek_front_ptr();
749
750 while victims.weight < candidate.weight {
752 if candidate.freq < victims.freq {
753 break;
754 }
755 if let Some(victim) = next_victim.take() {
756 next_victim = DeqNode::next_node_ptr(victim);
757 let vic_elem = &unsafe { victim.as_ref() }.element;
758
759 let vic_entry = cache
760 .get(&vic_elem.key)
761 .expect("Cannot get an victim entry");
762 victims.add_policy_weight(vic_elem.key.as_ref(), &vic_entry.value, weigher);
763 victims.add_frequency(freq, vic_elem.hash);
764 victim_nodes.push(victim);
765 } else {
766 break;
768 }
769 }
770
771 if victims.weight >= candidate.weight && candidate.freq > victims.freq {
777 AdmissionResult::Admitted {
778 victim_nodes,
779 victims_weight: victims.weight,
780 }
781 } else {
782 AdmissionResult::Rejected
783 }
784 }
785
786 fn handle_update(
787 &mut self,
788 key: Rc<K>,
789 timestamp: Option<Instant>,
790 policy_weight: u32,
791 old_entry: ValueEntry<K, V>,
792 ) {
793 let old_policy_weight = old_entry.policy_weight();
794
795 let entry = self.cache.get_mut(&key).unwrap();
796 entry.replace_deq_nodes_with(old_entry);
797 if let Some(ts) = timestamp {
798 entry.set_last_accessed(ts);
799 entry.set_last_modified(ts);
800 }
801 entry.set_policy_weight(policy_weight);
802
803 let deqs = &mut self.deques;
804 deqs.move_to_back_ao(entry);
805 if self.time_to_live.is_some() {
806 deqs.move_to_back_wo(entry);
807 }
808
809 self.saturating_sub_from_total_weight(old_policy_weight as u64);
810 self.saturating_add_to_total_weight(policy_weight as u64);
811 }
812
813 fn evict_expired(&mut self, now: Instant) {
814 if self.time_to_live.is_some() {
815 let (count, weight) = self.remove_expired_wo(EVICTION_BATCH_SIZE, now);
816 self.entry_count -= count;
817 self.saturating_sub_from_total_weight(weight);
818 }
819
820 if self.time_to_idle.is_some() {
821 let deqs = &mut self.deques;
822 let (window, probation, protected, wo, cache, time_to_idle) = (
823 &mut deqs.window,
824 &mut deqs.probation,
825 &mut deqs.protected,
826 &mut deqs.write_order,
827 &mut self.cache,
828 &self.time_to_idle,
829 );
830
831 let mut rm_expired_ao = |name, deq| {
832 Self::remove_expired_ao(
833 name,
834 deq,
835 wo,
836 cache,
837 time_to_idle,
838 EVICTION_BATCH_SIZE,
839 now,
840 )
841 };
842
843 let (count1, weight1) = rm_expired_ao("window", window);
844 let (count2, weight2) = rm_expired_ao("probation", probation);
845 let (count3, weight3) = rm_expired_ao("protected", protected);
846
847 self.entry_count -= count1 + count2 + count3;
848 self.saturating_sub_from_total_weight(weight1);
849 self.saturating_sub_from_total_weight(weight2);
850 self.saturating_sub_from_total_weight(weight3);
851 }
852 }
853
854 #[inline]
856 fn remove_expired_ao(
857 deq_name: &str,
858 deq: &mut Deque<KeyHashDate<K>>,
859 write_order_deq: &mut Deque<KeyDate<K>>,
860 cache: &mut CacheStore<K, V, S>,
861 time_to_idle: &Option<Duration>,
862 batch_size: usize,
863 now: Instant,
864 ) -> (u64, u64) {
865 let mut evicted_entry_count = 0u64;
866 let mut evicted_policy_weight = 0u64;
867
868 for _ in 0..batch_size {
869 let key = deq
870 .peek_front()
871 .and_then(|node| {
872 if Self::is_expired_entry_ao(time_to_idle, node, now) {
873 Some(Some(Rc::clone(&node.element.key)))
874 } else {
875 None
876 }
877 })
878 .unwrap_or_default();
879
880 if key.is_none() {
881 break;
882 }
883
884 let key = key.unwrap();
885
886 if let Some(mut entry) = cache.remove(&key) {
887 let weight = entry.policy_weight();
888 Deques::unlink_ao_from_deque(deq_name, deq, &mut entry);
889 Deques::unlink_wo(write_order_deq, &mut entry);
890 evicted_entry_count += 1;
891 evicted_policy_weight = evicted_policy_weight.saturating_add(weight as u64);
892 } else {
893 deq.pop_front();
894 }
895 }
896
897 (evicted_entry_count, evicted_policy_weight)
898 }
899
900 #[inline]
902 fn remove_expired_wo(&mut self, batch_size: usize, now: Instant) -> (u64, u64) {
903 let mut evicted_entry_count = 0u64;
904 let mut evicted_policy_weight = 0u64;
905 let time_to_live = &self.time_to_live;
906
907 for _ in 0..batch_size {
908 let key = self
909 .deques
910 .write_order
911 .peek_front()
912 .and_then(|node| {
913 if Self::is_expired_entry_wo(time_to_live, node, now) {
914 Some(Some(Rc::clone(&node.element.key)))
915 } else {
916 None
917 }
918 })
919 .unwrap_or_default();
920
921 if key.is_none() {
922 break;
923 }
924
925 let key = key.unwrap();
926
927 if let Some(mut entry) = self.cache.remove(&key) {
928 let weight = entry.policy_weight();
929 self.deques.unlink_ao(&mut entry);
930 Deques::unlink_wo(&mut self.deques.write_order, &mut entry);
931 evicted_entry_count += 1;
932 evicted_policy_weight = evicted_policy_weight.saturating_sub(weight as u64);
933 } else {
934 self.deques.write_order.pop_front();
935 }
936 }
937
938 (evicted_entry_count, evicted_policy_weight)
939 }
940
941 #[inline]
942 fn evict_lru_entries(&mut self) {
943 const DEQ_NAME: &str = "probation";
944
945 let weights_to_evict = self.weights_to_evict();
946 let mut evicted_count = 0u64;
947 let mut evicted_policy_weight = 0u64;
948
949 {
950 let deqs = &mut self.deques;
951 let (probation, wo, cache) =
952 (&mut deqs.probation, &mut deqs.write_order, &mut self.cache);
953
954 for _ in 0..EVICTION_BATCH_SIZE {
955 if evicted_policy_weight >= weights_to_evict {
956 break;
957 }
958
959 let key = probation
960 .peek_front()
961 .map(|node| Rc::clone(&node.element.key));
962
963 if key.is_none() {
964 break;
965 }
966 let key = key.unwrap();
967
968 if let Some(mut entry) = cache.remove(&key) {
969 let weight = entry.policy_weight();
970 Deques::unlink_ao_from_deque(DEQ_NAME, probation, &mut entry);
971 Deques::unlink_wo(wo, &mut entry);
972 evicted_count += 1;
973 evicted_policy_weight = evicted_policy_weight.saturating_add(weight as u64);
974 } else {
975 probation.pop_front();
976 }
977 }
978 }
979
980 self.entry_count -= evicted_count;
981 self.saturating_sub_from_total_weight(evicted_policy_weight);
982 }
983}
984
985#[cfg(test)]
989impl<K, V, S> Cache<K, V, S>
990where
991 K: Hash + Eq,
992 S: BuildHasher + Clone,
993{
994 fn set_expiration_clock(&mut self, clock: Option<crate::common::time::Clock>) {
995 self.expiration_clock = clock;
996 }
997}
998
999#[derive(Default)]
1000struct EntrySizeAndFrequency {
1001 weight: u64,
1002 freq: u32,
1003}
1004
1005impl EntrySizeAndFrequency {
1006 fn new(policy_weight: u64) -> Self {
1007 Self {
1008 weight: policy_weight,
1009 ..Default::default()
1010 }
1011 }
1012
1013 fn add_policy_weight<K, V>(&mut self, key: &K, value: &V, weigher: &mut Option<Weigher<K, V>>) {
1014 self.weight += weigh(weigher, key, value) as u64;
1015 }
1016
1017 fn add_frequency(&mut self, freq: &FrequencySketch, hash: u64) {
1018 self.freq += freq.frequency(hash) as u32;
1019 }
1020}
1021
1022type AoqNode<K> = NonNull<DeqNode<KeyHashDate<K>>>;
1024
1025enum AdmissionResult<K> {
1026 Admitted {
1027 victim_nodes: SmallVec<[AoqNode<K>; 8]>,
1028 victims_weight: u64,
1029 },
1030 Rejected,
1031}
1032
1033#[inline]
1037fn weigh<K, V>(weigher: &mut Option<Weigher<K, V>>, key: &K, value: &V) -> u32 {
1038 weigher.as_mut().map(|w| w(key, value)).unwrap_or(1)
1039}
1040
1041#[cfg(test)]
1043mod tests {
1044 use super::Cache;
1045 use crate::common::time::Clock;
1046
1047 use std::time::Duration;
1048
1049 #[test]
1050 fn basic_single_thread() {
1051 let mut cache = Cache::new(3);
1052 cache.enable_frequency_sketch_for_testing();
1053
1054 cache.insert("a", "alice");
1055 cache.insert("b", "bob");
1056 assert_eq!(cache.get(&"a"), Some(&"alice"));
1057 assert!(cache.contains_key(&"a"));
1058 assert!(cache.contains_key(&"b"));
1059 assert_eq!(cache.get(&"b"), Some(&"bob"));
1060 cache.insert("c", "cindy");
1063 assert_eq!(cache.get(&"c"), Some(&"cindy"));
1064 assert!(cache.contains_key(&"c"));
1065 assert!(cache.contains_key(&"a"));
1068 assert_eq!(cache.get(&"a"), Some(&"alice"));
1069 assert_eq!(cache.get(&"b"), Some(&"bob"));
1070 assert!(cache.contains_key(&"b"));
1071 cache.insert("d", "david"); assert_eq!(cache.get(&"d"), None); assert!(!cache.contains_key(&"d"));
1077
1078 cache.insert("d", "david");
1079 assert!(!cache.contains_key(&"d"));
1080 assert_eq!(cache.get(&"d"), None); cache.insert("d", "dennis");
1085 assert_eq!(cache.get(&"a"), Some(&"alice"));
1086 assert_eq!(cache.get(&"b"), Some(&"bob"));
1087 assert_eq!(cache.get(&"c"), None);
1088 assert_eq!(cache.get(&"d"), Some(&"dennis"));
1089 assert!(cache.contains_key(&"a"));
1090 assert!(cache.contains_key(&"b"));
1091 assert!(!cache.contains_key(&"c"));
1092 assert!(cache.contains_key(&"d"));
1093
1094 cache.invalidate(&"b");
1095 assert_eq!(cache.get(&"b"), None);
1096 assert!(!cache.contains_key(&"b"));
1097 }
1098
1099 #[test]
1100 fn size_aware_eviction() {
1101 let weigher = |_k: &&str, v: &(&str, u32)| v.1;
1102
1103 let alice = ("alice", 10);
1104 let bob = ("bob", 15);
1105 let bill = ("bill", 20);
1106 let cindy = ("cindy", 5);
1107 let david = ("david", 15);
1108 let dennis = ("dennis", 15);
1109
1110 let mut cache = Cache::builder().max_capacity(31).weigher(weigher).build();
1111 cache.enable_frequency_sketch_for_testing();
1112
1113 cache.insert("a", alice);
1114 cache.insert("b", bob);
1115 assert_eq!(cache.get(&"a"), Some(&alice));
1116 assert!(cache.contains_key(&"a"));
1117 assert!(cache.contains_key(&"b"));
1118 assert_eq!(cache.get(&"b"), Some(&bob));
1119 cache.insert("c", cindy);
1122 assert_eq!(cache.get(&"c"), Some(&cindy));
1123 assert!(cache.contains_key(&"c"));
1124 assert!(cache.contains_key(&"a"));
1127 assert_eq!(cache.get(&"a"), Some(&alice));
1128 assert_eq!(cache.get(&"b"), Some(&bob));
1129 assert!(cache.contains_key(&"b"));
1130 cache.insert("d", david); assert_eq!(cache.get(&"d"), None); assert!(!cache.contains_key(&"d"));
1138
1139 cache.insert("d", david);
1140 assert!(!cache.contains_key(&"d"));
1141 assert_eq!(cache.get(&"d"), None); cache.insert("d", david);
1144 assert_eq!(cache.get(&"d"), None); assert!(!cache.contains_key(&"d"));
1146
1147 cache.insert("d", david);
1148 assert!(!cache.contains_key(&"d"));
1149 assert_eq!(cache.get(&"d"), None); cache.insert("d", dennis);
1153 assert_eq!(cache.get(&"a"), None);
1154 assert_eq!(cache.get(&"b"), Some(&bob));
1155 assert_eq!(cache.get(&"c"), None);
1156 assert_eq!(cache.get(&"d"), Some(&dennis));
1157 assert!(!cache.contains_key(&"a"));
1158 assert!(cache.contains_key(&"b"));
1159 assert!(!cache.contains_key(&"c"));
1160 assert!(cache.contains_key(&"d"));
1161
1162 cache.insert("b", bill);
1164 assert_eq!(cache.get(&"b"), Some(&bill));
1165 assert_eq!(cache.get(&"d"), None);
1166 assert!(cache.contains_key(&"b"));
1167 assert!(!cache.contains_key(&"d"));
1168
1169 cache.insert("a", alice);
1171 cache.insert("b", bob);
1172 assert_eq!(cache.get(&"a"), Some(&alice));
1173 assert_eq!(cache.get(&"b"), Some(&bob));
1174 assert_eq!(cache.get(&"d"), None);
1175 assert!(cache.contains_key(&"a"));
1176 assert!(cache.contains_key(&"b"));
1177 assert!(!cache.contains_key(&"d"));
1178
1179 assert_eq!(cache.entry_count(), 2);
1181 assert_eq!(cache.weighted_size(), 25);
1182 }
1183
1184 #[test]
1185 fn invalidate_all() {
1186 let mut cache = Cache::new(100);
1187 cache.enable_frequency_sketch_for_testing();
1188
1189 cache.insert("a", "alice");
1190 cache.insert("b", "bob");
1191 cache.insert("c", "cindy");
1192 assert_eq!(cache.get(&"a"), Some(&"alice"));
1193 assert_eq!(cache.get(&"b"), Some(&"bob"));
1194 assert_eq!(cache.get(&"c"), Some(&"cindy"));
1195 assert!(cache.contains_key(&"a"));
1196 assert!(cache.contains_key(&"b"));
1197 assert!(cache.contains_key(&"c"));
1198
1199 cache.invalidate_all();
1200
1201 cache.insert("d", "david");
1202
1203 assert!(cache.get(&"a").is_none());
1204 assert!(cache.get(&"b").is_none());
1205 assert!(cache.get(&"c").is_none());
1206 assert_eq!(cache.get(&"d"), Some(&"david"));
1207 assert!(!cache.contains_key(&"a"));
1208 assert!(!cache.contains_key(&"b"));
1209 assert!(!cache.contains_key(&"c"));
1210 assert!(cache.contains_key(&"d"));
1211 }
1212
1213 #[test]
1214 fn invalidate_entries_if() {
1215 use std::collections::HashSet;
1216
1217 let mut cache = Cache::new(100);
1218 cache.enable_frequency_sketch_for_testing();
1219
1220 let (clock, mock) = Clock::mock();
1221 cache.set_expiration_clock(Some(clock));
1222
1223 cache.insert(0, "alice");
1224 cache.insert(1, "bob");
1225 cache.insert(2, "alex");
1226
1227 mock.increment(Duration::from_secs(5)); assert_eq!(cache.get(&0), Some(&"alice"));
1230 assert_eq!(cache.get(&1), Some(&"bob"));
1231 assert_eq!(cache.get(&2), Some(&"alex"));
1232 assert!(cache.contains_key(&0));
1233 assert!(cache.contains_key(&1));
1234 assert!(cache.contains_key(&2));
1235
1236 let names = ["alice", "alex"].iter().cloned().collect::<HashSet<_>>();
1237 cache.invalidate_entries_if(move |_k, &v| names.contains(v));
1238
1239 mock.increment(Duration::from_secs(5)); cache.insert(3, "alice");
1242
1243 assert!(cache.get(&0).is_none());
1244 assert!(cache.get(&2).is_none());
1245 assert_eq!(cache.get(&1), Some(&"bob"));
1246 assert_eq!(cache.get(&3), Some(&"alice"));
1248
1249 assert!(!cache.contains_key(&0));
1250 assert!(cache.contains_key(&1));
1251 assert!(!cache.contains_key(&2));
1252 assert!(cache.contains_key(&3));
1253
1254 assert_eq!(cache.cache.len(), 2);
1255
1256 mock.increment(Duration::from_secs(5)); cache.invalidate_entries_if(|_k, &v| v == "alice");
1259 cache.invalidate_entries_if(|_k, &v| v == "bob");
1260
1261 assert!(cache.get(&1).is_none());
1262 assert!(cache.get(&3).is_none());
1263
1264 assert!(!cache.contains_key(&1));
1265 assert!(!cache.contains_key(&3));
1266
1267 assert_eq!(cache.cache.len(), 0);
1268 }
1269
1270 #[test]
1271 fn time_to_live() {
1272 let mut cache = Cache::builder()
1273 .max_capacity(100)
1274 .time_to_live(Duration::from_secs(10))
1275 .build();
1276 cache.enable_frequency_sketch_for_testing();
1277
1278 let (clock, mock) = Clock::mock();
1279 cache.set_expiration_clock(Some(clock));
1280
1281 cache.insert("a", "alice");
1282
1283 mock.increment(Duration::from_secs(5)); assert_eq!(cache.get(&"a"), Some(&"alice"));
1286 assert!(cache.contains_key(&"a"));
1287
1288 mock.increment(Duration::from_secs(5)); assert_eq!(cache.get(&"a"), None);
1291 assert!(!cache.contains_key(&"a"));
1292 assert_eq!(cache.iter().count(), 0);
1293 assert!(cache.cache.is_empty());
1294
1295 cache.insert("b", "bob");
1296
1297 assert_eq!(cache.cache.len(), 1);
1298
1299 mock.increment(Duration::from_secs(5)); assert_eq!(cache.get(&"b"), Some(&"bob"));
1302 assert!(cache.contains_key(&"b"));
1303 assert_eq!(cache.cache.len(), 1);
1304
1305 cache.insert("b", "bill");
1306
1307 mock.increment(Duration::from_secs(5)); assert_eq!(cache.get(&"b"), Some(&"bill"));
1310 assert!(cache.contains_key(&"b"));
1311 assert_eq!(cache.cache.len(), 1);
1312
1313 mock.increment(Duration::from_secs(5)); assert_eq!(cache.get(&"a"), None);
1316 assert_eq!(cache.get(&"b"), None);
1317 assert!(!cache.contains_key(&"a"));
1318 assert!(!cache.contains_key(&"b"));
1319 assert_eq!(cache.iter().count(), 0);
1320 assert!(cache.cache.is_empty());
1321 }
1322
1323 #[test]
1324 fn time_to_idle() {
1325 let mut cache = Cache::builder()
1326 .max_capacity(100)
1327 .time_to_idle(Duration::from_secs(10))
1328 .build();
1329 cache.enable_frequency_sketch_for_testing();
1330
1331 let (clock, mock) = Clock::mock();
1332 cache.set_expiration_clock(Some(clock));
1333
1334 cache.insert("a", "alice");
1335
1336 mock.increment(Duration::from_secs(5)); assert_eq!(cache.get(&"a"), Some(&"alice"));
1339
1340 mock.increment(Duration::from_secs(5)); cache.insert("b", "bob");
1343
1344 assert_eq!(cache.cache.len(), 2);
1345
1346 mock.increment(Duration::from_secs(2)); assert!(cache.contains_key(&"a"));
1350 assert!(cache.contains_key(&"b"));
1351
1352 assert_eq!(cache.cache.len(), 2);
1353
1354 mock.increment(Duration::from_secs(3)); assert_eq!(cache.get(&"a"), None);
1357 assert_eq!(cache.get(&"b"), Some(&"bob"));
1358 assert!(!cache.contains_key(&"a"));
1359 assert!(cache.contains_key(&"b"));
1360 assert_eq!(cache.iter().count(), 1);
1361 assert_eq!(cache.cache.len(), 1);
1362
1363 mock.increment(Duration::from_secs(10)); assert_eq!(cache.get(&"a"), None);
1366 assert_eq!(cache.get(&"b"), None);
1367 assert!(!cache.contains_key(&"a"));
1368 assert!(!cache.contains_key(&"b"));
1369 assert_eq!(cache.iter().count(), 0);
1370 assert!(cache.cache.is_empty());
1371 }
1372
1373 #[cfg_attr(target_pointer_width = "16", ignore)]
1374 #[test]
1375 fn test_skt_capacity_will_not_overflow() {
1376 let pot = |exp| 2u64.pow(exp);
1378
1379 let ensure_sketch_len = |max_capacity, len, name| {
1380 let mut cache = Cache::<u8, u8>::new(max_capacity);
1381 cache.enable_frequency_sketch_for_testing();
1382 assert_eq!(cache.frequency_sketch.table_len(), len as usize, "{}", name);
1383 };
1384
1385 if cfg!(target_pointer_width = "32") {
1386 let pot24 = pot(24);
1387 let pot16 = pot(16);
1388 ensure_sketch_len(0, 128, "0");
1389 ensure_sketch_len(128, 128, "128");
1390 ensure_sketch_len(pot16, pot16, "pot16");
1391 ensure_sketch_len(pot16 + 1, pot(17), "pot16 + 1");
1393 ensure_sketch_len(pot24 - 1, pot24, "pot24 - 1");
1395 ensure_sketch_len(pot24, pot24, "pot24");
1396 ensure_sketch_len(pot(27), pot24, "pot(27)");
1397 ensure_sketch_len(u32::MAX as u64, pot24, "u32::MAX");
1398 } else {
1399 let pot30 = pot(30);
1401 let pot16 = pot(16);
1402 ensure_sketch_len(0, 128, "0");
1403 ensure_sketch_len(128, 128, "128");
1404 ensure_sketch_len(pot16, pot16, "pot16");
1405 ensure_sketch_len(pot16 + 1, pot(17), "pot16 + 1");
1407
1408 if !cfg!(circleci) {
1411 ensure_sketch_len(pot30 - 1, pot30, "pot30- 1");
1413 ensure_sketch_len(pot30, pot30, "pot30");
1414 ensure_sketch_len(u64::MAX, pot30, "u64::MAX");
1415 }
1416 };
1417 }
1418
1419 #[test]
1420 fn test_debug_format() {
1421 let mut cache = Cache::new(10);
1422 cache.insert('a', "alice");
1423 cache.insert('b', "bob");
1424 cache.insert('c', "cindy");
1425
1426 let debug_str = format!("{:?}", cache);
1427 assert!(debug_str.starts_with('{'));
1428 assert!(debug_str.contains(r#"'a': "alice""#));
1429 assert!(debug_str.contains(r#"'b': "bob""#));
1430 assert!(debug_str.contains(r#"'c': "cindy""#));
1431 assert!(debug_str.ends_with('}'));
1432 }
1433}