1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub struct Budgets {
11 pub window_ms: u64,
13 pub rst_received: u32,
15 pub rst_sent: u32,
19 pub ping: u32,
21 pub settings: u32,
23 pub empty_frames: u32,
26 pub priority: u32,
28 pub small_window_updates: u32,
30}
31
32impl Default for Budgets {
33 fn default() -> Self {
34 Budgets {
35 window_ms: 10_000,
36 rst_received: 200,
37 rst_sent: 200,
38 ping: 100,
39 settings: 100,
40 empty_frames: 200,
41 priority: 1000,
42 small_window_updates: 500,
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub(crate) enum Kind {
49 RstReceived = 0,
50 RstSent,
51 Ping,
52 Settings,
53 Empty,
54 Priority,
55 SmallWindowUpdate,
56}
57
58#[derive(Clone, Copy, Debug, Default)]
59struct Counter {
60 prev: u32,
61 cur: u32,
62 start: u64,
63}
64
65#[derive(Clone, Debug)]
66pub(crate) struct Meter {
67 limits: [u32; 7],
68 window: u64,
69 counters: [Counter; 7],
70}
71
72impl Meter {
73 pub(crate) fn new(b: &Budgets) -> Meter {
74 Meter {
75 limits: [b.rst_received, b.rst_sent, b.ping, b.settings, b.empty_frames, b.priority, b.small_window_updates],
76 window: b.window_ms.max(1),
77 counters: [Counter { prev: 0, cur: 0, start: 0 }; 7],
78 }
79 }
80
81 #[inline(never)]
83 pub(crate) fn hit(&mut self, kind: Kind, now: u64) -> bool {
84 let limit = self.limits[kind as usize];
85 if limit == u32::MAX {
86 return false;
87 }
88 let w = self.window;
89 let c = &mut self.counters[kind as usize];
90 let elapsed = now.saturating_sub(c.start);
91 if elapsed >= w {
92 c.prev = if elapsed >= 2 * w { 0 } else { c.cur };
93 c.cur = 0;
94 c.start = now - elapsed % w;
95 }
96 c.cur = c.cur.saturating_add(1);
97 let into = now.saturating_sub(c.start).min(w);
98 let estimate = (c.prev as u64 * (w - into)).div_ceil(w) + c.cur as u64;
99 estimate > limit as u64
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn sliding_window() {
109 let b = Budgets { window_ms: 1000, rst_received: 10, ..Budgets::default() };
110 let mut m = Meter::new(&b);
111 for i in 0..10 {
112 assert!(!m.hit(Kind::RstReceived, 5000 + i), "{i}");
113 }
114 assert!(m.hit(Kind::RstReceived, 5010));
115 let mut m = Meter::new(&b);
117 for _ in 0..10 {
118 assert!(!m.hit(Kind::RstReceived, 999));
119 }
120 assert!(m.hit(Kind::RstReceived, 1001));
121 let mut m = Meter::new(&b);
123 for _ in 0..10 {
124 m.hit(Kind::RstReceived, 0);
125 }
126 assert!(!m.hit(Kind::RstReceived, 2500));
127 let mut m = Meter::new(&b);
129 for t in 0..10_000u64 {
130 if t % 125 == 0 {
131 assert!(!m.hit(Kind::RstReceived, t), "{t}");
132 }
133 }
134 let mut m = Meter::new(&Budgets { ping: u32::MAX, ..b });
136 for _ in 0..100_000 {
137 assert!(!m.hit(Kind::Ping, 0));
138 }
139 }
140}