use crate::{packet::number::PacketNumber, time::Timestamp};
use core::time::Duration;
pub const K_PACKET_THRESHOLD: u64 = 3;
#[derive(Debug, PartialEq, Eq)]
pub enum Outcome {
NotLostYet { lost_time: Timestamp },
Lost,
}
pub fn detect(
time_threshold: Duration,
time_sent: Timestamp,
packet_number_threshold: u64,
packet_number: PacketNumber,
largest_acked_packet_number: PacketNumber,
now: Timestamp,
) -> Outcome {
debug_assert!(
largest_acked_packet_number > packet_number,
"only packets sent before the largest acknowledged packet may be considered lost"
);
let packet_lost_time = time_sent + time_threshold;
let time_threshold_exceeded = packet_lost_time.has_elapsed(now);
let packet_number_threshold_exceeded = largest_acked_packet_number
.checked_distance(packet_number)
.expect("largest_acked_packet_number > packet_number")
>= packet_number_threshold;
if time_threshold_exceeded || packet_number_threshold_exceeded {
return Outcome::Lost;
}
Outcome::NotLostYet {
lost_time: packet_lost_time,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{packet::number::PacketNumberSpace, time::testing::now};
#[allow(clippy::assertions_on_constants)]
#[test]
fn packet_reorder_threshold_at_least_three() {
assert!(K_PACKET_THRESHOLD >= 3);
}
#[test]
fn time_threshold() {
let time_threshold = Duration::from_secs(5);
let packet_number = new_packet_number(1);
let largest_acked_packet_number =
new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD - 1);
let time_sent = now();
let current_time = time_sent + time_threshold;
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(Outcome::Lost, outcome);
let time_sent = now();
let current_time = time_sent + time_threshold - Duration::from_secs(1);
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(
Outcome::NotLostYet {
lost_time: current_time + Duration::from_secs(1)
},
outcome
);
}
#[test]
fn packet_number_threshold() {
let time_threshold = Duration::from_secs(5);
let time_sent = now();
let current_time = time_sent + time_threshold - Duration::from_secs(1);
let packet_number = new_packet_number(1);
let largest_acked_packet_number =
new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD);
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(Outcome::Lost, outcome);
let largest_acked_packet_number =
new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD - 1);
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(
Outcome::NotLostYet {
lost_time: current_time + Duration::from_secs(1)
},
outcome
);
}
fn new_packet_number(packet_number: u64) -> PacketNumber {
PacketNumberSpace::ApplicationData.new_packet_number(packet_number.try_into().unwrap())
}
}