Skip to content
File

Blob: firmware/vendor/str0m/src/util/value_history.rs

rust110 lines
1use std::collections::VecDeque;
2use std::iter::Sum;
3use std::ops::{AddAssign, SubAssign};
4use std::time::{Duration, Instant};
5 
6/// Holds a history values of type T for up to a certain Duration, as well as the
7/// cumulated (total) value.
8#[derive(Debug)]
9pub(crate) struct ValueHistory<T> {
10 value: T,
11 history: VecDeque<(Instant, T)>,
12 max_time: Duration,
13}
14 
15const DEFAULT_VALUE_HISTORY_DURATION: Duration = Duration::from_secs(1);
16 
17impl<T: Default> Default for ValueHistory<T> {
18 fn default() -> Self {
19 Self {
20 value: Default::default(),
21 history: Default::default(),
22 max_time: DEFAULT_VALUE_HISTORY_DURATION,
23 }
24 }
25}
26 
27impl<T> ValueHistory<T>
28where
29 T: Copy + AddAssign + SubAssign + Sum,
30{
31 /// Adds a timed value
32 /// Note: time should always monotonically increase in subsequent calls to add()
33 pub fn push(&mut self, t: Instant, v: T) {
34 self.value += v;
35 self.history.push_back((t, v));
36 }
37 
38 /// Returns the sum of all values in the history up to max_time. Might
39 /// return stale value unless [`ValueHistory::purge_old`] is called before.
40 pub fn sum(&self) -> T {
41 self.value
42 }
43 
44 /// Recalculates sum purging values older than `now - max_time`.
45 pub fn purge_old(&mut self, now: Instant) {
46 while {
47 let Some(front_t) = self.history.front().map(|v| v.0) else {
48 return;
49 };
50 now.duration_since(front_t) > self.max_time
51 } {
52 if let Some((_, v)) = self.history.pop_front() {
53 self.value -= v;
54 }
55 }
56 }
57}
58 
59#[allow(clippy::unchecked_duration_subtraction)]
60#[cfg(test)]
61mod test {
62 use std::time::{Duration, Instant};
63 
64 use super::ValueHistory;
65 
66 #[test]
67 fn with_value_test() {
68 let now = Instant::now();
69 
70 let mut h = ValueHistory {
71 value: 11,
72 max_time: Duration::from_secs(1),
73 ..Default::default()
74 };
75 
76 assert_eq!(h.sum(), 11);
77 h.purge_old(now);
78 assert_eq!(h.sum(), 11);
79 h.push(now - Duration::from_millis(1500), 22);
80 h.push(now - Duration::from_millis(500), 22);
81 assert_eq!(h.sum(), 11 + 22 + 22);
82 h.purge_old(now);
83 assert_eq!(h.sum(), 11 + 22);
84 h.push(now, 0);
85 assert_eq!(h.sum(), 11 + 22);
86 }
87 
88 #[test]
89 fn test() {
90 let now = Instant::now();
91 let mut h = ValueHistory::default();
92 
93 assert_eq!(h.sum(), 0);
94 h.push(now - Duration::from_millis(1500), 22);
95 assert_eq!(h.sum(), 22);
96 h.purge_old(now);
97 assert_eq!(h.sum(), 0);
98 h.push(now - Duration::from_millis(700), 22);
99 h.push(now - Duration::from_millis(500), 33);
100 assert_eq!(h.sum(), 22 + 33);
101 h.purge_old(now);
102 assert_eq!(h.sum(), 22 + 33);
103 
104 h.purge_old(now + Duration::from_millis(400));
105 assert_eq!(h.sum(), 33);
106 h.purge_old(now + Duration::from_millis(600));
107 assert_eq!(h.sum(), 0);
108 }
109}