use crate::algorithms::{CongestionControlAlgorithm, Bbr, Cubic};
use crate::constants::perf;
use crate::events::CongestionEvent;
use crate::manager::{CongestionManager, CongestionConfig, AlgorithmType};
use crate::platform;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ControllerConfigBuilder {
initial_algorithm: Option<String>,
auto_switching: Option<bool>,
platform_optimized: Option<bool>,
metrics_window_size: Option<usize>,
switch_cooldown: Option<Duration>,
}
impl ControllerConfigBuilder {
pub fn new() -> Self {
Self {
initial_algorithm: None,
auto_switching: None,
platform_optimized: None,
metrics_window_size: None,
switch_cooldown: None,
}
}
pub fn initial_algorithm<S: Into<String>>(mut self, algorithm: S) -> Self {
self.initial_algorithm = Some(algorithm.into());
self
}
pub fn auto_switching(mut self, enabled: bool) -> Self {
self.auto_switching = Some(enabled);
self
}
pub fn platform_optimized(mut self, enabled: bool) -> Self {
self.platform_optimized = Some(enabled);
self
}
pub fn metrics_window_size(mut self, size: usize) -> Self {
self.metrics_window_size = Some(size);
self
}
pub fn switch_cooldown(mut self, duration: Duration) -> Self {
self.switch_cooldown = Some(duration);
self
}
pub fn build(self) -> ControllerConfig {
ControllerConfig {
initial_algorithm: self.initial_algorithm.unwrap_or_else(|| {
match platform::detection::PLATFORM {
"linux" => "bbr".to_string(),
"freebsd" => "cubic".to_string(),
"macos" => "cubic".to_string(),
"windows" => "cubic".to_string(),
_ => "auto".to_string(),
}
}),
auto_switching: self.auto_switching.unwrap_or(true),
platform_optimized: self.platform_optimized.unwrap_or(true),
metrics_window_size: self.metrics_window_size.unwrap_or_else(|| {
match platform::detection::PLATFORM {
"linux" => 64,
"freebsd" => 32,
"macos" => 32,
"windows" => 16,
_ => 32,
}
}),
switch_cooldown: self.switch_cooldown.unwrap_or(perf::SWITCH_COOLDOWN),
}
}
}
impl Default for ControllerConfigBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ControllerConfig {
pub initial_algorithm: String,
pub auto_switching: bool,
pub platform_optimized: bool,
pub metrics_window_size: usize,
pub switch_cooldown: Duration,
}
impl ControllerConfig {
pub fn builder() -> ControllerConfigBuilder {
ControllerConfigBuilder::new()
}
pub fn platform_optimized() -> Self {
Self::builder().build()
}
pub fn high_performance() -> Self {
Self::builder()
.initial_algorithm("bbr")
.auto_switching(true)
.platform_optimized(true)
.metrics_window_size(128)
.switch_cooldown(Duration::from_secs(1))
.build()
}
pub fn stable_network() -> Self {
Self::builder()
.initial_algorithm("cubic")
.auto_switching(false)
.platform_optimized(true)
.metrics_window_size(32)
.build()
}
pub fn adaptive() -> Self {
Self::builder()
.initial_algorithm("auto")
.auto_switching(true)
.platform_optimized(true)
.switch_cooldown(Duration::from_millis(500))
.build()
}
}
impl Default for ControllerConfig {
fn default() -> Self {
Self::platform_optimized()
}
}
pub struct CongestionController<M: crate::manager::NetworkMetrics, W: crate::manager::MetricsWindow> {
manager: CongestionManager<M, W>,
config: ControllerConfig,
packet_sequence: u64,
}
impl<M: crate::manager::NetworkMetrics, W: crate::manager::MetricsWindow> CongestionController<M, W> {
pub fn new(metrics: M, metrics_window: W) -> Self {
Self::with_config(ControllerConfig::default(), metrics, metrics_window)
}
pub fn with_config(config: ControllerConfig, metrics: M, metrics_window: W) -> Self {
let algorithm: Box<dyn CongestionControlAlgorithm> = match config.initial_algorithm.as_str() {
"bbr" => Box::new(Bbr::new()),
"cubic" => Box::new(Cubic::new()),
"auto" => {
if platform::detection::has_advanced_networking() {
Box::new(Bbr::new())
} else {
Box::new(Cubic::new())
}
},
_ => {
match platform::network::recommended_tcp_algorithm() {
"bbr" => Box::new(Bbr::new()),
_ => Box::new(Cubic::new()),
}
}
};
let manager_config = CongestionConfig {
auto_switch: config.auto_switching,
switch_cooldown: config.switch_cooldown,
metrics_window_size: config.metrics_window_size,
platform_optimized: config.platform_optimized,
};
let manager = CongestionManager::with_config(algorithm, manager_config, metrics, metrics_window);
Self {
manager,
config,
packet_sequence: 0,
}
}
pub fn high_performance(metrics: M, metrics_window: W) -> Self {
Self::with_config(ControllerConfig::high_performance(), metrics, metrics_window)
}
pub fn stable_network(metrics: M, metrics_window: W) -> Self {
Self::with_config(ControllerConfig::stable_network(), metrics, metrics_window)
}
pub fn adaptive(metrics: M, metrics_window: W) -> Self {
Self::with_config(ControllerConfig::adaptive(), metrics, metrics_window)
}
#[inline(always)]
pub fn on_packet_acked(&mut self, bytes: u64, rtt: Duration) {
self.packet_sequence += 1;
self.manager.on_congestion_event(CongestionEvent::PacketAcked {
packet_number: self.packet_sequence,
bytes,
rtt,
});
}
#[inline(always)]
pub fn on_packet_lost(&mut self, bytes: u64) {
self.packet_sequence += 1;
self.manager.on_congestion_event(CongestionEvent::PacketLost {
packet_number: self.packet_sequence,
bytes,
});
}
#[inline(always)]
pub fn on_congestion_notification(&mut self) {
self.manager.on_congestion_event(CongestionEvent::ExplicitCongestionNotification);
}
pub fn process_ack_batch(&mut self, acks: &[(u64, Duration)]) {
let batch: Vec<(u64, u64, Duration)> = acks.iter()
.map(|&(bytes, rtt)| {
self.packet_sequence += 1;
(self.packet_sequence, bytes, rtt)
})
.collect();
self.manager.process_ack_batch(&batch);
}
#[inline(always)]
pub fn can_send(&self, bytes_in_flight: u64) -> bool {
self.manager.can_send(bytes_in_flight)
}
#[inline(always)]
pub fn window_size(&self) -> u64 {
self.manager.congestion_window()
}
#[inline(always)]
pub fn pacing_rate(&self) -> u64 {
self.manager.pacing_rate()
}
#[inline(always)]
pub fn slow_start_threshold(&self) -> u64 {
self.manager.ssthresh()
}
pub fn current_algorithm(&self) -> &'static str {
self.manager.algorithm_name()
}
pub fn network_metrics(&self) -> &M {
self.manager.network_metrics()
}
pub fn platform(&self) -> &'static str {
platform::detection::PLATFORM
}
pub fn platform_optimizations_enabled(&self) -> bool {
self.config.platform_optimized
}
pub fn switch_algorithm(&mut self, algorithm: &str) {
let algorithm_type = match algorithm {
"bbr" => AlgorithmType::Bbr,
"cubic" => AlgorithmType::Cubic,
_ => return, };
self.manager.switch_to(algorithm_type);
}
pub fn reset(&mut self) {
self.manager.reset_statistics();
self.packet_sequence = 0;
}
pub fn config(&self) -> &ControllerConfig {
&self.config
}
pub fn update_config(&mut self, config: ControllerConfig) {
let manager_config = CongestionConfig {
auto_switch: config.auto_switching,
switch_cooldown: config.switch_cooldown,
metrics_window_size: config.metrics_window_size,
platform_optimized: config.platform_optimized,
};
self.manager.set_config(manager_config);
self.config = config;
}
}