use crate::packet::SEQ_NUMBER_MASK;
const SEQ_MOD: i64 = (SEQ_NUMBER_MASK as i64) + 1;
const SEQ_HALF: i64 = SEQ_MOD / 2;
pub fn seq_add(seq: u32, n: u32) -> u32 {
(((seq as u64) + (n as u64)) % (SEQ_MOD as u64)) as u32
}
pub fn seq_next(seq: u32) -> u32 {
seq_add(seq, 1)
}
pub fn seq_diff(a: u32, b: u32) -> i32 {
let a = i64::from(a & SEQ_NUMBER_MASK);
let b = i64::from(b & SEQ_NUMBER_MASK);
let raw = (a - b).rem_euclid(SEQ_MOD);
if raw > SEQ_HALF {
(raw - SEQ_MOD) as i32
} else {
raw as i32
}
}
pub fn seq_lt(a: u32, b: u32) -> bool {
seq_diff(a, b) < 0
}
pub fn seq_leq(a: u32, b: u32) -> bool {
seq_diff(a, b) <= 0
}
pub fn seq_gt(a: u32, b: u32) -> bool {
seq_diff(a, b) > 0
}
pub fn seq_geq(a: u32, b: u32) -> bool {
seq_diff(a, b) >= 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wrap_boundary_increment() {
assert_eq!(seq_next(SEQ_NUMBER_MASK), 0);
assert_eq!(seq_add(SEQ_NUMBER_MASK, 1), 0);
assert_eq!(seq_add(SEQ_NUMBER_MASK, 5), 4);
assert_eq!(seq_add(0, SEQ_NUMBER_MASK), SEQ_NUMBER_MASK);
}
#[test]
fn wrap_boundary_ordering() {
assert!(seq_lt(SEQ_NUMBER_MASK, 0));
assert!(seq_gt(0, SEQ_NUMBER_MASK));
assert_eq!(seq_diff(0, SEQ_NUMBER_MASK), 1);
assert_eq!(seq_diff(SEQ_NUMBER_MASK, 0), -1);
}
#[test]
fn ordinary_ordering_without_wrap() {
assert!(seq_lt(10, 20));
assert!(seq_leq(10, 10));
assert!(seq_leq(10, 20));
assert!(seq_gt(20, 10));
assert!(seq_geq(20, 20));
assert!(seq_geq(20, 10));
assert!(!seq_lt(20, 10));
assert!(!seq_gt(10, 20));
}
#[test]
fn diff_is_antisymmetric() {
for (a, b) in [(0u32, 0u32), (5, 100), (SEQ_NUMBER_MASK, 3), (12345, 12340)] {
assert_eq!(seq_diff(a, b), -seq_diff(b, a));
}
}
#[test]
fn walking_forward_past_the_wrap_stays_consistent() {
let start = SEQ_NUMBER_MASK - 4;
let mut prev = start;
for _ in 0..10 {
let next = seq_next(prev);
assert!(seq_lt(prev, next), "prev={prev:#x} next={next:#x}");
assert_eq!(seq_diff(next, prev), 1);
prev = next;
}
}
}