structured_proxy/shield/
window.rs1use std::time::Duration;
10
11pub fn epoch(now: Duration, window: Duration) -> u64 {
14 now.as_secs() / window.as_secs().max(1)
15}
16
17pub fn elapsed_in_window(now: Duration, window: Duration) -> Duration {
19 let w = window.as_secs().max(1);
20 Duration::from_secs(now.as_secs() % w) + Duration::from_nanos(u64::from(now.subsec_nanos()))
21}
22
23pub fn sliding_estimate(cur: u64, prev: u64, elapsed: Duration, window: Duration) -> f64 {
27 let w = window.as_secs_f64().max(f64::MIN_POSITIVE);
28 let frac = (elapsed.as_secs_f64() / w).clamp(0.0, 1.0);
29 cur as f64 + prev as f64 * (1.0 - frac)
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 const W: Duration = Duration::from_secs(60);
37
38 #[test]
39 fn epoch_advances_once_per_window() {
40 assert_eq!(epoch(Duration::from_secs(0), W), 0);
41 assert_eq!(epoch(Duration::from_secs(59), W), 0);
42 assert_eq!(epoch(Duration::from_secs(60), W), 1);
43 assert_eq!(epoch(Duration::from_secs(125), W), 2);
44 }
45
46 #[test]
47 fn estimate_at_window_start_counts_full_previous() {
48 assert_eq!(sliding_estimate(0, 100, Duration::ZERO, W), 100.0);
50 }
51
52 #[test]
53 fn estimate_at_window_end_drops_previous() {
54 let est = sliding_estimate(10, 100, W, W);
56 assert!((est - 10.0).abs() < 1e-9);
57 }
58
59 #[test]
60 fn estimate_midway_halves_previous() {
61 let est = sliding_estimate(10, 100, Duration::from_secs(30), W);
62 assert!((est - 60.0).abs() < 1e-9); }
64}