use std::time::Instant;
use crate::constants;
pub(crate) trait Controller {
fn on_sent(&mut self, now: Instant, bytes: u64);
fn on_ack(&mut self, now: Instant, sent_time: Instant, bytes: u64, app_limited: bool);
fn on_congestion_event(
&mut self,
now: Instant,
sent_time: Instant,
is_persistent: bool,
lost_bytes: u64,
);
fn window(&self) -> u64;
}
#[derive(Debug, Clone)]
pub(crate) struct NewReno {
cwnd: u64,
ssthresh: u64,
recovery_start: Option<Instant>,
acked_accum: u64,
}
impl Default for NewReno {
fn default() -> Self {
Self::new()
}
}
impl NewReno {
pub(crate) fn new() -> Self {
Self {
cwnd: constants::INITIAL_WINDOW,
ssthresh: u64::MAX,
recovery_start: None,
acked_accum: 0,
}
}
pub(crate) fn reset(&mut self, now: Instant) {
self.cwnd = constants::INITIAL_WINDOW;
self.ssthresh = u64::MAX;
self.recovery_start = Some(now);
self.acked_accum = 0;
}
fn in_recovery(&self, sent_time: Instant) -> bool {
self.recovery_start.is_some_and(|start| sent_time <= start)
}
#[cfg(test)]
pub(crate) fn ssthresh(&self) -> u64 {
self.ssthresh
}
#[cfg(test)]
pub(crate) fn recovery_start(&self) -> Option<Instant> {
self.recovery_start
}
}
impl Controller for NewReno {
fn on_sent(&mut self, now: Instant, bytes: u64) {
let _ = (now, bytes);
}
fn on_ack(&mut self, now: Instant, sent_time: Instant, bytes: u64, app_limited: bool) {
let _ = now;
if app_limited {
return;
}
if self.in_recovery(sent_time) {
return;
}
if self.cwnd < self.ssthresh {
self.cwnd = self.cwnd.saturating_add(bytes);
return;
}
self.acked_accum = self.acked_accum.saturating_add(bytes);
while self.acked_accum >= self.cwnd {
self.acked_accum -= self.cwnd;
self.cwnd = self.cwnd.saturating_add(constants::MAX_DATAGRAM as u64);
}
}
fn on_congestion_event(
&mut self,
now: Instant,
sent_time: Instant,
is_persistent: bool,
lost_bytes: u64,
) {
let _ = lost_bytes;
if self.in_recovery(sent_time) {
return;
}
self.cwnd = (self.cwnd / 2).max(constants::MINIMUM_WINDOW);
self.ssthresh = self.cwnd;
self.recovery_start = Some(now);
if is_persistent {
self.cwnd = constants::MINIMUM_WINDOW;
}
}
fn window(&self) -> u64 {
self.cwnd
}
}