use std::cmp;
use std::time::Duration;
use crate::TIMER_GRANULARITY;
pub struct RttEstimator {
latest_rtt: Duration,
smoothed_rtt: Option<Duration>,
rttvar: Duration,
min_rtt: Duration,
max_rtt: Duration,
}
impl RttEstimator {
pub fn new(initial_rtt: Duration) -> Self {
Self {
latest_rtt: initial_rtt,
smoothed_rtt: None,
rttvar: initial_rtt / 2,
min_rtt: initial_rtt,
max_rtt: initial_rtt,
}
}
pub fn smoothed_rtt(&self) -> Duration {
self.smoothed_rtt.unwrap_or(self.latest_rtt)
}
pub fn latest_rtt(&self) -> Duration {
self.latest_rtt
}
pub fn min_rtt(&self) -> Duration {
self.min_rtt
}
pub fn rttvar(&self) -> Duration {
self.rttvar
}
pub fn max_rtt(&self) -> Duration {
self.max_rtt
}
pub fn pto_base(&self) -> Duration {
self.smoothed_rtt() + cmp::max(4 * self.rttvar, TIMER_GRANULARITY)
}
pub fn try_set_init_rtt(&mut self, init_rtt: Duration) {
if self.smoothed_rtt.is_some() {
return;
}
self.latest_rtt = init_rtt;
self.smoothed_rtt = None;
self.rttvar = init_rtt / 2;
self.min_rtt = init_rtt;
self.max_rtt = init_rtt;
}
pub fn update(&mut self, ack_delay: Duration, rtt: Duration) {
self.latest_rtt = rtt;
self.min_rtt = cmp::min(self.min_rtt, self.latest_rtt);
self.max_rtt = cmp::max(self.max_rtt, self.latest_rtt);
if let Some(smoothed_rtt) = self.smoothed_rtt {
let adjusted_rtt = if self.min_rtt + ack_delay <= self.latest_rtt {
self.latest_rtt - ack_delay
} else {
self.latest_rtt
};
let var_sample = if smoothed_rtt > adjusted_rtt {
smoothed_rtt - adjusted_rtt
} else {
adjusted_rtt - smoothed_rtt
};
self.rttvar = (3 * self.rttvar + var_sample) / 4;
self.smoothed_rtt = Some((7 * smoothed_rtt + adjusted_rtt) / 8);
} else {
self.smoothed_rtt = Some(self.latest_rtt);
self.rttvar = self.latest_rtt / 2;
self.min_rtt = self.latest_rtt;
self.max_rtt = self.latest_rtt;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time;
#[test]
fn initial() {
let initial_rtt = time::Duration::from_millis(200);
let r = RttEstimator::new(initial_rtt);
assert_eq!(r.latest_rtt(), initial_rtt);
assert_eq!(r.min_rtt(), initial_rtt);
assert_eq!(r.max_rtt(), initial_rtt);
assert_eq!(r.rttvar(), initial_rtt / 2);
assert_eq!(r.smoothed_rtt(), initial_rtt);
assert_eq!(r.pto_base(), initial_rtt * 3);
}
#[test]
fn update() {
let initial_rtt = time::Duration::from_millis(200);
let mut r = RttEstimator::new(initial_rtt);
let initial_rtt_before_sample = time::Duration::from_millis(100);
r.try_set_init_rtt(initial_rtt_before_sample);
assert_eq!(r.latest_rtt(), initial_rtt_before_sample);
assert_eq!(r.min_rtt(), initial_rtt_before_sample);
assert_eq!(r.max_rtt(), initial_rtt_before_sample);
assert_eq!(r.rttvar(), initial_rtt_before_sample / 2);
assert_eq!(r.smoothed_rtt(), initial_rtt_before_sample);
let rtt_sample = time::Duration::from_millis(400);
let ack_delay = time::Duration::from_millis(100);
r.update(ack_delay, rtt_sample);
assert_eq!(r.latest_rtt(), rtt_sample);
assert_eq!(r.min_rtt(), rtt_sample);
assert_eq!(r.max_rtt(), rtt_sample);
assert_eq!(r.rttvar(), rtt_sample / 2);
assert_eq!(r.smoothed_rtt(), rtt_sample);
assert_eq!(r.pto_base(), rtt_sample * 3);
let initial_rtt_after_sample = time::Duration::from_millis(300);
r.try_set_init_rtt(initial_rtt_after_sample);
assert_eq!(r.latest_rtt(), rtt_sample);
assert_eq!(r.min_rtt(), rtt_sample);
assert_eq!(r.max_rtt(), rtt_sample);
assert_eq!(r.rttvar(), rtt_sample / 2);
assert_eq!(r.smoothed_rtt(), rtt_sample);
let rtt_sample = time::Duration::from_millis(700);
let ack_delay = time::Duration::from_millis(100);
r.update(ack_delay, rtt_sample);
assert_eq!(r.latest_rtt(), rtt_sample);
assert_eq!(r.min_rtt(), time::Duration::from_millis(400));
assert_eq!(r.max_rtt(), time::Duration::from_millis(700));
assert_eq!(r.rttvar(), time::Duration::from_millis(200));
assert_eq!(r.smoothed_rtt(), time::Duration::from_millis(425));
assert_eq!(r.pto_base(), time::Duration::from_millis(1225));
let rtt_sample = time::Duration::from_millis(225);
let ack_delay = time::Duration::from_millis(100);
r.update(ack_delay, rtt_sample);
assert_eq!(r.latest_rtt(), rtt_sample);
assert_eq!(r.min_rtt(), time::Duration::from_millis(225));
assert_eq!(r.max_rtt(), time::Duration::from_millis(700));
assert_eq!(r.rttvar(), time::Duration::from_millis(200));
assert_eq!(r.smoothed_rtt(), time::Duration::from_millis(400));
assert_eq!(r.pto_base(), time::Duration::from_millis(1200));
}
}