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)]
456 pub fn keys(&self) -> Vec<K> {
457 self.map.keys()
458 }
459
460 #[inline(always)]
462 pub fn is_empty(&self) -> bool {
463 self.map.is_empty()
464 }
465
466 #[inline(always)]
472 fn insert(&mut self, k: K, v: V, expires_at: Option<u64>) -> Option<V> {
473 let entry = ExpirableEntry::new(v, expires_at);
474 match self.map.insert(k.clone(), entry) {
475 Some(old) => {
476 if let EntryStatus::ExpiresAtSeconds(e) = old.status() {
478 self.drop_key_from_expiry(e, &k)
479 }
480
481 Some(old.owned_value())
482 }
483 None => None,
484 }
485 }
486
487 pub fn insert_expirable(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
496 self.expiration_tick += 1;
497
498 let now = self.clock.elapsed_seconds_since_creation();
499 if self.expiration_tick >= self.expiration_tick_cap {
500 self.drop_expired_entries_inner(now);
501 self.expiration_tick = 0;
502 }
503
504 let expires_at = now + duration.as_secs();
505
506 let res = self.insert(k.clone(), v, Some(expires_at));
507
508 self.expiries.entry(expires_at).or_default().insert(k);
509
510 res
511 }
512
513 pub fn insert_expirable_unchecked(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
522 let now = self.clock.elapsed_seconds_since_creation();
523 let expires_at = now + duration.as_secs();
524
525 let res = self.insert(k.clone(), v, Some(expires_at));
526
527 self.expiries.entry(expires_at).or_default().insert(k);
528
529 res
530 }
531
532 pub fn insert_constant(&mut self, k: K, v: V) -> Option<V> {
541 self.expiration_tick += 1;
542
543 let now = self.clock.elapsed_seconds_since_creation();
544 if self.expiration_tick >= self.expiration_tick_cap {
545 self.drop_expired_entries_inner(now);
546 self.expiration_tick = 0;
547 }
548
549 self.insert(k, v, None)
550 }
551
552 pub fn insert_constant_unchecked(&mut self, k: K, v: V) -> Option<V> {
561 self.expiration_tick += 1;
562 self.insert(k, v, None)
563 }
564
565 #[inline(always)]
571 pub fn remove(&mut self, k: &K) -> Option<V> {
572 self.map
573 .remove(k)
574 .filter(|v| {
575 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
576 self.drop_key_from_expiry(expires_at_seconds, k);
577 }
578
579 !v.is_expired(self.clock.elapsed_seconds_since_creation())
580 })
581 .map(|v| v.owned_value())
582 }
583
584 #[inline(always)]
589 pub fn remove_unchecked(&mut self, k: &K) -> Option<V> {
590 self.map
591 .remove(k)
592 .filter(|v| {
593 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
594 self.drop_key_from_expiry(expires_at_seconds, k);
595 }
596
597 true
598 })
599 .map(|v| v.owned_value())
600 }
601
602 #[inline(always)]
604 pub fn clear(&mut self) {
605 self.map.clear()
606 }
607
608 pub fn iter(&self) -> Iter<'_, K, V> {
610 self.into_iter()
611 }
612
613 pub fn iter_unchecked(&self) -> impl Iterator<Item = (&K, &V)> {
615 self.map.iter().map(|(k, v)| (k, v.value()))
616 }
617
618 pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
620 self.into_iter()
621 }
622
623 pub fn iter_mut_unchecked(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
625 self.map.iter_mut().map(|(k, v)| (k, v.value_mut()))
626 }
627
628 pub fn update_expiration_status(
633 &mut self,
634 key: K,
635 duration: Duration,
636 ) -> Result<Option<EntryStatus>, &'static str> {
637 match self.map.get_mut(&key) {
638 Some(entry) => {
639 let old_status = *entry.status();
640 let now = self.clock.elapsed_seconds_since_creation();
641 let expires_at = now + duration.as_secs();
642
643 entry.update_status(EntryStatus::ExpiresAtSeconds(expires_at));
644
645 if let EntryStatus::ExpiresAtSeconds(t) = &old_status {
646 self.drop_key_from_expiry(t, &key);
647 }
648 self.expiries
649 .entry(expires_at)
650 .or_default()
651 .insert(key.clone());
652
653 Ok(Some(old_status))
654 }
655 None => Err("entry not found"),
656 }
657 }
658
659 #[inline(always)]
664 pub fn drop_expired_entries(&mut self) -> Vec<(K, V)> {
665 let now = self.clock.elapsed_seconds_since_creation();
666 self.drop_expired_entries_inner(now)
667 }
668
669 fn drop_expired_entries_inner(&mut self, now: u64) -> Vec<(K, V)> {
670 let mut expired_entries = Vec::new();
671 while let Some((exp, keys)) = self.expiries.pop_first() {
673 if exp > now {
675 self.expiries.insert(exp, keys);
676 break;
677 }
678
679 for key in keys {
680 if let Some(value) = self.map.remove(&key) {
681 expired_entries.push((key, value.owned_value()));
682 }
683 }
684 }
685
686 expired_entries
687 }
688
689 fn drop_key_from_expiry(&mut self, expiry_key: &u64, map_key: &K) {
690 if let Some(list) = self.expiries.get_mut(expiry_key) {
691 list.remove(map_key);
692
693 if list.is_empty() {
694 self.expiries.remove(expiry_key);
695 }
696 }
697 }
698
699 #[inline(always)]
703 pub fn contains_key(&self, k: &K) -> bool {
704 self.get(k).is_some()
705 }
706
707 #[inline(always)]
711 pub fn contains_key_unchecked(&self, k: &K) -> bool {
712 self.get_unchecked(k).is_some()
713 }
714}
715
716#[cfg(test)]
717#[cfg(not(feature = "std"))]
718mod tests {
719 use super::*;
720
721 #[derive(Clone, Copy)]
722 struct MockClock {
723 current_time: u64,
724 }
725
726 impl Clock for MockClock {
727 fn elapsed_seconds_since_creation(&self) -> u64 {
728 self.current_time
729 }
730 }
731
732 #[test]
733 fn nostd_insert_and_get_constant_entry() {
734 let clock = MockClock { current_time: 1000 };
735 let mut map = TimedMap::new(clock);
736
737 map.insert_constant(1, "constant value");
738
739 assert_eq!(map.get(&1), Some(&"constant value"));
740 assert_eq!(map.get_remaining_duration(&1), None);
741 }
742
743 #[test]
744 fn nostd_insert_and_get_expirable_entry() {
745 let clock = MockClock { current_time: 1000 };
746 let mut map = TimedMap::new(clock);
747 let duration = Duration::from_secs(60);
748
749 map.insert_expirable(1, "expirable value", duration);
750
751 assert_eq!(map.get(&1), Some(&"expirable value"));
752 assert_eq!(map.get_remaining_duration(&1), Some(duration));
753 }
754
755 #[test]
756 fn nostd_expired_entry() {
757 let clock = MockClock { current_time: 1000 };
758 let mut map = TimedMap::new(clock);
759 let duration = Duration::from_secs(60);
760
761 map.insert_expirable(1, "expirable value", duration);
763
764 let clock = MockClock { current_time: 1070 };
766 map.clock = clock;
767
768 assert_eq!(map.get(&1), None);
770 assert_eq!(map.get_remaining_duration(&1), None);
771 }
772
773 #[test]
774 fn nostd_remove_entry() {
775 let clock = MockClock { current_time: 1000 };
776 let mut map = TimedMap::new(clock);
777
778 map.insert_constant(1, "constant value");
779
780 assert_eq!(map.remove(&1), Some("constant value"));
781 assert_eq!(map.get(&1), None);
782 }
783
784 #[test]
785 fn nostd_drop_expired_entries() {
786 let clock = MockClock { current_time: 1000 };
787 let mut map = TimedMap::new(clock);
788
789 map.insert_expirable(1, "expirable value1", Duration::from_secs(50));
791 map.insert_expirable(2, "expirable value2", Duration::from_secs(70));
792 map.insert_constant(3, "constant value");
793
794 let clock = MockClock { current_time: 1055 };
796 map.clock = clock;
797
798 assert_eq!(map.get(&1), None);
800 assert_eq!(map.get(&2), Some(&"expirable value2"));
801 assert_eq!(map.get(&3), Some(&"constant value"));
802
803 let clock = MockClock { current_time: 1071 };
805 map.clock = clock;
806
807 assert_eq!(map.get(&1), None);
808 assert_eq!(map.get(&2), None);
809 assert_eq!(map.get(&3), Some(&"constant value"));
810 }
811
812 #[test]
813 fn nostd_update_existing_entry() {
814 let clock = MockClock { current_time: 1000 };
815 let mut map = TimedMap::new(clock);
816
817 map.insert_constant(1, "initial value");
818 assert_eq!(map.get(&1), Some(&"initial value"));
819
820 map.insert_expirable(1, "updated value", Duration::from_secs(15));
822 assert_eq!(map.get(&1), Some(&"updated value"));
823
824 let clock = MockClock { current_time: 1016 };
826 map.clock = clock;
827
828 assert_eq!(map.get(&1), None);
829 }
830
831 #[test]
832 fn nostd_update_expirable_entry_status() {
833 let clock = MockClock { current_time: 1000 };
834 let mut map = TimedMap::new(clock);
835
836 map.insert_constant(1, "initial value");
837 assert_eq!(map.get(&1), Some(&"initial value"));
838
839 map.update_expiration_status(1, Duration::from_secs(16))
841 .expect("entry update shouldn't fail");
842 assert_eq!(map.get(&1), Some(&"initial value"));
843
844 let clock = MockClock { current_time: 1017 };
846 map.clock = clock;
847 assert_eq!(map.get(&1), None);
848 }
849
850 #[test]
851 fn nostd_update_expirable_entry_status_with_previou_time() {
852 let clock = MockClock { current_time: 1000 };
853 let mut map = TimedMap::new(clock);
854
855 map.insert_expirable(1, "expirable value", Duration::from_secs(15));
857 map.update_expiration_status(1, Duration::from_secs(15))
858 .expect("entry update shouldn't fail");
859
860 assert_eq!(map.get(&1), Some(&"expirable value"));
862 assert!(map.expiries.contains_key(&1015));
863 }
864
865 #[test]
866 fn nostd_contains_key() {
867 let clock = MockClock { current_time: 1000 };
868 let mut map = TimedMap::new(clock);
869
870 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
872 assert!(map.contains_key(&1));
873 }
874
875 #[test]
876 fn nostd_iter_empty() {
877 let clock = MockClock { current_time: 1000 };
878 let map: TimedMap<u64, &str, MockClock> = TimedMap::new(clock);
879 assert_eq!(map.iter().count(), 0);
880 assert_eq!(map.into_iter().count(), 0);
881 }
882
883 #[test]
884 fn nostd_iter_all_expired() {
885 let mut clock = MockClock { current_time: 1000 };
886 let mut map = TimedMap::new(clock);
887
888 map.insert_expirable(1, "val1", Duration::from_secs(1));
890 map.insert_expirable(2, "val2", Duration::from_secs(2));
892
893 clock.current_time = 1003;
894 map.clock = clock;
895
896 assert_eq!(map.iter().count(), 0);
897 assert_eq!(map.into_iter().count(), 0);
898 }
899
900 #[test]
901 fn nostd_iter_mixed() {
902 let mut clock = MockClock { current_time: 1000 };
903 let mut map = TimedMap::new(clock);
904
905 map.insert_expirable(1, "val1", Duration::from_secs(1));
907 map.insert_constant(2, "val2");
909 map.insert_expirable(3, "val3", Duration::from_secs(5));
911
912 clock.current_time = 1002;
913 map.clock = clock;
914
915 let mut collected: Vec<(&u64, &&str)> = map.iter().collect();
916 collected.sort_by_key(|(k, _)| *k);
917
918 assert_eq!(collected.len(), 2);
919 assert_eq!(collected[0], (&2, &"val2"));
920 assert_eq!(collected[1], (&3, &"val3"));
921 }
922
923 #[test]
924 fn nostd_iter_mut_mixed() {
925 let mut clock = MockClock { current_time: 1000 };
926 let mut map = TimedMap::new(clock);
927
928 map.insert_expirable(1, "val1", Duration::from_secs(1));
930 map.insert_constant(2, "val2");
932 map.insert_expirable(3, "val3", Duration::from_secs(5));
934
935 clock.current_time = 1002;
936 map.clock = clock;
937
938 for (k, v) in map.iter_mut() {
939 if *k == 2 {
940 *v = "modified_val2";
941 }
942 }
943
944 assert_eq!(map.get(&1), None);
945 assert_eq!(map.get(&2), Some(&"modified_val2"));
946 assert_eq!(map.get(&3), Some(&"val3"));
947 }
948}
949
950#[cfg(feature = "std")]
951#[cfg(test)]
952mod std_tests {
953 use core::ops::Add;
954
955 use super::*;
956
957 #[test]
958 fn std_expirable_and_constant_entries() {
959 let mut map = TimedMap::new();
960
961 map.insert_constant(1, "constant value");
962 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
963
964 assert_eq!(map.get(&1), Some(&"constant value"));
965 assert_eq!(map.get(&2), Some(&"expirable value"));
966
967 assert_eq!(map.get_remaining_duration(&1), None);
968 assert!(map.get_remaining_duration(&2).is_some());
969 }
970
971 #[test]
972 fn std_expired_entry_removal() {
973 let mut map = TimedMap::new();
974 let duration = Duration::from_secs(2);
975
976 map.insert_expirable(1, "expirable value", duration);
977
978 std::thread::sleep(Duration::from_secs(3));
980
981 assert_eq!(map.get(&1), None);
983 assert_eq!(map.get_remaining_duration(&1), None);
984 }
985
986 #[test]
987 fn std_remove_entry() {
988 let mut map = TimedMap::new();
989
990 map.insert_constant(1, "constant value");
991 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
992
993 assert_eq!(map.remove(&1), Some("constant value"));
994 assert_eq!(map.remove(&2), Some("expirable value"));
995
996 assert_eq!(map.get(&1), None);
997 assert_eq!(map.get(&2), None);
998 }
999
1000 #[test]
1001 fn std_drop_expired_entries() {
1002 let mut map = TimedMap::new();
1003
1004 map.insert_expirable(1, "expirable value1", Duration::from_secs(2));
1005 map.insert_expirable(2, "expirable value2", Duration::from_secs(4));
1006
1007 std::thread::sleep(Duration::from_secs(3));
1009
1010 assert_eq!(map.get(&1), None);
1012 assert_eq!(map.get(&2), Some(&"expirable value2"));
1013 }
1014
1015 #[test]
1016 fn std_update_existing_entry() {
1017 let mut map = TimedMap::new();
1018
1019 map.insert_constant(1, "initial value");
1020 assert_eq!(map.get(&1), Some(&"initial value"));
1021
1022 map.insert_expirable(1, "updated value", Duration::from_secs(1));
1024 assert_eq!(map.get(&1), Some(&"updated value"));
1025
1026 std::thread::sleep(Duration::from_secs(2));
1027
1028 assert_eq!(map.get(&1), None);
1030 }
1031
1032 #[test]
1033 fn std_insert_constant_and_expirable_combined() {
1034 let mut map = TimedMap::new();
1035
1036 map.insert_constant(1, "constant value");
1038 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
1039
1040 assert_eq!(map.get(&1), Some(&"constant value"));
1042 assert_eq!(map.get(&2), Some(&"expirable value"));
1043
1044 std::thread::sleep(Duration::from_secs(3));
1046
1047 assert_eq!(map.get(&1), Some(&"constant value"));
1049 assert_eq!(map.get(&2), None);
1050 }
1051
1052 #[test]
1053 fn std_expirable_entry_still_valid_before_expiration() {
1054 let mut map = TimedMap::new();
1055
1056 map.insert_expirable(1, "expirable value", Duration::from_secs(3));
1058
1059 std::thread::sleep(Duration::from_secs(2));
1061
1062 assert_eq!(map.get(&1), Some(&"expirable value"));
1064 assert!(map.get_remaining_duration(&1).unwrap().as_secs() == 1);
1065 }
1066
1067 #[test]
1068 fn std_length_functions() {
1069 let mut map = TimedMap::new();
1070
1071 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1072 map.insert_expirable(2, "expirable value", Duration::from_secs(1));
1073 map.insert_expirable(3, "expirable value", Duration::from_secs(3));
1074 map.insert_expirable(4, "expirable value", Duration::from_secs(3));
1075 map.insert_expirable(5, "expirable value", Duration::from_secs(3));
1076 map.insert_expirable(6, "expirable value", Duration::from_secs(3));
1077
1078 std::thread::sleep(Duration::from_secs(2).add(Duration::from_millis(1)));
1079
1080 assert_eq!(map.len(), 4);
1081 assert_eq!(map.len_expired(), 2);
1082 assert_eq!(map.len_unchecked(), 6);
1083 }
1084
1085 #[test]
1086 fn std_update_expirable_entry() {
1087 let mut map = TimedMap::new();
1088
1089 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1090 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
1091
1092 std::thread::sleep(Duration::from_secs(2));
1093
1094 assert!(!map.expiries.contains_key(&1));
1095 assert!(map.expiries.contains_key(&5));
1096 assert_eq!(map.get(&1), Some(&"expirable value"));
1097 }
1098
1099 #[test]
1100 fn std_update_expirable_entry_status() {
1101 let mut map = TimedMap::new();
1102
1103 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1104 map.update_expiration_status(1, Duration::from_secs(5))
1105 .expect("entry update shouldn't fail");
1106
1107 std::thread::sleep(Duration::from_secs(3));
1108 assert!(!map.expiries.contains_key(&1));
1109 assert!(map.expiries.contains_key(&5));
1110 assert_eq!(map.get(&1), Some(&"expirable value"));
1111 }
1112
1113 #[test]
1114 fn std_update_expirable_entry_status_with_previou_time() {
1115 let mut map = TimedMap::new();
1116
1117 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
1119 map.update_expiration_status(1, Duration::from_secs(5))
1120 .expect("entry update shouldn't fail");
1121
1122 assert_eq!(map.get(&1), Some(&"expirable value"));
1124 assert!(map.expiries.contains_key(&5));
1125 }
1126
1127 #[test]
1128 fn std_contains_key() {
1129 let mut map = TimedMap::new();
1130
1131 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1133 assert!(map.contains_key(&1));
1134 }
1135
1136 #[test]
1137 fn std_does_not_contain_key_anymore() {
1138 let mut map = TimedMap::new();
1139
1140 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1142 std::thread::sleep(Duration::from_secs(2));
1143 assert!(!map.contains_key(&1));
1144 }
1145
1146 #[test]
1147 fn std_contains_key_unchecked() {
1148 let mut map = TimedMap::new();
1149
1150 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1151 std::thread::sleep(Duration::from_secs(2));
1152 assert!(map.contains_key_unchecked(&1));
1153 }
1154
1155 #[test]
1156 fn std_iter_empty() {
1157 let map: TimedMap<u64, &str> = TimedMap::new();
1158 assert_eq!(map.iter().count(), 0);
1159 assert_eq!(map.into_iter().count(), 0);
1160 }
1161
1162 #[test]
1163 fn std_iter_all_expired() {
1164 let mut map = TimedMap::new();
1165
1166 map.insert_expirable(1, "val1", Duration::from_secs(1));
1167 map.insert_expirable(2, "val2", Duration::from_secs(1));
1168
1169 std::thread::sleep(Duration::from_secs(2));
1170
1171 assert_eq!(map.iter().count(), 0);
1172 assert_eq!(map.into_iter().count(), 0);
1173 }
1174
1175 #[test]
1176 fn std_iter_mixed() {
1177 let mut map = TimedMap::new();
1178
1179 map.insert_expirable(1, "val1", Duration::from_secs(1));
1180 map.insert_constant(2, "val2");
1181 map.insert_expirable(3, "val3", Duration::from_secs(5));
1182
1183 std::thread::sleep(Duration::from_secs(2));
1185
1186 let mut collected: Vec<(&u64, &&str)> = map.iter().collect();
1187 collected.sort_by_key(|(k, _)| *k);
1188
1189 assert_eq!(collected.len(), 2);
1190 assert_eq!(collected[0], (&2, &"val2"));
1191 assert_eq!(collected[1], (&3, &"val3"));
1192 }
1193
1194 #[test]
1195 fn std_iter_mut_mixed() {
1196 let mut map = TimedMap::new();
1197
1198 map.insert_expirable(1, "val1", Duration::from_secs(1));
1199 map.insert_constant(2, "val2");
1200 map.insert_expirable(3, "val3", Duration::from_secs(5));
1201
1202 std::thread::sleep(Duration::from_secs(2));
1204
1205 for (k, v) in map.iter_mut() {
1206 if *k == 2 {
1207 *v = "modified_val2";
1208 }
1209 }
1210
1211 assert_eq!(map.get(&1), None);
1212 assert_eq!(map.get(&2), Some(&"modified_val2"));
1213 assert_eq!(map.get(&3), Some(&"val3"));
1214 }
1215}