1mod error;
9mod object;
10mod policy;
11mod status;
12mod worker;
13
14use std::{
15 hash::{BuildHasher, BuildHasherDefault, Hash, RandomState},
16 sync::{Arc, atomic::AtomicU64},
17 thread,
18};
19
20use crossbeam_channel::unbounded;
21use dashmap::{DashMap, mapref::entry::Entry};
22use kwik::{fmt, math::set::Multiset};
23use log::{error, info};
24use nohash_hasher::NoHashHasher;
25use typesize::TypeSize;
26
27pub use crate::{error::CacheError, policy::PaperPolicy};
28use crate::{
29 object::{Object, ObjectSize, overhead::OverheadManager},
30 status::{AtomicStatus, Status},
31 worker::{Worker, WorkerEvent, WorkerManager, WorkerSender},
32};
33
34pub type CacheSize = u64;
35pub type AtomicCacheSize = AtomicU64;
36
37pub type HashedKey = u64;
38pub type NoHasher = BuildHasherDefault<NoHashHasher<HashedKey>>;
39
40pub type ObjectMapRef<K, V> = Arc<DashMap<HashedKey, Object<K, V>, NoHasher>>;
41pub type StatusRef = Arc<AtomicStatus>;
42pub type OverheadManagerRef = Arc<OverheadManager>;
43
44pub struct PaperCache<K, V, S = RandomState> {
45 objects: ObjectMapRef<K, V>,
46 status: StatusRef,
47
48 worker_manager: Arc<WorkerSender>,
49 overhead_manager: OverheadManagerRef,
50
51 hasher: S,
52}
53
54impl<K, V, S> PaperCache<K, V, S>
55where
56 K: 'static + Eq + Hash + TypeSize,
57 V: 'static + TypeSize,
58 S: Default + Clone + BuildHasher,
59{
60 pub fn new(
105 max_size: CacheSize,
106 policies: &[PaperPolicy],
107 policy: PaperPolicy,
108 ) -> Result<Self, CacheError> {
109 Self::with_hasher(max_size, policies, policy, Default::default())
110 }
111
112 pub fn with_hasher(
130 max_size: CacheSize,
131 policies: &[PaperPolicy],
132 policy: PaperPolicy,
133 hasher: S,
134 ) -> Result<Self, CacheError> {
135 if max_size == 0 {
136 return Err(CacheError::ZeroCacheSize);
137 }
138
139 if policies.is_empty() {
140 return Err(CacheError::EmptyPolicies);
141 }
142
143 if policies.contains(&PaperPolicy::Auto) {
144 return Err(CacheError::ConfiguredAutoPolicy);
145 }
146
147 if policies.iter().is_multiset() {
148 return Err(CacheError::DuplicatePolicies);
149 }
150
151 if !policy.is_auto() && !policies.contains(&policy) {
152 return Err(CacheError::UnconfiguredPolicy);
153 }
154
155 let objects = Arc::new(DashMap::with_hasher(NoHasher::default()));
156 let status = Arc::new(AtomicStatus::new(max_size, policies, policy)?);
157 let overhead_manager = Arc::new(OverheadManager::new(&status));
158
159 let (worker_sender, worker_listener) = unbounded();
160
161 let mut worker_manager =
162 WorkerManager::new(worker_listener, &objects, &status, &overhead_manager)?;
163
164 thread::spawn(move || worker_manager.run());
165
166 let cache = PaperCache {
167 objects,
168 status,
169
170 worker_manager: Arc::new(worker_sender),
171 overhead_manager,
172
173 hasher,
174 };
175
176 Ok(cache)
177 }
178
179 #[must_use]
194 pub fn version(&self) -> String {
195 env!("CARGO_PKG_VERSION").to_owned()
196 }
197
198 pub fn status(&self) -> Result<Status, CacheError> {
216 self.status.try_to_status()
217 }
218
219 pub fn get(&self, key: &K) -> Result<Arc<V>, CacheError> {
240 let hashed_key = self.hash_key(key);
241
242 let result = match self.objects.get(&hashed_key) {
243 Some(object) if object.key_matches(key) && !object.is_expired() => {
244 self.status.incr_hits();
245 Ok(object.data())
246 },
247
248 _ => {
249 self.status.incr_misses();
250 Err(CacheError::KeyNotFound)
251 },
252 };
253
254 self.broadcast(WorkerEvent::Get(hashed_key, result.is_ok()))?;
255
256 result
257 }
258
259 pub fn set(&self, key: K, value: V, ttl: Option<u32>) -> Result<(), CacheError> {
279 let hashed_key = self.hash_key(&key);
280
281 let object = Object::new(key, value, ttl);
282 let base_size = self.overhead_manager.base_size(&object);
283 let expiry = object.expiry();
284
285 if base_size == 0 {
286 return Err(CacheError::ZeroValueSize);
287 }
288
289 if self.status.exceeds_max_size(base_size) {
290 return Err(CacheError::ExceedingValueSize);
291 }
292
293 self.status.incr_sets();
294
295 let old_object_info = self
296 .objects
297 .insert(hashed_key, object)
298 .map(|old_object| {
299 let base_size = self.overhead_manager.base_size(&old_object);
300 let expiry = old_object.expiry();
301
302 (base_size, expiry)
303 });
304
305 let base_size_delta = if let Some((old_object_size, _)) = old_object_info {
306 base_size as i64 - old_object_size as i64
307 } else {
308 self.status.incr_num_objects();
310 base_size as i64
311 };
312
313 self.status.update_base_used_size(base_size_delta);
314 self.broadcast(WorkerEvent::Set(
315 hashed_key,
316 base_size,
317 expiry,
318 old_object_info,
319 ))?;
320
321 Ok(())
322 }
323
324 pub fn del(&self, key: &K) -> Result<(), CacheError> {
344 let hashed_key = self.hash_key(key);
345
346 let (removed_hashed_key, object) = erase(
347 &self.objects,
348 &self.status,
349 &self.overhead_manager,
350 Some(EraseKey::Original(key, hashed_key)),
351 )?;
352
353 self.status.incr_dels();
354 self.broadcast(WorkerEvent::Del(removed_hashed_key, object.expiry()))?;
355
356 Ok(())
357 }
358
359 pub fn has(&self, key: &K) -> bool {
378 let hashed_key = self.hash_key(key);
379
380 self.objects
381 .get(&hashed_key)
382 .is_some_and(|object| object.key_matches(key) && !object.is_expired())
383 }
384
385 pub fn peek(&self, key: &K) -> Result<Arc<V>, CacheError> {
414 let hashed_key = self.hash_key(key);
415
416 match self.objects.get(&hashed_key) {
417 Some(object) if object.key_matches(key) && !object.is_expired() => Ok(object.data()),
418
419 _ => Err(CacheError::KeyNotFound),
420 }
421 }
422
423 pub fn ttl(&self, key: &K, ttl: Option<u32>) -> Result<(), CacheError> {
440 let hashed_key = self.hash_key(key);
441
442 let mut object = match self.objects.get_mut(&hashed_key) {
443 Some(object) if object.key_matches(key) && !object.is_expired() => object,
444 _ => return Err(CacheError::KeyNotFound),
445 };
446
447 let old_expiry = object.expiry();
448 let old_base_size = self.overhead_manager.base_size(&object);
449
450 object.expires(ttl);
451
452 let new_expiry = object.expiry();
453 let new_base_size = self.overhead_manager.base_size(&object);
454
455 self.status
456 .update_base_used_size(new_base_size as i64 - old_base_size as i64);
457 self.broadcast(WorkerEvent::Ttl(hashed_key, old_expiry, new_expiry))?;
458
459 Ok(())
460 }
461
462 pub fn size(&self, key: &K) -> Result<ObjectSize, CacheError> {
483 let hashed_key = self.hash_key(key);
484
485 match self.objects.get(&hashed_key) {
486 Some(object) if object.key_matches(key) && !object.is_expired() => {
487 Ok(self.overhead_manager.total_size(&object))
488 },
489
490 _ => Err(CacheError::KeyNotFound),
491 }
492 }
493
494 pub fn wipe(&self) -> Result<(), CacheError> {
510 info!("Wiping cache");
511
512 self.objects.clear();
513 self.status.clear();
514
515 self.broadcast(WorkerEvent::Wipe)?;
516
517 Ok(())
518 }
519
520 pub fn resize(&self, max_size: CacheSize) -> Result<(), CacheError> {
539 if max_size == 0 {
540 return Err(CacheError::ZeroCacheSize);
541 }
542
543 let current_max_size = self.status.max_size();
544
545 if max_size == current_max_size {
546 return Ok(());
547 }
548
549 info!(
550 "Resizing cache from {} to {}",
551 fmt::memory(current_max_size, Some(2)),
552 fmt::memory(max_size, Some(2)),
553 );
554
555 self.status.set_max_size(max_size);
556 self.broadcast(WorkerEvent::Resize(max_size))?;
557
558 Ok(())
559 }
560
561 pub fn policy(&self, policy: PaperPolicy) -> Result<(), CacheError> {
577 if !policy.is_auto() && !self.status.policies().contains(&policy) {
578 return Err(CacheError::UnconfiguredPolicy);
579 }
580
581 self.status.set_policy(policy)?;
582 self.broadcast(WorkerEvent::Policy(policy))?;
583
584 Ok(())
585 }
586
587 fn broadcast(&self, event: WorkerEvent) -> Result<(), CacheError> {
588 if let Err(err) = self.worker_manager.try_send(event) {
589 error!("Could not communicate with workers: {err:?}");
590 return Err(CacheError::Internal);
591 }
592
593 Ok(())
594 }
595
596 fn hash_key(&self, key: &K) -> HashedKey {
597 self.hasher.hash_one(key)
598 }
599}
600
601pub enum EraseKey<'a, K> {
602 Original(&'a K, HashedKey),
603 Hashed(HashedKey),
604}
605
606pub fn erase<K, V>(
607 objects: &ObjectMapRef<K, V>,
608 status: &StatusRef,
609 overhead_manager: &OverheadManagerRef,
610 maybe_key: Option<EraseKey<K>>,
611) -> Result<(HashedKey, Object<K, V>), CacheError>
612where
613 K: Eq + TypeSize,
614 V: TypeSize,
615{
616 let hashed_key = match maybe_key {
617 Some(EraseKey::Original(_, hashed_key)) => hashed_key,
618 Some(EraseKey::Hashed(hashed_key)) => hashed_key,
619
620 None => {
621 let Some(object) = objects.iter().next() else {
626 error!("Object store is empty with non-zero used size");
627 return Err(CacheError::Internal);
628 };
629
630 object.key().to_owned()
631 },
632 };
633
634 let Entry::Occupied(entry) = objects.entry(hashed_key) else {
638 return Err(CacheError::KeyNotFound);
639 };
640
641 if let Some(EraseKey::Original(key, _)) = maybe_key
642 && !entry.get().key_matches(key)
643 {
644 return Err(CacheError::KeyNotFound);
645 };
646
647 let object = entry.remove();
648 let base_size = overhead_manager.base_size(&object) as i64;
649
650 status.update_base_used_size(-base_size);
651 status.decr_num_objects();
652
653 match !object.is_expired() {
654 true => Ok((hashed_key, object)),
655 false => Err(CacheError::KeyNotFound),
656 }
657}
658
659unsafe impl<K, V, S> Send for PaperCache<K, V, S> {}
660
661#[cfg(test)]
662mod tests {
663 use crate::{CacheError, PaperCache, PaperPolicy};
664
665 const TEST_CACHE_MAX_SIZE: u64 = 1000;
666
667 #[test]
668 fn it_returns_correct_version() {
669 let cache = init_test_cache();
670 assert_eq!(cache.version(), env!("CARGO_PKG_VERSION"));
671 }
672
673 #[test]
674 fn it_returns_status() {
675 let cache = init_test_cache();
676 let status = cache.status().unwrap();
677
678 assert_eq!(status.max_size(), TEST_CACHE_MAX_SIZE);
679 }
680
681 #[test]
682 fn it_gets_an_existing_object() {
683 let cache = init_test_cache();
684
685 assert!(cache.set(0, 1, None).is_ok());
686 assert_eq!(cache.get(&0).as_deref(), Ok(&1));
687 }
688
689 #[test]
690 fn it_does_not_get_a_non_existing_object() {
691 let cache = init_test_cache();
692
693 assert!(cache.set(0, 1, None).is_ok());
694 assert_eq!(cache.get(&1), Err(CacheError::KeyNotFound));
695 }
696
697 #[test]
698 fn it_calculates_miss_ratio_correctly() {
699 let cache = init_test_cache();
700
701 assert!(cache.set(0, 1, None).is_ok());
702 assert!(cache.get(&0).is_ok());
703 assert!(cache.get(&0).is_ok());
704 assert!(cache.get(&0).is_ok());
705 assert!(cache.get(&1).is_err());
706
707 let status = cache.status().unwrap();
708 assert_eq!(status.miss_ratio(), 0.25);
709 }
710
711 #[test]
712 fn it_sets_with_no_ttl() {
713 let cache = init_test_cache();
714
715 assert!(cache.set(0, 1, None).is_ok());
716 assert!(cache.get(&0).is_ok());
717 }
718
719 #[test]
720 fn it_sets_with_ttl() {
721 use std::{thread, time::Duration};
722
723 let cache = init_test_cache();
724 assert!(cache.set(0, 1, Some(1)).is_ok());
725
726 assert!(cache.get(&0).is_ok());
727 thread::sleep(Duration::from_secs(2));
728 assert!(cache.get(&0).is_err());
729 }
730
731 #[test]
732 fn it_dels_an_existing_object() {
733 let cache = init_test_cache();
734 assert!(cache.set(0, 1, Some(1)).is_ok());
735
736 assert!(cache.get(&0).is_ok());
737 assert!(cache.del(&0).is_ok());
738 assert!(cache.get(&0).is_err());
739 }
740
741 #[test]
742 fn it_does_not_del_a_non_existing_object() {
743 let cache = init_test_cache();
744 assert_eq!(cache.del(&0), Err(CacheError::KeyNotFound));
745 }
746
747 #[test]
748 fn it_does_not_allow_empty_policies() {
749 let try_cache = PaperCache::<u32, u32>::new(TEST_CACHE_MAX_SIZE, &[], PaperPolicy::Lfu);
750
751 assert!(try_cache.is_err_and(|err| err == CacheError::EmptyPolicies));
752
753 let try_cache = PaperCache::<u32, u32>::new(TEST_CACHE_MAX_SIZE, &[], PaperPolicy::Auto);
754
755 assert!(try_cache.is_err_and(|err| err == CacheError::EmptyPolicies));
756 }
757
758 #[test]
759 fn it_does_not_allow_auto_policy_in_configured_policies() {
760 let try_cache = PaperCache::<u32, u32>::new(
761 TEST_CACHE_MAX_SIZE,
762 &[PaperPolicy::Auto],
763 PaperPolicy::Auto,
764 );
765
766 assert!(try_cache.is_err_and(|err| err == CacheError::ConfiguredAutoPolicy));
767
768 let try_cache = PaperCache::<u32, u32>::new(
769 TEST_CACHE_MAX_SIZE,
770 &[
771 PaperPolicy::Auto,
772 PaperPolicy::Lru,
773 ],
774 PaperPolicy::Auto,
775 );
776
777 assert!(try_cache.is_err_and(|err| err == CacheError::ConfiguredAutoPolicy));
778 }
779
780 #[test]
781 fn it_does_not_allow_duplicate_policies() {
782 let try_cache = PaperCache::<u32, u32>::new(
783 TEST_CACHE_MAX_SIZE,
784 &[
785 PaperPolicy::Lfu,
786 PaperPolicy::Lru,
787 PaperPolicy::Lfu,
788 ],
789 PaperPolicy::Lfu,
790 );
791
792 assert!(try_cache.is_err_and(|err| err == CacheError::DuplicatePolicies));
793
794 let try_cache = PaperCache::<u32, u32>::new(
795 TEST_CACHE_MAX_SIZE,
796 &[
797 PaperPolicy::Lfu,
798 PaperPolicy::Lru,
799 ],
800 PaperPolicy::Lfu,
801 );
802
803 assert!(try_cache.is_ok());
804 }
805
806 #[test]
807 fn it_has_an_existing_object() {
808 let cache = init_test_cache();
809
810 assert!(cache.set(0, 1, Some(1)).is_ok());
811 assert!(cache.has(&0));
812 }
813
814 #[test]
815 fn it_does_not_have_a_non_existing_object() {
816 let cache = init_test_cache();
817 assert!(!cache.has(&1));
818 }
819
820 #[test]
821 fn it_peeks_an_existing_object() {
822 let cache = init_test_cache();
823
824 assert!(cache.set(0, 1, None).is_ok());
825 assert_eq!(cache.peek(&0).as_deref(), Ok(&1));
826 }
827
828 #[test]
829 fn it_does_not_peek_a_non_existing_object() {
830 let cache = init_test_cache();
831
832 assert!(cache.set(0, 1, None).is_ok());
833 assert_eq!(cache.peek(&1), Err(CacheError::KeyNotFound));
834 }
835
836 #[test]
837 fn it_sets_an_existing_objects_ttl() {
838 use std::{thread, time::Duration};
839
840 let cache = init_test_cache();
841
842 assert!(cache.set(0, 1, None).is_ok());
843 assert!(cache.get(&0).is_ok());
844
845 assert!(cache.ttl(&0, Some(1)).is_ok());
846
847 thread::sleep(Duration::from_secs(2));
848 assert_eq!(cache.get(&0), Err(CacheError::KeyNotFound));
849 }
850
851 #[test]
852 fn it_does_not_set_a_non_existing_objects_ttl() {
853 let cache = init_test_cache();
854 assert_eq!(cache.ttl(&0, Some(1)), Err(CacheError::KeyNotFound));
855 }
856
857 #[test]
858 fn it_resets_an_objects_ttl() {
859 use std::{thread, time::Duration};
860
861 let cache = init_test_cache();
862
863 assert!(cache.set(0, 1, Some(1)).is_ok());
864 assert!(cache.get(&0).is_ok());
865
866 assert!(cache.ttl(&0, Some(5)).is_ok());
867
868 thread::sleep(Duration::from_secs(2));
869 assert!(cache.get(&0).is_ok());
870 }
871
872 #[test]
873 fn it_gets_an_objects_size() {
874 use std::mem;
875
876 use crate::object::{ExpireTime, overhead::get_policy_overhead};
877
878 let cache = init_test_cache();
879
880 let expected =
881 4 + 4 + mem::size_of::<ExpireTime>() as u32 + get_policy_overhead(&PaperPolicy::Lfu);
882
883 assert!(cache.set(0, 1, None).is_ok());
884 assert_eq!(cache.size(&0), Ok(expected));
885 }
886
887 #[test]
888 fn it_gets_an_expiring_objects_size() {
889 use std::mem;
890
891 use crate::object::{
892 ExpireTime,
893 overhead::{get_policy_overhead, get_ttl_overhead},
894 };
895
896 let cache = init_test_cache();
897
898 let expected = 4
899 + 4 + mem::size_of::<ExpireTime>() as u32
900 + get_policy_overhead(&PaperPolicy::Lfu)
901 + get_ttl_overhead();
902
903 assert!(cache.set(0, 1, Some(1)).is_ok());
904 assert_eq!(cache.size(&0), Ok(expected));
905 }
906
907 #[test]
908 fn it_gets_an_objects_size_after_policy_switch() {
909 use std::mem;
910
911 use crate::object::{ExpireTime, overhead::get_policy_overhead};
912
913 let cache = PaperCache::<u32, u32>::new(
914 TEST_CACHE_MAX_SIZE,
915 &[
916 PaperPolicy::Lru,
917 PaperPolicy::Lfu,
918 ],
919 PaperPolicy::Lfu,
920 )
921 .expect("Could not initialize test cache");
922
923 let base_expected = 4 + 4 + mem::size_of::<ExpireTime>() as u32;
924 let lfu_expected = base_expected + get_policy_overhead(&PaperPolicy::Lfu);
925 let lru_expected = base_expected + get_policy_overhead(&PaperPolicy::Lru);
926
927 assert!(cache.set(0, 1, None).is_ok());
928 assert_eq!(cache.size(&0), Ok(lfu_expected));
929
930 assert!(cache.policy(PaperPolicy::Lru).is_ok());
931 assert_eq!(cache.size(&0), Ok(lru_expected));
932 }
933
934 #[test]
935 fn it_gets_an_objects_size_after_set_ttl() {
936 use std::mem;
937
938 use crate::object::{
939 ExpireTime,
940 overhead::{get_policy_overhead, get_ttl_overhead},
941 };
942
943 let cache = init_test_cache();
944
945 let pre_expected =
946 4 + 4 + mem::size_of::<ExpireTime>() as u32 + get_policy_overhead(&PaperPolicy::Lfu);
947
948 let post_expected = pre_expected + get_ttl_overhead();
949
950 assert!(cache.set(0, 1, None).is_ok());
951 assert_eq!(cache.size(&0), Ok(pre_expected));
952
953 assert!(cache.ttl(&0, Some(1)).is_ok());
954 assert_eq!(cache.size(&0), Ok(post_expected));
955 }
956
957 #[test]
958 fn it_gets_an_objects_size_after_unset_ttl() {
959 use std::mem;
960
961 use crate::object::{
962 ExpireTime,
963 overhead::{get_policy_overhead, get_ttl_overhead},
964 };
965
966 let cache = init_test_cache();
967
968 let pre_expected = 4
969 + 4 + mem::size_of::<ExpireTime>() as u32
970 + get_policy_overhead(&PaperPolicy::Lfu)
971 + get_ttl_overhead();
972
973 let post_expected = pre_expected - get_ttl_overhead();
974
975 assert!(cache.set(0, 1, Some(1)).is_ok());
976 assert_eq!(cache.size(&0), Ok(pre_expected));
977
978 assert!(cache.ttl(&0, None).is_ok());
979 assert_eq!(cache.size(&0), Ok(post_expected));
980 }
981
982 #[test]
983 fn status_shows_correct_used_size() {
984 use std::mem;
985
986 use crate::object::{
987 ExpireTime,
988 overhead::{get_policy_overhead, get_ttl_overhead},
989 };
990
991 let cache = init_test_cache();
992
993 let expected = (4 + 4) * 2
994 + mem::size_of::<ExpireTime>() as u32 * 2
995 + get_policy_overhead(&PaperPolicy::Lfu) * 2
996 + get_ttl_overhead();
997
998 assert!(cache.set(0, 1, None).is_ok());
999 assert!(cache.set(1, 1, Some(1)).is_ok());
1000
1001 let status = cache.status().unwrap();
1002 assert_eq!(status.used_size(), expected as u64);
1003 }
1004
1005 #[test]
1006 fn status_shows_correct_used_size_after_policy_switch() {
1007 use std::mem;
1008
1009 use crate::object::{ExpireTime, overhead::get_policy_overhead};
1010
1011 let cache = PaperCache::<u32, u32>::new(
1012 TEST_CACHE_MAX_SIZE,
1013 &[
1014 PaperPolicy::Lru,
1015 PaperPolicy::Lfu,
1016 ],
1017 PaperPolicy::Lfu,
1018 )
1019 .expect("Could not initialize test cache");
1020
1021 let base_expected = 4 + 4 + mem::size_of::<ExpireTime>() as u32;
1022 let lfu_expected = base_expected + get_policy_overhead(&PaperPolicy::Lfu);
1023 let lru_expected = base_expected + get_policy_overhead(&PaperPolicy::Lru);
1024
1025 assert!(cache.set(0, 1, None).is_ok());
1026 let status = cache.status().unwrap();
1027 assert_eq!(status.used_size(), lfu_expected as u64);
1028
1029 assert!(cache.policy(PaperPolicy::Lru).is_ok());
1030 let status = cache.status().unwrap();
1031 assert_eq!(status.used_size(), lru_expected as u64);
1032 }
1033
1034 #[test]
1035 fn status_shows_correct_used_size_after_set_ttl() {
1036 use std::mem;
1037
1038 use crate::object::{
1039 ExpireTime,
1040 overhead::{get_policy_overhead, get_ttl_overhead},
1041 };
1042
1043 let cache = init_test_cache();
1044
1045 let pre_expected =
1046 4 + 4 + mem::size_of::<ExpireTime>() as u32 + get_policy_overhead(&PaperPolicy::Lfu);
1047
1048 let post_expected = pre_expected + get_ttl_overhead();
1049
1050 assert!(cache.set(0, 1, None).is_ok());
1051 let status = cache.status().unwrap();
1052 assert_eq!(status.used_size(), pre_expected as u64);
1053
1054 assert!(cache.ttl(&0, Some(1)).is_ok());
1055 let status = cache.status().unwrap();
1056 assert_eq!(status.used_size(), post_expected as u64);
1057 }
1058
1059 #[test]
1060 fn status_shows_correct_used_size_after_unset_ttl() {
1061 use std::mem;
1062
1063 use crate::object::{
1064 ExpireTime,
1065 overhead::{get_policy_overhead, get_ttl_overhead},
1066 };
1067
1068 let cache = init_test_cache();
1069
1070 let pre_expected = 4
1071 + 4 + mem::size_of::<ExpireTime>() as u32
1072 + get_policy_overhead(&PaperPolicy::Lfu)
1073 + get_ttl_overhead();
1074
1075 let post_expected = pre_expected - get_ttl_overhead();
1076
1077 assert!(cache.set(0, 1, Some(1)).is_ok());
1078 let status = cache.status().unwrap();
1079 assert_eq!(status.used_size(), pre_expected as u64);
1080
1081 assert!(cache.ttl(&0, None).is_ok());
1082 let status = cache.status().unwrap();
1083 assert_eq!(status.used_size(), post_expected as u64);
1084 }
1085
1086 fn init_test_cache() -> PaperCache<u32, u32> {
1087 PaperCache::<u32, u32>::new(TEST_CACHE_MAX_SIZE, &[PaperPolicy::Lfu], PaperPolicy::Lfu)
1088 .expect("Could not initialize test cache")
1089 }
1090}