use std::time::Instant;
use crate::constants;
use super::frame::Ack;
use super::session::ReplayWindow;
#[derive(Debug, Clone, Default)]
pub(crate) struct AckState {
since_ack: u64,
largest_at: Option<Instant>,
largest_frame_seen: bool,
owed: bool,
pending: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AckAction {
None,
Now,
Arm(Instant),
}
impl AckState {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn on_recv(
&mut self,
now: Instant,
counter: u64,
prev_greatest: Option<u64>,
ack_eliciting: bool,
frame_seen: bool,
) -> AckAction {
if prev_greatest.is_none_or(|greatest| counter > greatest) {
self.largest_at = Some(now);
self.largest_frame_seen = frame_seen;
}
if !ack_eliciting {
return AckAction::None;
}
self.since_ack += 1;
let in_order =
prev_greatest.is_some_and(|greatest| greatest.checked_add(1) == Some(counter));
if !in_order {
self.owed = true;
return AckAction::Now;
}
if self.since_ack >= constants::ACK_ELICITING_PER_ACK {
if self.since_ack >= constants::ACK_COALESCE_MAX {
self.owed = true;
return AckAction::Now;
}
self.pending = true;
return AckAction::Arm(now);
}
AckAction::Arm(now + constants::MAX_ACK_DELAY)
}
pub(crate) fn is_owed(&self) -> bool {
self.owed
}
pub(crate) fn is_ready(&self) -> bool {
self.owed || self.pending
}
pub(crate) fn on_delay_expired(&mut self) {
self.owed = true;
}
pub(crate) fn on_ack_packed(&mut self) {
self.owed = false;
self.pending = false;
self.since_ack = 0;
}
pub(crate) fn ack_delay_us(&self, now: Instant) -> u64 {
if !self.largest_frame_seen {
return 0;
}
let Some(at) = self.largest_at else {
return 0;
};
u64::try_from(now.saturating_duration_since(at).as_micros()).unwrap_or(u64::MAX)
}
}
pub(crate) fn derive(window: &ReplayWindow, ack_delay_us: u64, room: usize) -> Option<Ack> {
let mut blocks = window.ranges_desc();
let first = blocks.next()?;
let largest = *first.end();
debug_assert_eq!(
Some(largest),
window.greatest(),
"§12.2: the newest block's top is the window's greatest"
);
let mut ack = Ack {
largest,
ack_delay: ack_delay_us,
first_range: largest - *first.start(),
ranges: Vec::new(),
};
if ack.encoded_len() > room {
return None;
}
let mut smallest = *first.start();
for block in blocks {
if ack.ranges.len() == constants::MAX_ACK_RANGES {
break;
}
let block_largest = *block.end();
let gap = smallest - block_largest - 2;
let range = block_largest - *block.start();
ack.ranges.push((gap, range));
if ack.encoded_len() > room {
ack.ranges.pop();
break;
}
smallest = *block.start();
}
debug_assert!(ack.encoded_len() <= room);
debug_assert!(ack.ranges.len() <= constants::MAX_ACK_RANGES);
Some(ack)
}