use rand::Rng;
use crate::caps::{PEX_ARRIVAL_GRACE, PEX_MAX_INTERVAL, PEX_MIN_INTERVAL};
#[must_use]
pub fn clamp_interval(secs: u32) -> u32 {
secs.clamp(PEX_MIN_INTERVAL, PEX_MAX_INTERVAL)
}
#[must_use]
pub fn effective_interval_secs(own_declared: u32, remote_declared: Option<u32>) -> u32 {
let own = clamp_interval(own_declared);
let remote = remote_declared.map_or(0, clamp_interval);
own.max(remote).max(PEX_MIN_INTERVAL)
}
#[must_use]
pub fn arrival_floor_ms(remote_declared: u32) -> u64 {
let declared = clamp_interval(remote_declared).max(PEX_MIN_INTERVAL);
u64::from(declared - PEX_ARRIVAL_GRACE) * 1000
}
#[must_use]
pub fn jitter_ms(effective_secs: u32) -> u64 {
let span = u64::from(effective_secs) * 100;
if span == 0 {
0
} else {
rand::thread_rng().gen_range(0..=span)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clamps_into_range() {
assert_eq!(clamp_interval(0), PEX_MIN_INTERVAL);
assert_eq!(clamp_interval(29), 30);
assert_eq!(clamp_interval(60), 60);
assert_eq!(clamp_interval(4000), PEX_MAX_INTERVAL);
}
#[test]
fn effective_is_max_of_own_remote_and_floor() {
assert_eq!(effective_interval_secs(60, None), 60);
assert_eq!(effective_interval_secs(10, None), 30); assert_eq!(effective_interval_secs(60, Some(120)), 120);
assert_eq!(effective_interval_secs(120, Some(45)), 120);
}
#[test]
fn arrival_floor_is_declared_minus_grace() {
assert_eq!(arrival_floor_ms(60), 55_000);
assert_eq!(arrival_floor_ms(30), 25_000);
assert_eq!(arrival_floor_ms(1), 25_000);
}
#[test]
fn jitter_is_additive_and_bounded() {
for _ in 0..1000 {
let j = jitter_ms(60);
assert!(
j <= 6000,
"jitter must be <= 10% of 60 s = 6000 ms, got {j}"
);
}
}
}