1use std::collections::VecDeque;
5use std::time::{Duration, Instant};
6
7const WINDOW: Duration = Duration::from_secs(2);
9const NANOS_PER_SECOND: u128 = 1_000_000_000;
10
11#[derive(Debug, Default)]
12pub(crate) struct SpeedMeter {
13 samples: VecDeque<(Instant, u64)>,
15}
16
17impl SpeedMeter {
18 pub(crate) fn record(&mut self, now: Instant, received: u64) -> u64 {
21 self.samples.push_back((now, received));
22 while self
24 .samples
25 .get(1)
26 .is_some_and(|&(taken, _)| now.duration_since(taken) >= WINDOW)
27 {
28 self.samples.pop_front();
29 }
30 let Some(&(since, from)) = self.samples.front() else {
31 return 0;
32 };
33 let elapsed = now.duration_since(since).as_nanos();
34 if elapsed == 0 {
35 return 0;
36 }
37 let bytes = u128::from(received.saturating_sub(from));
38 u64::try_from(bytes * NANOS_PER_SECOND / elapsed).unwrap_or(u64::MAX)
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn averages_over_the_last_two_seconds() {
48 let start = Instant::now();
49 let at = |millis| start + Duration::from_millis(millis);
50 let mut meter = SpeedMeter::default();
51 assert_eq!(meter.record(at(0), 0), 0, "one sample has no speed yet");
52 assert_eq!(meter.record(at(500), 1_000), 2_000);
53 assert_eq!(meter.record(at(1_000), 2_000), 2_000);
54 assert_eq!(
55 meter.record(at(3_000), 2_000),
56 0,
57 "nothing in the last two seconds"
58 );
59 assert_eq!(
60 meter.record(at(3_250), 2_500),
61 222,
62 "500 bytes over 2.25 seconds"
63 );
64 }
65}