1use crate::{packet::number::PacketNumber, varint::VarInt};
5use core::mem;
6
7#[derive(Clone, Default, Debug)]
8pub struct SlidingWindow {
9 window: Window,
12 right_edge: Option<PacketNumber>,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub enum SlidingWindowError {
19 Duplicate,
20 TooOld,
21}
22
23type Window = u128;
26
27const WINDOW_WIDTH: u64 = 1 + mem::size_of::<Window>() as u64 * 8;
30
31#[derive(Debug, PartialEq, Eq)]
32enum WindowPosition {
33 Left,
35 Right(u64),
37 RightEdge,
39 Within(u64),
41 Empty,
43}
44
45#[derive(Default, Clone)]
47pub struct EvictedSet {
48 window: Window,
53 right_edge: PacketNumber,
58}
59
60impl core::fmt::Debug for EvictedSet {
61 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62 f.debug_set().entries(self.clone()).finish()
63 }
64}
65
66impl PartialEq for EvictedSet {
67 fn eq(&self, other: &Self) -> bool {
68 self.clone().eq(other.clone())
69 }
70}
71
72impl Iterator for EvictedSet {
73 type Item = PacketNumber;
74
75 fn next(&mut self) -> Option<PacketNumber> {
76 loop {
77 if self.window == 0 {
79 return None;
80 }
81
82 let shift = self.window.leading_zeros() + 1;
83
84 self.right_edge = PacketNumber::from_varint(
89 PacketNumber::as_varint(self.right_edge) + VarInt::from_u32(shift),
90 self.right_edge.space(),
91 );
92
93 if shift == Window::BITS {
95 self.window = 0;
96 } else {
97 self.window <<= shift;
98 }
99
100 if let Some(left_edge) = PacketNumber::as_varint(self.right_edge)
101 .checked_sub(VarInt::from_u32(WINDOW_WIDTH as u32))
102 {
103 return Some(PacketNumber::from_varint(
104 left_edge,
105 self.right_edge.space(),
106 ));
107 } else {
108 continue;
111 }
112 }
113 }
114}
115
116impl SlidingWindow {
128 pub fn insert(&mut self, packet_number: PacketNumber) -> Result<(), SlidingWindowError> {
134 self.insert_with_evicted(packet_number).map(|_| ())
135 }
136
137 pub fn insert_with_evicted(
146 &mut self,
147 packet_number: PacketNumber,
148 ) -> Result<EvictedSet, SlidingWindowError> {
149 #[cfg(debug_assertions)]
150 let initial = self.clone();
151
152 let res = self.insert_with_evicted_inner(packet_number);
153
154 #[cfg(debug_assertions)]
155 self.check_insert_result(packet_number, initial, &res);
156
157 res
158 }
159
160 fn insert_with_evicted_inner(
161 &mut self,
162 packet_number: PacketNumber,
163 ) -> Result<EvictedSet, SlidingWindowError> {
164 match self.window_position(packet_number) {
165 WindowPosition::Left => Err(SlidingWindowError::TooOld),
166 WindowPosition::RightEdge => Err(SlidingWindowError::Duplicate),
167 WindowPosition::Right(delta) => {
168 let removed = if delta < WINDOW_WIDTH {
169 let removed_mask = if delta == 128 {
171 u128::MAX
172 } else {
173 !u128::MAX.wrapping_shr(delta as u32)
174 };
175 let removed = !self.window & removed_mask;
176 self.window = self.window.checked_shl(delta as u32).unwrap_or(0);
178 self.window |= 1 << (delta - 1);
180 removed
181 } else {
182 let removed = self.window;
184 self.window = 0;
185 !removed
188 };
189 if let Some(prev_right_edge) = self.right_edge.replace(packet_number) {
190 Ok(EvictedSet {
191 window: removed,
192 right_edge: prev_right_edge,
193 })
194 } else {
195 assert!(removed == 0);
197 Ok(EvictedSet::default())
198 }
199 }
200 WindowPosition::Within(delta) => {
201 let mask = 1 << (delta - 1); let duplicate = self.window & mask != 0;
203 self.window |= mask;
204 if duplicate {
205 Err(SlidingWindowError::Duplicate)
206 } else {
207 Ok(EvictedSet::default())
208 }
209 }
210 WindowPosition::Empty => {
211 self.right_edge = Some(packet_number);
212 Ok(EvictedSet::default())
213 }
214 }
215 }
216
217 #[cfg_attr(not(debug_assertions), allow(dead_code))]
218 fn check_insert_result(
219 &self,
220 packet_number: PacketNumber,
221 initial: Self,
222 res: &Result<EvictedSet, SlidingWindowError>,
223 ) {
224 let evicted = match res {
225 Ok(evicted) => evicted,
226 Err(_) => {
227 assert_eq!(self.window, initial.window);
229 assert_eq!(self.right_edge, initial.right_edge);
230 return;
231 }
232 };
233
234 {
235 for pn in evicted.clone() {
236 assert_eq!(initial.check(pn), Ok(()), "{pn:?}");
238 assert_eq!(self.check(pn), Err(SlidingWindowError::TooOld), "{pn:?}");
240 }
241 }
242
243 for pn in initial
244 .right_edge
245 .map_or(0, |e| e.as_u64())
246 .saturating_sub(WINDOW_WIDTH)
247 ..initial.right_edge.map_or(WINDOW_WIDTH, |e| e.as_u64())
248 {
249 let pn = PacketNumber::from_varint(VarInt::new(pn).unwrap(), packet_number.space());
250
251 match self.check(pn) {
252 Ok(()) => assert!(evicted.clone().all(|e| e != pn)),
254 Err(SlidingWindowError::TooOld) => {
255 if initial.check(pn).is_ok()
258 && initial.window_position(pn) != WindowPosition::Empty
261 {
262 assert!(
263 evicted.clone().any(|e| e == pn),
264 "{pn:?} expected in evicted after insert of {packet_number:?}"
265 );
266 }
267 }
268 Err(SlidingWindowError::Duplicate) => {
269 assert!(evicted.clone().all(|e| e != pn))
271 }
272 }
273 }
274 }
275
276 pub fn check(&self, packet_number: PacketNumber) -> Result<(), SlidingWindowError> {
279 match self.window_position(packet_number) {
280 WindowPosition::Left => Err(SlidingWindowError::TooOld),
281 WindowPosition::RightEdge => Err(SlidingWindowError::Duplicate),
282 WindowPosition::Right(_) | WindowPosition::Empty => Ok(()),
283 WindowPosition::Within(delta) => {
284 let mask = 1 << (delta - 1); if self.window & mask != 0 {
286 Err(SlidingWindowError::Duplicate)
287 } else {
288 Ok(())
289 }
290 }
291 }
292 }
293
294 fn window_position(&self, packet_number: PacketNumber) -> WindowPosition {
296 if let Some(right_edge) = self.right_edge {
297 match right_edge.checked_distance(packet_number) {
298 Some(0) => WindowPosition::RightEdge,
299 Some(delta) if delta >= WINDOW_WIDTH => WindowPosition::Left,
300 Some(delta) => WindowPosition::Within(delta),
301 None => WindowPosition::Right(
302 packet_number
303 .checked_distance(right_edge)
304 .expect("packet_number must be greater than right_edge"),
305 ),
306 }
307 } else {
308 WindowPosition::Empty
309 }
310 }
311}
312
313#[cfg(test)]
314mod test {
315 use super::*;
316 use crate::{packet::number::PacketNumberSpace, varint::VarInt};
317 use bolero::{check, generator::*};
318 use SlidingWindowError::*;
319
320 macro_rules! assert_window {
323 (
324 $window:expr, $to_insert:expr, $duplicate:expr, $expected_window:expr, $right_edge:expr
325 ) => {{
326 assert_eq!($window.check($to_insert), $duplicate);
327 assert_eq!($window.insert($to_insert), $duplicate);
328 assert_eq!(
329 $window.window, $expected_window,
330 "Expected: {:b}, Actual: {:b}",
331 $expected_window, $window.window
332 );
333 assert_eq!($window.right_edge.unwrap(), $right_edge);
334 }};
335 }
336
337 #[test]
338 #[allow(clippy::cognitive_complexity)] fn insert() {
340 let space = PacketNumberSpace::ApplicationData;
341 let mut window = SlidingWindow::default();
342
343 let zero = space.new_packet_number(VarInt::from_u8(0));
344 let one = space.new_packet_number(VarInt::from_u8(1));
345 let two = space.new_packet_number(VarInt::from_u8(2));
346 let three = space.new_packet_number(VarInt::from_u8(3));
347 let four = space.new_packet_number(VarInt::from_u8(4));
348 let five = space.new_packet_number(VarInt::from_u8(5));
349 let six = space.new_packet_number(VarInt::from_u8(6));
350 let seven = space.new_packet_number(VarInt::from_u8(7));
351 let eight = space.new_packet_number(VarInt::from_u8(8));
352 let large = space.new_packet_number(VarInt::MAX);
353
354 assert_eq!(window.window, Window::default());
355 assert_eq!(window.right_edge, None);
356
357 assert_window!(window, zero, Ok(()), Window::default(), zero);
358 assert_window!(window, zero, Err(Duplicate), Window::default(), zero);
359 assert_window!(window, one, Ok(()), 0b1, one);
360 assert_window!(window, one, Err(Duplicate), 0b1, one);
361 assert_window!(window, two, Ok(()), 0b11, two);
362 assert_window!(window, five, Ok(()), 0b11100, five);
363 assert_window!(window, eight, Ok(()), 0b1110_0100, eight);
364 assert_window!(window, seven, Ok(()), 0b1110_0101, eight);
365 assert_window!(window, three, Ok(()), 0b1111_0101, eight);
366 assert_window!(window, six, Ok(()), 0b1111_0111, eight);
367 assert_window!(window, four, Ok(()), 0b1111_1111, eight);
368 assert_window!(window, seven, Err(Duplicate), 0b1111_1111, eight);
369 assert_window!(window, two, Err(Duplicate), 0b1111_1111, eight);
370 assert_window!(window, eight, Err(Duplicate), 0b1111_1111, eight);
371 assert_window!(window, large, Ok(()), Window::default(), large);
372 assert_window!(window, five, Err(TooOld), Window::default(), large);
373 }
374
375 #[test]
376 #[cfg_attr(miri, ignore)] fn incremental_insert() {
378 let mut window = SlidingWindow::default();
379 let space = PacketNumberSpace::ApplicationData;
380 for right_edge in 0..1000 {
381 let pn = space.new_packet_number(VarInt::from_u32(right_edge));
382 assert_eq!(window.check(pn), Ok(()));
383 assert_eq!(window.insert(pn), Ok(()));
384 assert_eq!(window.right_edge.unwrap(), pn);
385 for dup in 0..=right_edge {
386 let expected_error = if right_edge - dup < WINDOW_WIDTH as u32 {
387 Err(Duplicate)
388 } else {
389 Err(TooOld)
390 };
391 let dup_pn = space.new_packet_number(VarInt::from_u32(dup));
392 assert_eq!(window.check(dup_pn), expected_error);
393 assert_eq!(window.insert(dup_pn), expected_error);
394 }
395 }
396 }
397
398 #[test]
399 #[allow(clippy::cognitive_complexity)] fn insert_at_edge() {
401 let mut window = SlidingWindow::default();
402 let space = PacketNumberSpace::ApplicationData;
403 let zero = space.new_packet_number(VarInt::from_u8(0));
404 let window_width_minus_1 = space.new_packet_number(VarInt::new(WINDOW_WIDTH - 1).unwrap());
405 let window_width = window_width_minus_1.next().unwrap();
406
407 assert_window!(window, zero, Ok(()), Window::default(), zero);
408 assert_window!(
409 window,
410 window_width_minus_1,
411 Ok(()),
412 (1_u128) << 127,
413 window_width_minus_1
414 );
415 assert_window!(
416 window,
417 window_width_minus_1,
418 Err(Duplicate),
419 (1_u128) << 127,
420 window_width_minus_1
421 );
422 assert_window!(window, window_width, Ok(()), 0b1, window_width);
423
424 window = SlidingWindow::default();
425 assert_window!(window, zero, Ok(()), Window::default(), zero);
426 assert_window!(
427 window,
428 window_width,
429 Ok(()),
430 Window::default(),
431 window_width
432 );
433 assert_window!(
434 window,
435 window_width,
436 Err(Duplicate),
437 Window::default(),
438 window_width
439 );
440 }
441
442 #[test]
443 fn delta_larger_than_32_bits() {
444 let mut window = SlidingWindow::default();
445 let space = PacketNumberSpace::ApplicationData;
446 let zero = space.new_packet_number(VarInt::from_u8(0));
447 let large = space.new_packet_number(VarInt::new((1 << 32) + 1).unwrap());
448 assert_eq!(window.check(zero), Ok(()));
449 assert_eq!(window.insert(zero), Ok(()));
450 assert_eq!(window.check(large), Ok(()));
451 assert_eq!(window.insert(large), Ok(()));
452 assert_eq!(window.check(large), Err(Duplicate));
453 assert_eq!(window.insert(large), Err(Duplicate));
454 assert_eq!(window.window, 0b0);
455 }
456
457 #[test]
461 fn insert_into_empty() {
462 let pn = VarInt::from_u32(256);
463 let mut window = SlidingWindow::default();
464 let space = PacketNumberSpace::ApplicationData;
465 let packet_number = space.new_packet_number(pn);
466 assert!(window.insert(packet_number).is_ok());
467 }
468
469 #[test]
470 #[cfg_attr(kani, kani::proof, kani::unwind(130), kani::solver(kissat))]
471 #[cfg_attr(miri, ignore)] fn insert_test() {
473 let gen = produce::<(VarInt, VarInt)>().filter_gen(|(a, b)| a != b);
475
476 check!()
477 .with_generator(gen)
478 .cloned()
479 .for_each(|(pn, other_pn)| {
480 let mut window = SlidingWindow::default();
481 let space = PacketNumberSpace::ApplicationData;
482 let packet_number = space.new_packet_number(pn);
483 let other_packet_number = space.new_packet_number(other_pn);
484 assert!(window.insert(packet_number).is_ok());
485 assert_eq!(Err(Duplicate), window.check(packet_number));
486 assert_ne!(Err(Duplicate), window.check(other_packet_number));
487 });
488 }
489
490 #[test]
495 #[allow(clippy::cognitive_complexity)] fn insert_evicted() {
497 let space = PacketNumberSpace::ApplicationData;
498 let mut window = SlidingWindow::default();
499
500 let zero = space.new_packet_number(VarInt::from_u8(0));
501 let one = space.new_packet_number(VarInt::from_u8(1));
502 let two = space.new_packet_number(VarInt::from_u8(2));
503 let three = space.new_packet_number(VarInt::from_u8(3));
504 let four = space.new_packet_number(VarInt::from_u8(4));
505 let five = space.new_packet_number(VarInt::from_u8(5));
506 let six = space.new_packet_number(VarInt::from_u8(6));
507 let seven = space.new_packet_number(VarInt::from_u8(7));
508 let eight = space.new_packet_number(VarInt::from_u8(8));
509 let large = space.new_packet_number(VarInt::MAX);
510
511 assert!(window.insert(zero).is_ok());
512 assert!(window.insert(two).is_ok());
513 assert!(window.insert(four).is_ok());
514 assert!(window.insert(five).is_ok());
515 assert!(window.insert(seven).is_ok());
516 assert!(window.insert(eight).is_ok());
517
518 let mut evicted = window.insert_with_evicted(large).unwrap();
519 assert_eq!(evicted.next(), Some(one));
520 assert_eq!(evicted.next(), Some(three));
521 assert_eq!(evicted.next(), Some(six));
522 assert_eq!(evicted.next(), None);
523 }
524}