use std::time::Duration;
use std::time::Instant;
#[derive(Default, Debug)]
pub struct FlowControl {
pub read_off: u64,
recv_off: u64,
max_data: u64,
window: u64,
max_window: u64,
last_updated: Option<Instant>,
}
impl FlowControl {
pub fn new(window: u64, max_window: u64) -> FlowControl {
FlowControl {
max_data: window,
window,
max_window,
..FlowControl::default()
}
}
pub fn window(&self) -> u64 {
self.window
}
pub fn max_data(&self) -> u64 {
self.max_data
}
pub fn recv_off(&self) -> u64 {
self.recv_off
}
pub fn increase_recv_off(&mut self, delta: u64) {
self.recv_off += delta;
}
pub fn increase_read_off(&mut self, delta: u64) {
self.read_off += delta;
}
pub fn should_send_max_data(&self) -> bool {
(self.max_data - self.read_off) * 2 < self.window
}
pub fn max_data_next(&self) -> u64 {
self.read_off + self.window
}
pub fn update_max_data(&mut self, now: Instant) {
self.max_data = self.max_data_next();
self.last_updated = Some(now);
}
pub fn autotune_window(&mut self, now: Instant, srtt: Duration) {
if let Some(last_updated) = self.last_updated {
if now - last_updated < srtt * 2 {
self.window = std::cmp::min(self.window * 2, self.max_window);
}
}
}
pub fn ensure_window_lower_bound(&mut self, min_window: u64) {
self.window = std::cmp::max(self.window, min_window);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fc_new() {
let flow_control = FlowControl::new(100, 200);
assert_eq!(flow_control.max_data(), 100);
assert_eq!(flow_control.window(), 100);
assert_eq!(flow_control.max_window, 200);
assert_eq!(flow_control.read_off, 0);
assert_eq!(flow_control.recv_off, 0);
assert_eq!(flow_control.last_updated, None);
}
#[test]
fn fc_increase_recv_off() {
let mut fc = FlowControl::new(100, 200);
for (delta, total) in [(10, 10), (20, 30), (30, 60)] {
fc.increase_recv_off(delta);
assert_eq!(fc.recv_off, total);
}
}
#[test]
fn fc_update_logic() {
let mut fc = FlowControl::new(100, 200);
for (read_delta, read_off, should_send, max_data_next) in [
(0, 0, false, 100),
(50, 50, false, 150),
(1, 51, true, 151),
] {
fc.increase_read_off(read_delta);
assert_eq!(fc.read_off, read_off);
assert_eq!(fc.should_send_max_data(), should_send);
assert_eq!(fc.max_data_next(), max_data_next);
}
fc.update_max_data(Instant::now());
assert_eq!(fc.max_data(), 151);
}
#[test]
fn fc_autotune_window() {
let window = 10;
let max_window = 30;
let now = Instant::now();
let srtt = Duration::from_millis(100);
let mut fc = FlowControl::new(window, max_window);
let read_off = 6;
fc.increase_read_off(read_off);
assert_eq!(fc.should_send_max_data(), true);
let max_data_next = fc.max_data_next();
assert_eq!(max_data_next, read_off + fc.window);
fc.update_max_data(now);
assert_eq!(fc.max_data(), max_data_next);
fc.autotune_window(now + srtt / 2, srtt);
assert_eq!(fc.window, window * 2);
let read_off_delta = 5;
fc.increase_read_off(read_off_delta);
assert_eq!(fc.should_send_max_data(), true);
let max_data_next = fc.max_data_next();
assert_eq!(max_data_next, read_off + read_off_delta + fc.window);
fc.update_max_data(now);
assert_eq!(fc.max_data(), max_data_next);
fc.autotune_window(now + srtt / 2, srtt);
assert_eq!(fc.window, max_window);
}
#[test]
fn fc_ensure_window_lower_bound() {
let mut fc = FlowControl::new(10, 200);
for (min_window, window) in [
(9, 10),
(10, 10),
(11, 11),
] {
fc.ensure_window_lower_bound(min_window);
assert_eq!(fc.window(), window);
}
}
}