use std::time::{Duration, Instant};
pub const LEDBAT_TARGET_DELAY: Duration = Duration::from_millis(100);
pub const LEDBAT_MIN_CWND: u32 = 2;
pub const LEDBAT_MAX_CWND: u32 = 1000;
pub const GAIN: f64 = 0.5;
const DEFAULT_MSS: u32 = 1500;
#[derive(Debug, Clone)]
pub struct LedbatController {
cwnd: u32,
bytes_in_flight: u32,
base_delay: Option<u64>,
current_delay: u64,
target_delay_us: u64,
last_send_time: Option<Instant>,
slow_start: bool,
mss: u32,
delay_history: Vec<u64>,
max_history: usize,
slow_start_acks: u32,
}
impl Default for LedbatController {
fn default() -> Self {
Self::new()
}
}
impl LedbatController {
pub fn new() -> Self {
Self {
cwnd: LEDBAT_MIN_CWND * DEFAULT_MSS,
bytes_in_flight: 0,
base_delay: None,
current_delay: 0,
target_delay_us: LEDBAT_TARGET_DELAY.as_micros() as u64,
last_send_time: None,
slow_start: true,
mss: DEFAULT_MSS,
delay_history: Vec::with_capacity(60),
max_history: 60, slow_start_acks: 0,
}
}
pub fn with_mss(mss: u32) -> Self {
Self {
cwnd: LEDBAT_MIN_CWND * mss,
mss,
..Self::new()
}
}
pub fn with_target_delay(target_delay: Duration) -> Self {
Self {
target_delay_us: target_delay.as_micros() as u64,
..Self::new()
}
}
pub fn get_window_size(&self) -> u32 {
self.cwnd
}
pub fn get_window_packets(&self) -> u32 {
self.cwnd / self.mss
}
pub fn get_bytes_in_flight(&self) -> u32 {
self.bytes_in_flight
}
pub fn get_base_delay(&self) -> Option<Duration> {
self.base_delay.map(Duration::from_micros)
}
pub fn get_current_delay(&self) -> Duration {
Duration::from_micros(self.current_delay)
}
pub fn get_queuing_delay(&self) -> Duration {
let queuing_us = self
.current_delay
.saturating_sub(self.base_delay.unwrap_or(0));
Duration::from_micros(queuing_us)
}
pub fn can_send(&self) -> bool {
self.bytes_in_flight < self.cwnd
}
pub fn available_window(&self) -> u32 {
self.cwnd.saturating_sub(self.bytes_in_flight)
}
pub fn on_data_sent(&mut self, bytes: u32) {
self.bytes_in_flight = self.bytes_in_flight.saturating_add(bytes);
self.last_send_time = Some(Instant::now());
}
pub fn on_ack_received(&mut self, timestamp_diff: u64, bytes_acked: u32) {
self.update_delay(timestamp_diff);
self.bytes_in_flight = self.bytes_in_flight.saturating_sub(bytes_acked);
let has_enough_samples = self.delay_history.len() >= 3;
let queuing_delay_us = if has_enough_samples {
self.current_delay
.saturating_sub(self.base_delay.unwrap_or(0))
} else {
0
};
if self.slow_start {
if (has_enough_samples && queuing_delay_us > self.target_delay_us)
|| self.slow_start_acks >= 10
{
self.slow_start = false;
if has_enough_samples {
self.update_cwnd_congestion_avoidance(bytes_acked, queuing_delay_us);
}
} else {
self.slow_start_acks += 1;
self.cwnd = self.cwnd.saturating_add(bytes_acked);
}
} else {
if has_enough_samples {
self.update_cwnd_congestion_avoidance(bytes_acked, queuing_delay_us);
}
}
self.clamp_cwnd();
}
fn update_cwnd_congestion_avoidance(&mut self, bytes_acked: u32, queuing_delay_us: u64) {
let target = self.target_delay_us as f64;
let queuing = queuing_delay_us as f64;
let delay_factor = (target - queuing) / target;
let delta = GAIN * delay_factor * bytes_acked as f64;
if delta >= 0.0 {
self.cwnd = self.cwnd.saturating_add(delta as u32);
} else {
self.cwnd = self.cwnd.saturating_sub((-delta) as u32);
}
}
pub fn update_delay(&mut self, delay_us: u64) {
self.current_delay = delay_us;
self.delay_history.push(delay_us);
if self.delay_history.len() > self.max_history {
self.delay_history.remove(0);
}
self.base_delay = self.delay_history.iter().min().copied();
}
pub fn on_timeout(&mut self) {
self.cwnd = LEDBAT_MIN_CWND * self.mss;
self.bytes_in_flight = 0;
self.slow_start = true;
self.slow_start_acks = 0;
}
pub fn on_loss(&mut self) {
self.cwnd = (self.cwnd / 2).max(LEDBAT_MIN_CWND * self.mss);
self.slow_start = false;
}
pub fn reset(&mut self) {
self.cwnd = LEDBAT_MIN_CWND * self.mss;
self.bytes_in_flight = 0;
self.base_delay = None;
self.current_delay = 0;
self.last_send_time = None;
self.slow_start = true;
self.delay_history.clear();
self.slow_start_acks = 0;
}
fn clamp_cwnd(&mut self) {
let min_cwnd = LEDBAT_MIN_CWND * self.mss;
let max_cwnd = LEDBAT_MAX_CWND * self.mss;
self.cwnd = self.cwnd.clamp(min_cwnd, max_cwnd);
}
pub fn is_slow_start(&self) -> bool {
self.slow_start
}
pub fn get_target_delay(&self) -> Duration {
Duration::from_micros(self.target_delay_us)
}
pub fn time_since_last_send(&self) -> Option<Duration> {
self.last_send_time.map(|t| t.elapsed())
}
pub fn get_mss(&self) -> u32 {
self.mss
}
pub fn has_delay_samples(&self) -> bool {
!self.delay_history.is_empty()
}
pub fn delay_sample_count(&self) -> usize {
self.delay_history.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_controller_initialization() {
let controller = LedbatController::new();
assert_eq!(controller.get_window_size(), LEDBAT_MIN_CWND * DEFAULT_MSS);
assert_eq!(controller.get_bytes_in_flight(), 0);
assert!(controller.can_send());
assert!(controller.is_slow_start());
assert_eq!(controller.get_target_delay(), LEDBAT_TARGET_DELAY);
}
#[test]
fn test_controller_with_custom_mss() {
let controller = LedbatController::with_mss(1400);
assert_eq!(controller.get_mss(), 1400);
assert_eq!(controller.get_window_size(), LEDBAT_MIN_CWND * 1400);
}
#[test]
fn test_controller_with_custom_target_delay() {
let controller = LedbatController::with_target_delay(Duration::from_millis(200));
assert_eq!(controller.get_target_delay(), Duration::from_millis(200));
}
#[test]
fn test_on_data_sent() {
let mut controller = LedbatController::new();
controller.on_data_sent(1500);
assert_eq!(controller.get_bytes_in_flight(), 1500);
assert!(controller.time_since_last_send().is_some());
controller.on_data_sent(500);
assert_eq!(controller.get_bytes_in_flight(), 2000);
}
#[test]
fn test_can_send() {
let mut controller = LedbatController::new();
assert!(controller.can_send());
let window = controller.get_window_size();
controller.on_data_sent(window);
assert!(!controller.can_send());
controller.on_ack_received(50_000, 1500);
assert!(controller.can_send());
}
#[test]
fn test_available_window() {
let mut controller = LedbatController::new();
let initial_window = controller.available_window();
assert_eq!(initial_window, controller.get_window_size());
controller.on_data_sent(1500);
let available = controller.available_window();
assert_eq!(available, controller.get_window_size() - 1500);
}
#[test]
fn test_slow_start_increase() {
let mut controller = LedbatController::new();
let initial_cwnd = controller.get_window_size();
controller.on_ack_received(50_000, 1500);
assert!(controller.get_window_size() > initial_cwnd);
assert!(controller.is_slow_start());
}
#[test]
fn test_slow_start_exit_on_target_delay() {
let mut controller = LedbatController::new();
controller.on_data_sent(controller.get_window_size());
controller.on_ack_received(50_000, 1500); controller.on_ack_received(45_000, 1500); controller.on_ack_received(48_000, 1500);
assert_eq!(
controller.get_base_delay(),
Some(Duration::from_micros(45_000))
);
let high_delay = LEDBAT_TARGET_DELAY.as_micros() as u64 + 50_000; controller.on_ack_received(high_delay, 1500);
assert!(!controller.is_slow_start());
}
#[test]
fn test_slow_start_exit_on_ack_count() {
let mut controller = LedbatController::new();
for _ in 0..15 {
controller.on_data_sent(1500);
controller.on_ack_received(50_000, 1500);
}
assert!(!controller.is_slow_start());
}
#[test]
fn test_congestion_avoidance_below_target() {
let mut controller = LedbatController::new();
controller.slow_start = false;
let initial_cwnd = controller.get_window_size();
let low_delay = 50_000; controller.base_delay = Some(20_000); controller.current_delay = low_delay;
controller.on_ack_received(low_delay, 1500);
assert!(controller.get_window_size() >= initial_cwnd);
}
#[test]
fn test_congestion_avoidance_above_target() {
let mut controller = LedbatController::new();
controller.slow_start = false;
controller.on_ack_received(20_000, 1500); controller.on_ack_received(25_000, 1500); controller.on_ack_received(22_000, 1500);
assert_eq!(
controller.get_base_delay(),
Some(Duration::from_micros(20_000))
);
let cwnd_before_high_delay = controller.get_window_size();
let high_delay = 150_000; controller.on_ack_received(high_delay, 1500);
assert!(controller.get_window_size() < cwnd_before_high_delay);
controller.on_ack_received(high_delay, 1500);
controller.on_ack_received(high_delay, 1500);
assert!(controller.get_window_size() < cwnd_before_high_delay - 500);
}
#[test]
fn test_on_timeout() {
let mut controller = LedbatController::new();
controller.on_data_sent(1500);
controller.on_ack_received(50_000, 1500);
controller.on_data_sent(1500);
controller.on_ack_received(50_000, 1500);
let cwnd_before = controller.get_window_size();
assert!(cwnd_before > LEDBAT_MIN_CWND * DEFAULT_MSS);
controller.on_timeout();
assert_eq!(controller.get_window_size(), LEDBAT_MIN_CWND * DEFAULT_MSS);
assert_eq!(controller.get_bytes_in_flight(), 0);
assert!(controller.is_slow_start());
}
#[test]
fn test_on_loss() {
let mut controller = LedbatController::new();
controller.on_data_sent(1500);
controller.on_ack_received(50_000, 1500);
controller.on_data_sent(1500);
controller.on_ack_received(50_000, 1500);
let cwnd_before = controller.get_window_size();
controller.on_loss();
assert!(controller.get_window_size() <= cwnd_before / 2);
assert!(!controller.is_slow_start());
}
#[test]
fn test_reset() {
let mut controller = LedbatController::new();
controller.on_data_sent(1500);
controller.on_ack_received(50_000, 1500);
controller.on_data_sent(1500);
controller.slow_start = false;
controller.reset();
assert_eq!(controller.get_window_size(), LEDBAT_MIN_CWND * DEFAULT_MSS);
assert_eq!(controller.get_bytes_in_flight(), 0);
assert!(controller.is_slow_start());
assert!(controller.get_base_delay().is_none());
assert_eq!(controller.get_current_delay(), Duration::ZERO);
}
#[test]
fn test_delay_measurements() {
let mut controller = LedbatController::new();
controller.on_ack_received(50_000, 1500);
assert_eq!(
controller.get_base_delay(),
Some(Duration::from_micros(50_000))
);
assert_eq!(
controller.get_current_delay(),
Duration::from_micros(50_000)
);
controller.on_ack_received(80_000, 1500);
assert_eq!(
controller.get_base_delay(),
Some(Duration::from_micros(50_000))
); assert_eq!(
controller.get_current_delay(),
Duration::from_micros(80_000)
);
controller.on_ack_received(30_000, 1500);
assert_eq!(
controller.get_base_delay(),
Some(Duration::from_micros(30_000))
); assert_eq!(
controller.get_current_delay(),
Duration::from_micros(30_000)
);
}
#[test]
fn test_queuing_delay() {
let mut controller = LedbatController::new();
controller.base_delay = Some(50_000); controller.current_delay = 150_000;
let queuing = controller.get_queuing_delay();
assert_eq!(queuing, Duration::from_micros(100_000)); }
#[test]
fn test_window_bounds() {
let mut controller = LedbatController::new();
controller.cwnd = LEDBAT_MAX_CWND * DEFAULT_MSS + 10000;
controller.on_ack_received(50_000, 1500);
assert_eq!(controller.get_window_size(), LEDBAT_MAX_CWND * DEFAULT_MSS);
}
#[test]
fn test_minimum_window() {
let mut controller = LedbatController::new();
controller.cwnd = 100; controller.on_ack_received(200_000, 1500);
assert!(controller.get_window_size() >= LEDBAT_MIN_CWND * DEFAULT_MSS);
}
#[test]
fn test_bytes_in_flight_tracking() {
let mut controller = LedbatController::new();
controller.on_data_sent(1500);
assert_eq!(controller.get_bytes_in_flight(), 1500);
controller.on_data_sent(1500);
assert_eq!(controller.get_bytes_in_flight(), 3000);
controller.on_ack_received(50_000, 1500);
assert_eq!(controller.get_bytes_in_flight(), 1500);
controller.on_ack_received(50_000, 1500);
assert_eq!(controller.get_bytes_in_flight(), 0);
}
#[test]
fn test_delay_sample_count() {
let controller = LedbatController::new();
assert_eq!(controller.delay_sample_count(), 0);
assert!(!controller.has_delay_samples());
let mut controller = controller;
controller.on_ack_received(50_000, 1500);
assert_eq!(controller.delay_sample_count(), 1);
assert!(controller.has_delay_samples());
}
#[test]
fn test_rfc6817_scenario() {
let mut controller = LedbatController::new();
for i in 0..3 {
controller.on_data_sent(1500);
controller.on_ack_received(50_000 - i * 1000, 1500); println!(
"Iteration {}: cwnd = {}, slow_start = {}, base_delay = {:?}",
i,
controller.get_window_size(),
controller.is_slow_start(),
controller.get_base_delay()
);
}
assert!(controller.get_window_size() > LEDBAT_MIN_CWND * DEFAULT_MSS);
assert!(controller.get_base_delay().is_some());
for i in 0..3 {
controller.on_data_sent(1500);
controller.on_ack_received(150_000, 1500); println!(
"Congestion {}: cwnd = {}, slow_start = {}, queuing_delay = {:?}",
i,
controller.get_window_size(),
controller.is_slow_start(),
controller.get_queuing_delay()
);
}
assert!(!controller.is_slow_start());
}
#[test]
fn test_gain_factor() {
let mut controller = LedbatController::new();
controller.slow_start = false;
controller.cwnd = 10000;
controller.on_ack_received(20_000, 1500); controller.on_ack_received(25_000, 1500); controller.on_ack_received(22_000, 1500);
assert_eq!(
controller.get_base_delay(),
Some(Duration::from_micros(20_000))
);
let initial_cwnd = controller.get_window_size();
controller.on_ack_received(70_000, 1500);
let new_cwnd = controller.get_window_size();
assert!(new_cwnd > initial_cwnd);
assert!(new_cwnd < initial_cwnd + 500); }
#[test]
fn test_saturating_operations() {
let mut controller = LedbatController::new();
controller.on_data_sent(u32::MAX / 2);
controller.on_data_sent(u32::MAX / 2);
controller.on_ack_received(50_000, u32::MAX);
assert_eq!(controller.get_bytes_in_flight(), 0);
}
#[test]
fn test_time_since_last_send() {
let controller = LedbatController::new();
assert!(controller.time_since_last_send().is_none());
let mut controller = controller;
controller.on_data_sent(1500);
let elapsed = controller.time_since_last_send();
assert!(elapsed.is_some());
assert!(elapsed.unwrap() < Duration::from_millis(100));
}
}