#[derive(Debug, Default, Clone)]
pub(crate) struct SequenceExtender {
highest: Option<u64>,
}
impl SequenceExtender {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn extend(&mut self, sequence_number: u16) -> u64 {
let Some(highest) = self.highest else {
let extended = u64::from(sequence_number);
self.highest = Some(extended);
return extended;
};
let previous = (highest & 0xFFFF) as u16;
let distance = i32::from(sequence_number.wrapping_sub(previous) as i16);
let extended = (highest as i64 + i64::from(distance)).max(0) as u64;
if extended > highest {
self.highest = Some(extended);
}
extended
}
pub(crate) fn highest(&self) -> Option<u64> {
self.highest
}
}
#[derive(Debug, Default, Clone)]
pub(crate) struct TimestampExtender {
highest: Option<u64>,
}
impl TimestampExtender {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn extend(&mut self, timestamp: u32) -> u64 {
let Some(highest) = self.highest else {
let extended = u64::from(timestamp);
self.highest = Some(extended);
return extended;
};
let previous = (highest & 0xFFFF_FFFF) as u32;
let distance = i64::from(timestamp.wrapping_sub(previous) as i32);
let extended = (highest as i64 + distance).max(0) as u64;
if extended > highest {
self.highest = Some(extended);
}
extended
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_first_packet_establishes_the_origin() {
let mut extender = SequenceExtender::new();
assert_eq!(1000, extender.extend(1000));
assert_eq!(Some(1000), extender.highest());
}
#[test]
fn consecutive_packets_increase_by_one() {
let mut extender = SequenceExtender::new();
let extended: Vec<u64> = (10..15).map(|n| extender.extend(n)).collect();
assert_eq!(vec![10, 11, 12, 13, 14], extended);
}
#[test]
fn wrapping_past_65535_keeps_increasing() {
let mut extender = SequenceExtender::new();
assert_eq!(65534, extender.extend(65534));
assert_eq!(65535, extender.extend(65535));
assert_eq!(65536, extender.extend(0), "0 follows 65535");
assert_eq!(65537, extender.extend(1));
}
#[test]
fn several_wraps_keep_increasing() {
let mut extender = SequenceExtender::new();
let mut expected = 0u64;
let mut sequence_number = 0u16;
for _ in 0..(3 * 65536 + 100) {
assert_eq!(expected, extender.extend(sequence_number));
expected += 1;
sequence_number = sequence_number.wrapping_add(1);
}
assert!(expected > 3 * 65536, "covered three wrap-arounds");
}
#[test]
fn a_reordered_packet_extends_backwards() {
let mut extender = SequenceExtender::new();
extender.extend(100);
extender.extend(103);
assert_eq!(
101,
extender.extend(101),
"the straggler belongs before 103"
);
assert_eq!(102, extender.extend(102));
assert_eq!(
Some(103),
extender.highest(),
"a late packet does not lower the anchor"
);
}
#[test]
fn a_packet_reordered_across_a_wrap_lands_in_the_previous_cycle() {
let mut extender = SequenceExtender::new();
extender.extend(65534);
extender.extend(65535);
assert_eq!(65536, extender.extend(0));
assert_eq!(
65535 - 2,
extender.extend(65533),
"a straggler from before the wrap sorts before it, not a cycle later"
);
assert_eq!(Some(65536), extender.highest());
}
#[test]
fn reordering_below_the_origin_clamps_at_zero() {
let mut extender = SequenceExtender::new();
extender.extend(2);
assert_eq!(1, extender.extend(1));
assert_eq!(0, extender.extend(0));
assert_eq!(0, extender.extend(65535), "clamped rather than wrapped");
}
#[test]
fn a_jump_of_more_than_half_the_space_is_read_as_reordering() {
let mut extender = SequenceExtender::new();
extender.extend(0);
assert_eq!(
0u64.wrapping_sub(0),
extender.extend(0),
"sanity: the same number is the same value"
);
assert_eq!(0, extender.extend(40000));
}
}