1use super::*;
2
3use crate::iter::{GenericMapIntoIter, GenericMapIter, GenericMapIterMut, IntoIter, Iter, IterMut};
4
5macro_rules! cfg_std_feature {
6 ($($item:item)*) => {
7 $(
8 #[cfg(feature = "std")]
9 $item
10 )*
11 };
12}
13
14macro_rules! cfg_not_std_feature {
15 ($($item:item)*) => {
16 $(
17 #[cfg(not(feature = "std"))]
18 $item
19 )*
20 };
21}
22
23cfg_not_std_feature! {
24 pub trait GenericKey: Clone + Eq + Ord {}
27 impl<T: Clone + Eq + Ord> GenericKey for T {}
28}
29
30cfg_std_feature! {
31 pub trait GenericKey: Clone + Eq + Ord + Hash {}
34 impl<T: Clone + Eq + Ord + Hash> GenericKey for T {}
35}
36
37#[allow(clippy::enum_variant_names)]
45#[derive(Clone, Debug)]
46enum GenericMap<K, V> {
47 BTreeMap(BTreeMap<K, V>),
48 #[cfg(feature = "std")]
49 HashMap(HashMap<K, V>),
50 #[cfg(all(feature = "std", feature = "rustc-hash"))]
51 FxHashMap(FxHashMap<K, V>),
52}
53
54impl<K, V> Default for GenericMap<K, V> {
55 fn default() -> Self {
56 Self::BTreeMap(BTreeMap::default())
57 }
58}
59
60impl<K, V> GenericMap<K, V>
61where
62 K: GenericKey,
63{
64 #[inline(always)]
65 fn get(&self, k: &K) -> Option<&V> {
66 match self {
67 Self::BTreeMap(inner) => inner.get(k),
68 #[cfg(feature = "std")]
69 Self::HashMap(inner) => inner.get(k),
70 #[cfg(all(feature = "std", feature = "rustc-hash"))]
71 Self::FxHashMap(inner) => inner.get(k),
72 }
73 }
74
75 #[inline(always)]
76 fn get_mut(&mut self, k: &K) -> Option<&mut V> {
77 match self {
78 Self::BTreeMap(inner) => inner.get_mut(k),
79 #[cfg(feature = "std")]
80 Self::HashMap(inner) => inner.get_mut(k),
81 #[cfg(all(feature = "std", feature = "rustc-hash"))]
82 Self::FxHashMap(inner) => inner.get_mut(k),
83 }
84 }
85
86 #[inline(always)]
87 fn len(&self) -> usize {
88 match self {
89 Self::BTreeMap(inner) => inner.len(),
90 #[cfg(feature = "std")]
91 Self::HashMap(inner) => inner.len(),
92 #[cfg(all(feature = "std", feature = "rustc-hash"))]
93 Self::FxHashMap(inner) => inner.len(),
94 }
95 }
96
97 #[inline(always)]
98 fn keys(&self) -> Vec<K> {
99 match self {
100 Self::BTreeMap(inner) => inner.keys().cloned().collect(),
101 #[cfg(feature = "std")]
102 Self::HashMap(inner) => inner.keys().cloned().collect(),
103 #[cfg(all(feature = "std", feature = "rustc-hash"))]
104 Self::FxHashMap(inner) => inner.keys().cloned().collect(),
105 }
106 }
107
108 #[inline(always)]
109 fn is_empty(&self) -> bool {
110 match self {
111 Self::BTreeMap(inner) => inner.is_empty(),
112 #[cfg(feature = "std")]
113 Self::HashMap(inner) => inner.is_empty(),
114 #[cfg(all(feature = "std", feature = "rustc-hash"))]
115 Self::FxHashMap(inner) => inner.is_empty(),
116 }
117 }
118
119 #[inline(always)]
120 fn insert(&mut self, k: K, v: V) -> Option<V> {
121 match self {
122 Self::BTreeMap(inner) => inner.insert(k, v),
123 #[cfg(feature = "std")]
124 Self::HashMap(inner) => inner.insert(k, v),
125 #[cfg(all(feature = "std", feature = "rustc-hash"))]
126 Self::FxHashMap(inner) => inner.insert(k, v),
127 }
128 }
129
130 #[inline(always)]
131 fn clear(&mut self) {
132 match self {
133 Self::BTreeMap(inner) => inner.clear(),
134 #[cfg(feature = "std")]
135 Self::HashMap(inner) => inner.clear(),
136 #[cfg(all(feature = "std", feature = "rustc-hash"))]
137 Self::FxHashMap(inner) => inner.clear(),
138 }
139 }
140
141 #[inline(always)]
142 fn remove(&mut self, k: &K) -> Option<V> {
143 match self {
144 Self::BTreeMap(inner) => inner.remove(k),
145 #[cfg(feature = "std")]
146 Self::HashMap(inner) => inner.remove(k),
147 #[cfg(all(feature = "std", feature = "rustc-hash"))]
148 Self::FxHashMap(inner) => inner.remove(k),
149 }
150 }
151
152 fn iter(&self) -> GenericMapIter<'_, K, V> {
153 match self {
154 Self::BTreeMap(inner) => GenericMapIter::BTreeMap(inner.iter()),
155 #[cfg(feature = "std")]
156 Self::HashMap(inner) => GenericMapIter::HashMap(inner.iter()),
157 #[cfg(all(feature = "std", feature = "rustc-hash"))]
158 Self::FxHashMap(inner) => GenericMapIter::FxHashMap(inner.iter()),
159 }
160 }
161
162 fn into_iter(self) -> GenericMapIntoIter<K, V> {
163 match self {
164 Self::BTreeMap(inner) => GenericMapIntoIter::BTreeMap(inner.into_iter()),
165 #[cfg(feature = "std")]
166 Self::HashMap(inner) => GenericMapIntoIter::HashMap(inner.into_iter()),
167 #[cfg(all(feature = "std", feature = "rustc-hash"))]
168 Self::FxHashMap(inner) => GenericMapIntoIter::FxHashMap(inner.into_iter()),
169 }
170 }
171
172 fn iter_mut(&mut self) -> GenericMapIterMut<'_, K, V> {
173 match self {
174 Self::BTreeMap(inner) => GenericMapIterMut::BTreeMap(inner.iter_mut()),
175 #[cfg(feature = "std")]
176 Self::HashMap(inner) => GenericMapIterMut::HashMap(inner.iter_mut()),
177 #[cfg(all(feature = "std", feature = "rustc-hash"))]
178 Self::FxHashMap(inner) => GenericMapIterMut::FxHashMap(inner.iter_mut()),
179 }
180 }
181}
182
183#[cfg(feature = "std")]
185#[allow(clippy::enum_variant_names)]
186pub enum MapKind {
187 BTreeMap,
188 HashMap,
189 #[cfg(feature = "rustc-hash")]
190 FxHashMap,
191}
192
193#[derive(Clone, Debug)]
200pub struct TimedMap<K, V, #[cfg(feature = "std")] C = StdClock, #[cfg(not(feature = "std"))] C> {
201 clock: C,
202
203 map: GenericMap<K, ExpirableEntry<V>>,
204 expiries: BTreeMap<u64, BTreeSet<K>>,
205
206 expiration_tick: u16,
207 expiration_tick_cap: u16,
208}
209
210#[cfg(feature = "serde")]
211impl<K: serde::Serialize + Ord, V: serde::Serialize, C: Clock> serde::Serialize
212 for TimedMap<K, V, C>
213{
214 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
215 where
216 S: serde::Serializer,
217 {
218 let now = self.clock.elapsed_seconds_since_creation();
219 match &self.map {
220 GenericMap::BTreeMap(inner) => {
221 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
222 serializer.collect_map(map)
223 }
224 #[cfg(feature = "std")]
225 GenericMap::HashMap(inner) => {
226 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
227 serializer.collect_map(map)
228 }
229 #[cfg(all(feature = "std", feature = "rustc-hash"))]
230 GenericMap::FxHashMap(inner) => {
231 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
232 serializer.collect_map(map)
233 }
234 }
235 }
236}
237
238impl<'a, K, V, C> IntoIterator for &'a TimedMap<K, V, C>
239where
240 K: GenericKey,
241 C: Clock,
242{
243 type Item = (&'a K, &'a V);
244 type IntoIter = Iter<'a, K, V>;
245
246 fn into_iter(self) -> Self::IntoIter {
247 let now = self.clock.elapsed_seconds_since_creation();
248 Iter {
249 inner: self.map.iter(),
250 now,
251 }
252 }
253}
254
255impl<'a, K, V, C> IntoIterator for &'a mut TimedMap<K, V, C>
256where
257 K: GenericKey,
258 C: Clock,
259{
260 type Item = (&'a K, &'a mut V);
261 type IntoIter = IterMut<'a, K, V>;
262
263 fn into_iter(self) -> Self::IntoIter {
264 let now = self.clock.elapsed_seconds_since_creation();
265 IterMut {
266 inner: self.map.iter_mut(),
267 now,
268 }
269 }
270}
271
272impl<K, V, C> IntoIterator for TimedMap<K, V, C>
273where
274 K: GenericKey,
275 C: Clock,
276{
277 type Item = (K, V);
278 type IntoIter = IntoIter<K, V>;
279
280 fn into_iter(self) -> Self::IntoIter {
281 let now = self.clock.elapsed_seconds_since_creation();
282 IntoIter {
283 inner: self.map.into_iter(),
284 now,
285 }
286 }
287}
288
289impl<K, V, C> Default for TimedMap<K, V, C>
290where
291 C: Default,
292{
293 fn default() -> Self {
294 Self {
295 clock: Default::default(),
296
297 map: GenericMap::default(),
298 expiries: BTreeMap::default(),
299
300 expiration_tick: 0,
301 expiration_tick_cap: 1,
302 }
303 }
304}
305
306#[cfg(feature = "std")]
307impl<K, V> TimedMap<K, V, StdClock>
308where
309 K: GenericKey,
310{
311 pub fn new() -> Self {
313 Self::default()
314 }
315
316 pub fn new_with_map_kind(map_kind: MapKind) -> Self {
318 let map = match map_kind {
319 MapKind::BTreeMap => GenericMap::<K, ExpirableEntry<V>>::BTreeMap(BTreeMap::default()),
320 MapKind::HashMap => GenericMap::HashMap(HashMap::default()),
321 #[cfg(feature = "rustc-hash")]
322 MapKind::FxHashMap => GenericMap::FxHashMap(FxHashMap::default()),
323 };
324
325 Self {
326 map,
327
328 clock: StdClock::default(),
329 expiries: BTreeMap::default(),
330
331 expiration_tick: 0,
332 expiration_tick_cap: 1,
333 }
334 }
335}
336
337impl<K, V, C> TimedMap<K, V, C>
338where
339 C: Clock,
340 K: GenericKey,
341{
342 #[cfg(not(feature = "std"))]
346 pub fn new(clock: C) -> Self {
347 Self {
348 clock,
349 map: GenericMap::default(),
350 expiries: BTreeMap::default(),
351 expiration_tick: 0,
352 expiration_tick_cap: 1,
353 }
354 }
355
356 #[inline(always)]
367 pub fn expiration_tick_cap(mut self, expiration_tick_cap: u16) -> Self {
368 self.expiration_tick_cap = expiration_tick_cap;
369 self
370 }
371
372 pub fn get(&self, k: &K) -> Option<&V> {
376 self.map
377 .get(k)
378 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
379 .map(|v| v.value())
380 }
381
382 pub fn get_mut(&mut self, k: &K) -> Option<&mut V> {
386 self.map
387 .get_mut(k)
388 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
389 .map(|v| v.value_mut())
390 }
391
392 #[inline(always)]
396 pub fn get_unchecked(&self, k: &K) -> Option<&V> {
397 self.map.get(k).map(|v| v.value())
398 }
399
400 #[inline(always)]
405 pub fn get_mut_unchecked(&mut self, k: &K) -> Option<&mut V> {
406 self.map.get_mut(k).map(|v| v.value_mut())
407 }
408
409 pub fn get_remaining_duration(&self, k: &K) -> Option<Duration> {
413 match self.map.get(k) {
414 Some(v) => {
415 let now = self.clock.elapsed_seconds_since_creation();
416 if v.is_expired(now) {
417 return None;
418 }
419
420 v.remaining_duration(now)
421 }
422 None => None,
423 }
424 }
425
426 #[inline(always)]
430 pub fn len(&self) -> usize {
431 self.map.len() - self.len_expired()
432 }
433
434 #[inline(always)]
438 pub fn len_expired(&self) -> usize {
439 let now = self.clock.elapsed_seconds_since_creation();
440 self.expiries
441 .range(..=now)
442 .map(|(_exp, keys)| keys.len())
443 .sum()
444 }
445
446 #[inline(always)]
450 pub fn len_unchecked(&self) -> usize {
451 self.map.len()
452 }
453
454 #[inline(always)]
458 pub fn keys(&self) -> Vec<K> {
459 let now = self.clock.elapsed_seconds_since_creation();
460 self.map
461 .iter()
462 .filter(|(_k, v)| !v.is_expired(now))
463 .map(|(k, _v)| k.clone())
464 .collect()
465 }
466
467 #[inline(always)]
469 pub fn keys_expired(&self) -> Vec<K> {
470 let now = self.clock.elapsed_seconds_since_creation();
471 self.map
472 .iter()
473 .filter(|(_k, v)| v.is_expired(now))
474 .map(|(k, _v)| k.clone())
475 .collect()
476 }
477
478 #[inline(always)]
482 pub fn keys_unchecked(&self) -> Vec<K> {
483 self.map.keys()
484 }
485
486 #[inline(always)]
490 pub fn is_empty(&self) -> bool {
491 self.len() == 0
492 }
493
494 #[inline(always)]
498 pub fn is_empty_unchecked(&self) -> bool {
499 self.map.is_empty()
500 }
501
502 #[inline(always)]
508 fn insert(&mut self, k: K, v: V, expires_at: Option<u64>) -> Option<V> {
509 let entry = ExpirableEntry::new(v, expires_at);
510 match self.map.insert(k.clone(), entry) {
511 Some(old) => {
512 if let EntryStatus::ExpiresAtSeconds(e) = old.status() {
514 self.drop_key_from_expiry(e, &k)
515 }
516
517 Some(old.owned_value())
518 }
519 None => None,
520 }
521 }
522
523 pub fn insert_expirable(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
532 self.expiration_tick += 1;
533
534 let now = self.clock.elapsed_seconds_since_creation();
535 if self.expiration_tick >= self.expiration_tick_cap {
536 self.drop_expired_entries_inner(now);
537 self.expiration_tick = 0;
538 }
539
540 let expires_at = now + duration.as_secs();
541
542 let res = self.insert(k.clone(), v, Some(expires_at));
543
544 self.expiries.entry(expires_at).or_default().insert(k);
545
546 res
547 }
548
549 pub fn insert_expirable_unchecked(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
558 let now = self.clock.elapsed_seconds_since_creation();
559 let expires_at = now + duration.as_secs();
560
561 let res = self.insert(k.clone(), v, Some(expires_at));
562
563 self.expiries.entry(expires_at).or_default().insert(k);
564
565 res
566 }
567
568 pub fn insert_constant(&mut self, k: K, v: V) -> Option<V> {
577 self.expiration_tick += 1;
578
579 let now = self.clock.elapsed_seconds_since_creation();
580 if self.expiration_tick >= self.expiration_tick_cap {
581 self.drop_expired_entries_inner(now);
582 self.expiration_tick = 0;
583 }
584
585 self.insert(k, v, None)
586 }
587
588 pub fn insert_constant_unchecked(&mut self, k: K, v: V) -> Option<V> {
597 self.expiration_tick += 1;
598 self.insert(k, v, None)
599 }
600
601 #[inline(always)]
607 pub fn remove(&mut self, k: &K) -> Option<V> {
608 self.map
609 .remove(k)
610 .filter(|v| {
611 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
612 self.drop_key_from_expiry(expires_at_seconds, k);
613 }
614
615 !v.is_expired(self.clock.elapsed_seconds_since_creation())
616 })
617 .map(|v| v.owned_value())
618 }
619
620 #[inline(always)]
625 pub fn remove_unchecked(&mut self, k: &K) -> Option<V> {
626 self.map
627 .remove(k)
628 .filter(|v| {
629 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
630 self.drop_key_from_expiry(expires_at_seconds, k);
631 }
632
633 true
634 })
635 .map(|v| v.owned_value())
636 }
637
638 #[inline(always)]
640 pub fn clear(&mut self) {
641 self.map.clear();
642 self.expiries.clear();
643 }
644
645 pub fn iter(&self) -> Iter<'_, K, V> {
647 self.into_iter()
648 }
649
650 pub fn iter_unchecked(&self) -> impl Iterator<Item = (&K, &V)> {
652 self.map.iter().map(|(k, v)| (k, v.value()))
653 }
654
655 pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
657 self.into_iter()
658 }
659
660 pub fn iter_mut_unchecked(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
662 self.map.iter_mut().map(|(k, v)| (k, v.value_mut()))
663 }
664
665 pub fn update_expiration_status(
670 &mut self,
671 key: K,
672 duration: Duration,
673 ) -> Result<Option<EntryStatus>, &'static str> {
674 match self.map.get_mut(&key) {
675 Some(entry) => {
676 let old_status = *entry.status();
677 let now = self.clock.elapsed_seconds_since_creation();
678 let expires_at = now + duration.as_secs();
679
680 entry.update_status(EntryStatus::ExpiresAtSeconds(expires_at));
681
682 if let EntryStatus::ExpiresAtSeconds(t) = &old_status {
683 self.drop_key_from_expiry(t, &key);
684 }
685 self.expiries
686 .entry(expires_at)
687 .or_default()
688 .insert(key.clone());
689
690 match old_status {
691 EntryStatus::Constant => Ok(None),
692 EntryStatus::ExpiresAtSeconds(_) => Ok(Some(old_status)),
693 }
694 }
695 None => Err("entry not found"),
696 }
697 }
698
699 #[inline(always)]
704 pub fn drop_expired_entries(&mut self) -> Vec<(K, V)> {
705 let now = self.clock.elapsed_seconds_since_creation();
706 self.drop_expired_entries_inner(now)
707 }
708
709 fn drop_expired_entries_inner(&mut self, now: u64) -> Vec<(K, V)> {
710 let mut expired_entries = Vec::new();
711 while let Some((exp, keys)) = self.expiries.pop_first() {
713 if exp > now {
715 self.expiries.insert(exp, keys);
716 break;
717 }
718
719 for key in keys {
720 if let Some(value) = self.map.remove(&key) {
721 expired_entries.push((key, value.owned_value()));
722 }
723 }
724 }
725
726 expired_entries
727 }
728
729 fn drop_key_from_expiry(&mut self, expiry_key: &u64, map_key: &K) {
730 if let Some(list) = self.expiries.get_mut(expiry_key) {
731 list.remove(map_key);
732
733 if list.is_empty() {
734 self.expiries.remove(expiry_key);
735 }
736 }
737 }
738
739 #[inline(always)]
743 pub fn contains_key(&self, k: &K) -> bool {
744 self.get(k).is_some()
745 }
746
747 #[inline(always)]
751 pub fn contains_key_unchecked(&self, k: &K) -> bool {
752 self.get_unchecked(k).is_some()
753 }
754}
755
756#[cfg(test)]
757#[cfg(not(feature = "std"))]
758mod tests {
759 use super::*;
760
761 #[derive(Clone, Copy)]
762 struct MockClock {
763 current_time: u64,
764 }
765
766 impl Clock for MockClock {
767 fn elapsed_seconds_since_creation(&self) -> u64 {
768 self.current_time
769 }
770 }
771
772 #[test]
773 fn nostd_insert_and_get_constant_entry() {
774 let clock = MockClock { current_time: 1000 };
775 let mut map = TimedMap::new(clock);
776
777 map.insert_constant(1, "constant value");
778
779 assert_eq!(map.get(&1), Some(&"constant value"));
780 assert_eq!(map.get_remaining_duration(&1), None);
781 }
782
783 #[test]
784 fn nostd_insert_and_get_expirable_entry() {
785 let clock = MockClock { current_time: 1000 };
786 let mut map = TimedMap::new(clock);
787 let duration = Duration::from_secs(60);
788
789 map.insert_expirable(1, "expirable value", duration);
790
791 assert_eq!(map.get(&1), Some(&"expirable value"));
792 assert_eq!(map.get_remaining_duration(&1), Some(duration));
793 }
794
795 #[test]
796 fn nostd_expired_entry() {
797 let clock = MockClock { current_time: 1000 };
798 let mut map = TimedMap::new(clock);
799 let duration = Duration::from_secs(60);
800
801 map.insert_expirable(1, "expirable value", duration);
803
804 let clock = MockClock { current_time: 1070 };
806 map.clock = clock;
807
808 assert_eq!(map.get(&1), None);
810 assert_eq!(map.get_remaining_duration(&1), None);
811 }
812
813 #[test]
814 fn nostd_remove_entry() {
815 let clock = MockClock { current_time: 1000 };
816 let mut map = TimedMap::new(clock);
817
818 map.insert_constant(1, "constant value");
819
820 assert_eq!(map.remove(&1), Some("constant value"));
821 assert_eq!(map.get(&1), None);
822 }
823
824 #[test]
825 fn nostd_clear_removes_expiry_bookkeeping() {
826 let clock = MockClock { current_time: 1000 };
827 let mut map = TimedMap::new(clock);
828
829 map.insert_expirable(1, "expirable value", Duration::from_secs(60));
830
831 assert!(!map.expiries.is_empty());
832
833 map.clear();
834
835 assert!(map.expiries.is_empty());
836 assert_eq!(map.len(), 0);
837 assert_eq!(map.len_expired(), 0);
838 assert_eq!(map.len_unchecked(), 0);
839 }
840
841 #[test]
842 fn nostd_drop_expired_entries() {
843 let clock = MockClock { current_time: 1000 };
844 let mut map = TimedMap::new(clock);
845
846 map.insert_expirable(1, "expirable value1", Duration::from_secs(50));
848 map.insert_expirable(2, "expirable value2", Duration::from_secs(70));
849 map.insert_constant(3, "constant value");
850
851 let clock = MockClock { current_time: 1055 };
853 map.clock = clock;
854
855 assert_eq!(map.get(&1), None);
857 assert_eq!(map.get(&2), Some(&"expirable value2"));
858 assert_eq!(map.get(&3), Some(&"constant value"));
859
860 let clock = MockClock { current_time: 1071 };
862 map.clock = clock;
863
864 assert_eq!(map.get(&1), None);
865 assert_eq!(map.get(&2), None);
866 assert_eq!(map.get(&3), Some(&"constant value"));
867 }
868
869 #[test]
870 fn nostd_update_existing_entry() {
871 let clock = MockClock { current_time: 1000 };
872 let mut map = TimedMap::new(clock);
873
874 map.insert_constant(1, "initial value");
875 assert_eq!(map.get(&1), Some(&"initial value"));
876
877 map.insert_expirable(1, "updated value", Duration::from_secs(15));
879 assert_eq!(map.get(&1), Some(&"updated value"));
880
881 let clock = MockClock { current_time: 1016 };
883 map.clock = clock;
884
885 assert_eq!(map.get(&1), None);
886 }
887
888 #[test]
889 fn nostd_update_expirable_entry_status() {
890 let clock = MockClock { current_time: 1000 };
891 let mut map = TimedMap::new(clock);
892
893 map.insert_constant(1, "initial value");
894 assert_eq!(map.get(&1), Some(&"initial value"));
895
896 let old_status = map
898 .update_expiration_status(1, Duration::from_secs(16))
899 .expect("entry update shouldn't fail");
900 assert!(old_status.is_none());
901 assert_eq!(map.get(&1), Some(&"initial value"));
902
903 let clock = MockClock { current_time: 1017 };
905 map.clock = clock;
906 assert_eq!(map.get(&1), None);
907 }
908
909 #[test]
910 fn nostd_update_expirable_entry_status_with_previou_time() {
911 let clock = MockClock { current_time: 1000 };
912 let mut map = TimedMap::new(clock);
913
914 map.insert_expirable(1, "expirable value", Duration::from_secs(15));
916 let old_status = map
917 .update_expiration_status(1, Duration::from_secs(15))
918 .expect("entry update shouldn't fail");
919 assert!(matches!(
920 old_status,
921 Some(EntryStatus::ExpiresAtSeconds(1015))
922 ));
923
924 assert_eq!(map.get(&1), Some(&"expirable value"));
926 assert!(map.expiries.contains_key(&1015));
927 }
928
929 #[test]
930 fn nostd_contains_key() {
931 let clock = MockClock { current_time: 1000 };
932 let mut map = TimedMap::new(clock);
933
934 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
936 assert!(map.contains_key(&1));
937 }
938
939 #[test]
940 fn nostd_keys_and_is_empty_ignore_expired_entries() {
941 let mut clock = MockClock { current_time: 1000 };
942 let mut map = TimedMap::new(clock);
943
944 map.insert_expirable(1, "expired value", Duration::from_secs(1));
945 map.insert_constant(2, "constant value");
946
947 clock.current_time = 1002;
948 map.clock = clock;
949
950 assert_eq!(map.keys().as_slice(), &[2]);
951 assert_eq!(map.keys_expired().as_slice(), &[1]);
952 assert_eq!(map.keys_unchecked().as_slice(), &[1, 2]);
953 assert!(!map.is_empty());
954 assert!(!map.is_empty_unchecked());
955
956 map.remove(&2);
957
958 assert!(map.is_empty());
959 assert_eq!(map.keys_expired().as_slice(), &[1]);
960 assert!(!map.is_empty_unchecked());
961 }
962
963 #[test]
964 fn nostd_iter_empty() {
965 let clock = MockClock { current_time: 1000 };
966 let map: TimedMap<u64, &str, MockClock> = TimedMap::new(clock);
967 assert_eq!(map.iter().count(), 0);
968 assert_eq!(map.into_iter().count(), 0);
969 }
970
971 #[test]
972 fn nostd_iter_all_expired() {
973 let mut clock = MockClock { current_time: 1000 };
974 let mut map = TimedMap::new(clock);
975
976 map.insert_expirable(1, "val1", Duration::from_secs(1));
978 map.insert_expirable(2, "val2", Duration::from_secs(2));
980
981 clock.current_time = 1003;
982 map.clock = clock;
983
984 assert_eq!(map.iter().count(), 0);
985 assert_eq!(map.into_iter().count(), 0);
986 }
987
988 #[test]
989 fn nostd_iter_mixed() {
990 let mut clock = MockClock { current_time: 1000 };
991 let mut map = TimedMap::new(clock);
992
993 map.insert_expirable(1, "val1", Duration::from_secs(1));
995 map.insert_constant(2, "val2");
997 map.insert_expirable(3, "val3", Duration::from_secs(5));
999
1000 clock.current_time = 1002;
1001 map.clock = clock;
1002
1003 let mut collected: Vec<(&u64, &&str)> = map.iter().collect();
1004 collected.sort_by_key(|(k, _)| *k);
1005
1006 assert_eq!(collected.len(), 2);
1007 assert_eq!(collected[0], (&2, &"val2"));
1008 assert_eq!(collected[1], (&3, &"val3"));
1009 }
1010
1011 #[test]
1012 fn nostd_iter_mut_mixed() {
1013 let mut clock = MockClock { current_time: 1000 };
1014 let mut map = TimedMap::new(clock);
1015
1016 map.insert_expirable(1, "val1", Duration::from_secs(1));
1018 map.insert_constant(2, "val2");
1020 map.insert_expirable(3, "val3", Duration::from_secs(5));
1022
1023 clock.current_time = 1002;
1024 map.clock = clock;
1025
1026 for (k, v) in map.iter_mut() {
1027 if *k == 2 {
1028 *v = "modified_val2";
1029 }
1030 }
1031
1032 assert_eq!(map.get(&1), None);
1033 assert_eq!(map.get(&2), Some(&"modified_val2"));
1034 assert_eq!(map.get(&3), Some(&"val3"));
1035 }
1036}
1037
1038#[cfg(feature = "std")]
1039#[cfg(test)]
1040mod std_tests {
1041 use core::ops::Add;
1042
1043 use super::*;
1044
1045 #[test]
1046 fn std_expirable_and_constant_entries() {
1047 let mut map = TimedMap::new();
1048
1049 map.insert_constant(1, "constant value");
1050 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
1051
1052 assert_eq!(map.get(&1), Some(&"constant value"));
1053 assert_eq!(map.get(&2), Some(&"expirable value"));
1054
1055 assert_eq!(map.get_remaining_duration(&1), None);
1056 assert!(map.get_remaining_duration(&2).is_some());
1057 }
1058
1059 #[test]
1060 fn std_expired_entry_removal() {
1061 let mut map = TimedMap::new();
1062 let duration = Duration::from_secs(2);
1063
1064 map.insert_expirable(1, "expirable value", duration);
1065
1066 std::thread::sleep(Duration::from_secs(3));
1068
1069 assert_eq!(map.get(&1), None);
1071 assert_eq!(map.get_remaining_duration(&1), None);
1072 }
1073
1074 #[test]
1075 fn std_remove_entry() {
1076 let mut map = TimedMap::new();
1077
1078 map.insert_constant(1, "constant value");
1079 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
1080
1081 assert_eq!(map.remove(&1), Some("constant value"));
1082 assert_eq!(map.remove(&2), Some("expirable value"));
1083
1084 assert_eq!(map.get(&1), None);
1085 assert_eq!(map.get(&2), None);
1086 }
1087
1088 #[test]
1089 fn std_clear_removes_expiry_bookkeeping() {
1090 let mut map = TimedMap::new();
1091
1092 map.insert_expirable(1, "expirable value", Duration::from_secs(60));
1093
1094 assert!(!map.expiries.is_empty());
1095
1096 map.clear();
1097
1098 assert!(map.expiries.is_empty());
1099 assert_eq!(map.len(), 0);
1100 assert_eq!(map.len_expired(), 0);
1101 assert_eq!(map.len_unchecked(), 0);
1102 }
1103
1104 #[test]
1105 fn std_drop_expired_entries() {
1106 let mut map = TimedMap::new();
1107
1108 map.insert_expirable(1, "expirable value1", Duration::from_secs(2));
1109 map.insert_expirable(2, "expirable value2", Duration::from_secs(4));
1110
1111 std::thread::sleep(Duration::from_secs(3));
1113
1114 assert_eq!(map.get(&1), None);
1116 assert_eq!(map.get(&2), Some(&"expirable value2"));
1117 }
1118
1119 #[test]
1120 fn std_update_existing_entry() {
1121 let mut map = TimedMap::new();
1122
1123 map.insert_constant(1, "initial value");
1124 assert_eq!(map.get(&1), Some(&"initial value"));
1125
1126 map.insert_expirable(1, "updated value", Duration::from_secs(1));
1128 assert_eq!(map.get(&1), Some(&"updated value"));
1129
1130 std::thread::sleep(Duration::from_secs(2));
1131
1132 assert_eq!(map.get(&1), None);
1134 }
1135
1136 #[test]
1137 fn std_insert_constant_and_expirable_combined() {
1138 let mut map = TimedMap::new();
1139
1140 map.insert_constant(1, "constant value");
1142 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
1143
1144 assert_eq!(map.get(&1), Some(&"constant value"));
1146 assert_eq!(map.get(&2), Some(&"expirable value"));
1147
1148 std::thread::sleep(Duration::from_secs(3));
1150
1151 assert_eq!(map.get(&1), Some(&"constant value"));
1153 assert_eq!(map.get(&2), None);
1154 }
1155
1156 #[test]
1157 fn std_expirable_entry_still_valid_before_expiration() {
1158 let mut map = TimedMap::new();
1159
1160 map.insert_expirable(1, "expirable value", Duration::from_secs(3));
1162
1163 std::thread::sleep(Duration::from_secs(2));
1165
1166 assert_eq!(map.get(&1), Some(&"expirable value"));
1168 assert!(map.get_remaining_duration(&1).unwrap().as_secs() == 1);
1169 }
1170
1171 #[test]
1172 fn std_length_functions() {
1173 let mut map = TimedMap::new();
1174
1175 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1176 map.insert_expirable(2, "expirable value", Duration::from_secs(1));
1177 map.insert_expirable(3, "expirable value", Duration::from_secs(3));
1178 map.insert_expirable(4, "expirable value", Duration::from_secs(3));
1179 map.insert_expirable(5, "expirable value", Duration::from_secs(3));
1180 map.insert_expirable(6, "expirable value", Duration::from_secs(3));
1181
1182 std::thread::sleep(Duration::from_secs(2).add(Duration::from_millis(1)));
1183
1184 assert_eq!(map.len(), 4);
1185 assert_eq!(map.len_expired(), 2);
1186 assert_eq!(map.len_unchecked(), 6);
1187 }
1188
1189 #[test]
1190 fn std_update_expirable_entry() {
1191 let mut map = TimedMap::new();
1192
1193 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1194 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
1195
1196 std::thread::sleep(Duration::from_secs(2));
1197
1198 assert!(!map.expiries.contains_key(&1));
1199 assert!(map.expiries.contains_key(&5));
1200 assert_eq!(map.get(&1), Some(&"expirable value"));
1201 }
1202
1203 #[test]
1204 fn std_update_expirable_entry_status() {
1205 let mut map = TimedMap::new();
1206
1207 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1208 let old_status = map
1209 .update_expiration_status(1, Duration::from_secs(5))
1210 .expect("entry update shouldn't fail");
1211 assert!(matches!(old_status, Some(EntryStatus::ExpiresAtSeconds(_))));
1212
1213 std::thread::sleep(Duration::from_secs(3));
1214 assert!(!map.expiries.contains_key(&1));
1215 assert!(map.expiries.contains_key(&5));
1216 assert_eq!(map.get(&1), Some(&"expirable value"));
1217 }
1218
1219 #[test]
1220 fn std_update_constant_entry_status_returns_none() {
1221 let mut map = TimedMap::new();
1222
1223 map.insert_constant(1, "initial value");
1224
1225 let old_status = map
1226 .update_expiration_status(1, Duration::from_secs(5))
1227 .expect("entry update shouldn't fail");
1228
1229 assert!(old_status.is_none());
1230 assert_eq!(map.get(&1), Some(&"initial value"));
1231 }
1232
1233 #[test]
1234 fn std_update_expirable_entry_status_with_previou_time() {
1235 let mut map = TimedMap::new();
1236
1237 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
1239 map.update_expiration_status(1, Duration::from_secs(5))
1240 .expect("entry update shouldn't fail");
1241
1242 assert_eq!(map.get(&1), Some(&"expirable value"));
1244 assert!(map.expiries.contains_key(&5));
1245 }
1246
1247 #[test]
1248 fn std_contains_key() {
1249 let mut map = TimedMap::new();
1250
1251 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1253 assert!(map.contains_key(&1));
1254 }
1255
1256 #[test]
1257 fn std_does_not_contain_key_anymore() {
1258 let mut map = TimedMap::new();
1259
1260 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1262 std::thread::sleep(Duration::from_secs(2));
1263 assert!(!map.contains_key(&1));
1264 }
1265
1266 #[test]
1267 fn std_contains_key_unchecked() {
1268 let mut map = TimedMap::new();
1269
1270 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1271 std::thread::sleep(Duration::from_secs(2));
1272 assert!(map.contains_key_unchecked(&1));
1273 }
1274
1275 #[test]
1276 fn std_keys_and_is_empty_ignore_expired_entries() {
1277 let mut map = TimedMap::new();
1278
1279 map.insert_expirable(1, "expired value", Duration::from_secs(1));
1280 map.insert_constant(2, "constant value");
1281
1282 std::thread::sleep(Duration::from_secs(2));
1283
1284 assert_eq!(map.keys().as_slice(), &[2]);
1285 assert_eq!(map.keys_expired().as_slice(), &[1]);
1286 assert_eq!(map.keys_unchecked().as_slice(), &[1, 2]);
1287 assert!(!map.is_empty());
1288 assert!(!map.is_empty_unchecked());
1289
1290 map.remove(&2);
1291
1292 assert!(map.is_empty());
1293 assert_eq!(map.keys_expired().as_slice(), &[1]);
1294 assert!(!map.is_empty_unchecked());
1295 }
1296
1297 #[test]
1298 fn std_iter_empty() {
1299 let map: TimedMap<u64, &str> = TimedMap::new();
1300 assert_eq!(map.iter().count(), 0);
1301 assert_eq!(map.into_iter().count(), 0);
1302 }
1303
1304 #[test]
1305 fn std_iter_all_expired() {
1306 let mut map = TimedMap::new();
1307
1308 map.insert_expirable(1, "val1", Duration::from_secs(1));
1309 map.insert_expirable(2, "val2", Duration::from_secs(1));
1310
1311 std::thread::sleep(Duration::from_secs(2));
1312
1313 assert_eq!(map.iter().count(), 0);
1314 assert_eq!(map.into_iter().count(), 0);
1315 }
1316
1317 #[test]
1318 fn std_iter_mixed() {
1319 let mut map = TimedMap::new();
1320
1321 map.insert_expirable(1, "val1", Duration::from_secs(1));
1322 map.insert_constant(2, "val2");
1323 map.insert_expirable(3, "val3", Duration::from_secs(5));
1324
1325 std::thread::sleep(Duration::from_secs(2));
1327
1328 let mut collected: Vec<(&u64, &&str)> = map.iter().collect();
1329 collected.sort_by_key(|(k, _)| *k);
1330
1331 assert_eq!(collected.len(), 2);
1332 assert_eq!(collected[0], (&2, &"val2"));
1333 assert_eq!(collected[1], (&3, &"val3"));
1334 }
1335
1336 #[test]
1337 fn std_iter_mut_mixed() {
1338 let mut map = TimedMap::new();
1339
1340 map.insert_expirable(1, "val1", Duration::from_secs(1));
1341 map.insert_constant(2, "val2");
1342 map.insert_expirable(3, "val3", Duration::from_secs(5));
1343
1344 std::thread::sleep(Duration::from_secs(2));
1346
1347 for (k, v) in map.iter_mut() {
1348 if *k == 2 {
1349 *v = "modified_val2";
1350 }
1351 }
1352
1353 assert_eq!(map.get(&1), None);
1354 assert_eq!(map.get(&2), Some(&"modified_val2"));
1355 assert_eq!(map.get(&3), Some(&"val3"));
1356 }
1357}