pub const WINDOW: u64 = 64;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct AckWindow {
newest: u64,
mask: u64,
started: bool,
}
impl AckWindow {
pub fn new() -> Self {
Self::default()
}
pub fn from_encoded(newest: u64, mask: u64) -> Self {
Self { newest, mask, started: true }
}
pub fn encode(&self) -> Option<(u64, u64)> {
self.started.then_some((self.newest, self.mask))
}
pub fn observe(&mut self, seq: u64) -> bool {
if !self.started {
self.started = true;
self.newest = seq;
self.mask = 0;
return true;
}
if seq > self.newest {
let shift = seq - self.newest;
let shifted = if shift >= 64 { 0 } else { self.mask << shift };
self.mask = if shift <= WINDOW { shifted | (1u64 << (shift - 1)) } else { 0 };
self.newest = seq;
true
} else if seq == self.newest {
false
} else {
let back = self.newest - seq;
if back > WINDOW {
return false;
}
let bit = 1u64 << (back - 1);
let was_set = self.mask & bit != 0;
self.mask |= bit;
!was_set
}
}
pub fn newest(&self) -> Option<u64> {
self.started.then_some(self.newest)
}
pub fn mask(&self) -> u64 {
self.mask
}
pub fn contains(&self, seq: u64) -> bool {
if !self.started || seq > self.newest {
return false;
}
if seq == self.newest {
return true;
}
let back = self.newest - seq;
back <= WINDOW && self.mask & (1u64 << (back - 1)) != 0
}
pub fn missing_since(&self, oldest: u64) -> impl Iterator<Item = u64> + '_ {
let floor = self.newest.saturating_sub(WINDOW).max(oldest);
let end = if self.started { self.newest } else { 0 };
(floor..end).filter(move |seq| self.started && !self.contains(*seq))
}
pub fn contiguous_base(&self, first: u64) -> Option<u64> {
if !self.contains(first) {
return None;
}
let mut base = first;
while base < self.newest && self.contains(base + 1) {
base += 1;
}
Some(base)
}
pub fn received_in_window(&self) -> u32 {
if !self.started {
return 0;
}
1 + self.mask.count_ones()
}
pub fn reset(&mut self) {
*self = Self::new();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_clean_run_records_everything() {
let mut w = AckWindow::new();
for seq in 0..40 {
assert!(w.observe(seq), "seq {seq} was new");
}
assert_eq!(w.newest(), Some(39));
for seq in 0..40 {
assert!(w.contains(seq), "seq {seq} should be recorded");
}
assert_eq!(w.missing_since(0).count(), 0);
}
#[test]
fn gaps_are_reported_and_nothing_else_is() {
let mut w = AckWindow::new();
for seq in [10u64, 11, 13, 14, 17] {
w.observe(seq);
}
assert_eq!(w.missing_since(10).collect::<Vec<_>>(), vec![12, 15, 16]);
assert!(!w.contains(12));
assert!(w.contains(13));
}
#[test]
fn a_straggler_lands_in_its_own_slot() {
let mut w = AckWindow::new();
w.observe(20);
w.observe(25);
assert!(w.observe(22), "the straggler is new");
assert!(!w.observe(22), "and only new once");
assert_eq!(w.newest(), Some(25));
assert!(w.contains(22));
assert_eq!(w.missing_since(20).collect::<Vec<_>>(), vec![21, 23, 24]);
}
#[test]
fn a_big_jump_forgets_the_old_window_rather_than_lying_about_it() {
let mut w = AckWindow::new();
for seq in 0..10 {
w.observe(seq);
}
w.observe(500);
assert_eq!(w.newest(), Some(500));
assert!(!w.contains(9));
assert_eq!(w.mask(), 0);
assert_eq!(w.missing_since(0).count(), WINDOW as usize);
}
#[test]
fn the_encoding_round_trips() {
let mut w = AckWindow::new();
for seq in [100u64, 101, 104, 107, 108] {
w.observe(seq);
}
let (newest, mask) = w.encode().unwrap();
let peer = AckWindow::from_encoded(newest, mask);
assert_eq!(peer, w);
assert_eq!(peer.missing_since(100).collect::<Vec<_>>(), w.missing_since(100).collect::<Vec<_>>());
}
#[test]
fn an_empty_window_encodes_to_nothing() {
let w = AckWindow::new();
assert_eq!(w.encode(), None);
assert_eq!(w.newest(), None);
assert!(!w.contains(0));
assert_eq!(w.missing_since(0).count(), 0);
}
#[test]
fn the_cost_does_not_grow_with_the_loss_rate() {
let mut clean = AckWindow::new();
let mut awful = AckWindow::new();
for seq in 0..64u64 {
clean.observe(seq);
if seq % 5 == 0 {
awful.observe(seq);
}
}
assert!(clean.encode().is_some() && awful.encode().is_some());
assert_eq!(clean.received_in_window(), 64);
assert_eq!(awful.received_in_window(), 13);
assert_eq!(core::mem::size_of_val(&clean.encode().unwrap()), core::mem::size_of_val(&awful.encode().unwrap()));
}
#[test]
fn the_contiguous_base_stops_at_the_first_gap_not_the_newest_bit() {
let mut w = AckWindow::new();
for seq in [1u64, 2, 3, 5, 6, 7] {
w.observe(seq);
}
assert_eq!(w.newest(), Some(7));
assert_eq!(w.contiguous_base(1), Some(3), "took a state the peer never reached");
}
#[test]
fn a_run_that_reaches_the_newest_reports_it() {
let mut w = AckWindow::new();
for seq in 1..=10u64 {
w.observe(seq);
}
assert_eq!(w.contiguous_base(1), Some(10));
assert_eq!(w.contiguous_base(10), Some(10), "a run of one is still a run");
assert_eq!(w.contiguous_base(11), None, "nothing beyond the newest has arrived");
}
#[test]
fn a_protocol_numbering_from_zero_is_representable() {
let mut w = AckWindow::new();
for seq in [0u64, 1, 2] {
w.observe(seq);
}
assert_eq!(w.contiguous_base(0), Some(2), "the very first packet is part of the run");
}
#[test]
fn a_filled_gap_lets_the_frontier_jump_past_it() {
let mut w = AckWindow::new();
for seq in [1u64, 2, 4, 5, 6] {
w.observe(seq);
}
assert_eq!(w.contiguous_base(1), Some(2));
w.observe(3);
assert_eq!(w.contiguous_base(1), Some(6), "the whole run unblocked at once");
}
#[test]
fn a_base_older_than_the_window_is_reported_as_unknowable() {
let mut w = AckWindow::new();
w.observe(5);
w.observe(1000);
assert_eq!(w.contiguous_base(6), None, "the window cannot speak about 6..936");
assert_eq!(w.contiguous_base(1000), Some(1000));
}
#[test]
fn an_empty_window_has_no_run_at_all() {
let w = AckWindow::new();
assert_eq!(w.contiguous_base(0), None);
assert_eq!(w.contiguous_base(7), None);
}
#[test]
fn the_frontier_costs_at_most_a_window_regardless_of_the_gap() {
let mut w = AckWindow::new();
for seq in 0..=WINDOW {
w.observe(seq);
}
assert_eq!(w.contiguous_base(0), Some(WINDOW));
assert_eq!(w.contiguous_base(WINDOW - 1), Some(WINDOW));
assert_eq!(w.contiguous_base(WINDOW), Some(WINDOW));
}
#[test]
fn shifting_exactly_the_window_width_is_not_off_by_one() {
let mut w = AckWindow::new();
w.observe(0);
w.observe(WINDOW);
assert!(w.contains(0), "the oldest slot is still inside the window");
w.observe(WINDOW + 1);
assert!(!w.contains(0), "and one more step pushes it out");
}
}