1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::time::{Duration, Instant};
use std::{collections::VecDeque, sync::RwLock};
use crate::client::CtxField;
pub static TRAFF_COUNT: CtxField<RwLock<TraffCount>> = |_| RwLock::new(TraffCount::new());
/// A structure that maintains a history of traffic volumes.
/// Used to calculate speeds based on traffic over time.
pub struct TraffCount {
/// Per-second bins for traffic measurements
bins: VecDeque<f64>,
/// When the current time window started (for the oldest bin)
window_start: Instant,
/// Maximum history length to maintain (in seconds)
max_history_seconds: usize,
}
impl TraffCount {
/// Create a new traffic counter with default settings
pub fn new() -> Self {
let now = Instant::now();
Self {
bins: VecDeque::with_capacity(600), // Pre-allocate bins for a minute
window_start: now,
max_history_seconds: 600, // Keep up to a minute of history by default
}
}
// /// Create a new traffic counter with custom history length
// pub fn with_history(max_seconds: usize) -> Self {
// let now = Instant::now();
// Self {
// bins: VecDeque::with_capacity(max_seconds),
// window_start: now,
// max_history_seconds: max_seconds,
// }
// }
/// Increment the traffic count with the given number of bytes
pub fn incr(&mut self, bytes: f64) {
let now = Instant::now();
self.ensure_bins_updated(now);
// Add the new bytes to the most recent bin
if let Some(last) = self.bins.back_mut() {
*last += bytes;
} else if bytes > 0.0 {
// If there are no bins yet but we have traffic, create the first bin
self.bins.push_back(bytes);
}
}
/// Ensure the bins array is up-to-date with current time
fn ensure_bins_updated(&mut self, now: Instant) {
// Calculate how many seconds have passed since our window started
let seconds_elapsed = now.duration_since(self.window_start).as_secs() as usize;
if seconds_elapsed == 0 && !self.bins.is_empty() {
// Still in the same second, no need to add bins
return;
}
if self.bins.is_empty() {
// Initialize with a single bin if empty
self.bins.push_back(0.0);
return;
}
// Add new bins for elapsed seconds
for _ in 0..seconds_elapsed {
// If we exceed our max history, remove the oldest bin
if self.bins.len() >= self.max_history_seconds {
self.bins.pop_front();
}
// Add a new empty bin
self.bins.push_back(0.0);
}
// Update the window start time to account for the bins we've added
self.window_start += Duration::from_secs(seconds_elapsed as u64);
}
/// Gets a vector of speeds binned per second, in bytes per second
pub fn speed_history(&self) -> Vec<f64> {
// Return bins directly - they're already per-second
self.bins.iter().cloned().collect()
}
// /// Remove measurements that are too old
// fn cleanup(&mut self) {
// let now = Instant::now();
// self.ensure_bins_updated(now);
// }
}
impl Default for TraffCount {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_traffic_count_basic() {
let mut counter = TraffCount::new();
// Add some traffic
counter.incr(100.0);
counter.incr(200.0);
// We should have some history
let history = counter.speed_history();
assert!(!history.is_empty());
// The most recent entry should contain our traffic
assert!(history.last().unwrap() > &0.0);
}
}