use std::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Seq(u32);
impl Seq {
pub const MAX: u32 = 0x7FFF_FFFF;
pub const fn new(v: u32) -> Self {
Self(v & Self::MAX)
}
pub const fn get(self) -> u32 {
self.0
}
pub const fn add(self, n: u32) -> Self {
Self::new(self.0.wrapping_add(n))
}
pub const fn next(self) -> Self {
self.add(1)
}
pub const fn prev(self) -> Self {
Self::new(self.0.wrapping_sub(1))
}
pub const fn distance_to(self, other: Seq) -> i32 {
(other.0.wrapping_sub(self.0) << 1) as i32 >> 1
}
}
impl PartialOrd for Seq {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Seq {
fn cmp(&self, other: &Self) -> Ordering {
0.cmp(&self.distance_to(*other))
}
}
impl std::fmt::Display for Seq {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct TimestampExtender {
latest: Option<(u32, i64)>,
}
impl TimestampExtender {
pub fn new() -> Self {
Self::default()
}
pub fn starting_at(ts: u32) -> Self {
Self {
latest: Some((ts, i64::from(ts))),
}
}
pub fn extend(&mut self, ts: u32) -> i64 {
match self.latest {
None => {
self.latest = Some((ts, i64::from(ts)));
i64::from(ts)
}
Some((raw, ext)) => {
let diff = i64::from(ts.wrapping_sub(raw) as i32);
let v = ext.saturating_add(diff);
if diff > 0 {
self.latest = Some((ts, v));
}
v
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sequence_numbers_wrap_at_31_bits() {
let top = Seq::new(Seq::MAX);
assert_eq!(top.next(), Seq::new(0));
assert_eq!(Seq::new(0).prev(), top);
assert_eq!(
Seq::new(0x8000_0005),
Seq::new(5),
"the top bit is not part of it"
);
assert_eq!(top.distance_to(Seq::new(2)), 3);
assert_eq!(Seq::new(2).distance_to(top), -3);
assert!(top < Seq::new(2));
assert!(Seq::new(2) > top);
assert_eq!(top.add(10), Seq::new(9));
}
#[test]
fn distance_is_signed_half_the_space() {
let a = Seq::new(100);
assert_eq!(a.distance_to(a.add(0x3FFF_FFFF)), 0x3FFF_FFFF);
assert_eq!(a.distance_to(a.add(0x4000_0000)), -0x4000_0000);
assert_eq!(a.distance_to(a), 0);
assert_eq!(a.cmp(&a), Ordering::Equal);
}
#[test]
fn timestamps_extend_across_the_wrap() {
let mut x = TimestampExtender::starting_at(u32::MAX - 10);
assert_eq!(x.extend(u32::MAX - 5), i64::from(u32::MAX) - 5);
assert_eq!(x.extend(5), (1i64 << 32) + 5);
assert_eq!(x.extend(u32::MAX - 3), i64::from(u32::MAX) - 3);
assert_eq!(x.extend(6), (1i64 << 32) + 6);
}
#[test]
fn timestamps_extend_backwards_from_the_start() {
let mut x = TimestampExtender::starting_at(3);
assert_eq!(x.extend(u32::MAX), -1, "just before the handshake");
assert_eq!(x.extend(1_000_000), 1_000_000);
}
}