use crate::constants::*;
#[derive(Debug, Clone)]
pub struct WindowState {
pub window: usize,
pub window_max: usize,
pub window_min: usize,
pub window_flexibility: usize,
pub fast_rate_rounds: usize,
pub very_slow_rate_rounds: usize,
}
impl WindowState {
pub fn new() -> Self {
WindowState {
window: RESOURCE_WINDOW,
window_max: RESOURCE_WINDOW_MAX_SLOW,
window_min: RESOURCE_WINDOW_MIN,
window_flexibility: RESOURCE_WINDOW_FLEXIBILITY,
fast_rate_rounds: 0,
very_slow_rate_rounds: 0,
}
}
pub fn restore(&mut self, previous_window: usize) {
self.window = previous_window;
}
pub fn on_window_complete(&mut self) {
if self.window < self.window_max {
self.window += 1;
if (self.window as isize - self.window_min as isize)
> (self.window_flexibility as isize - 1)
{
self.window_min += 1;
}
}
}
pub fn on_timeout(&mut self) {
if self.window > self.window_min {
self.window -= 1;
if self.window_max > self.window_min {
self.window_max -= 1;
if (self.window_max as isize - self.window as isize)
> (self.window_flexibility as isize - 1)
{
self.window_max -= 1;
}
}
}
}
pub fn update_req_resp_rate(&mut self, rate: f64) {
if rate > RESOURCE_RATE_FAST && self.fast_rate_rounds < RESOURCE_FAST_RATE_THRESHOLD {
self.fast_rate_rounds += 1;
if self.fast_rate_rounds == RESOURCE_FAST_RATE_THRESHOLD {
self.window_max = RESOURCE_WINDOW_MAX_FAST;
}
}
}
pub fn update_data_rate(&mut self, rate: f64) {
if rate > RESOURCE_RATE_FAST && self.fast_rate_rounds < RESOURCE_FAST_RATE_THRESHOLD {
self.fast_rate_rounds += 1;
if self.fast_rate_rounds == RESOURCE_FAST_RATE_THRESHOLD {
self.window_max = RESOURCE_WINDOW_MAX_FAST;
}
}
if self.fast_rate_rounds == 0
&& rate < RESOURCE_RATE_VERY_SLOW
&& self.very_slow_rate_rounds < RESOURCE_VERY_SLOW_RATE_THRESHOLD
{
self.very_slow_rate_rounds += 1;
if self.very_slow_rate_rounds == RESOURCE_VERY_SLOW_RATE_THRESHOLD {
self.window_max = RESOURCE_WINDOW_MAX_VERY_SLOW;
}
}
}
}
impl Default for WindowState {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initial_state() {
let ws = WindowState::new();
assert_eq!(ws.window, RESOURCE_WINDOW); assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); assert_eq!(ws.window_min, RESOURCE_WINDOW_MIN); assert_eq!(ws.window_flexibility, RESOURCE_WINDOW_FLEXIBILITY); assert_eq!(ws.fast_rate_rounds, 0);
assert_eq!(ws.very_slow_rate_rounds, 0);
}
#[test]
fn test_window_increase_on_complete() {
let mut ws = WindowState::new();
ws.on_window_complete();
assert_eq!(ws.window, 5);
assert_eq!(ws.window_min, 2);
ws.on_window_complete();
assert_eq!(ws.window, 6);
assert_eq!(ws.window_min, 3);
}
#[test]
fn test_window_capped_at_max() {
let mut ws = WindowState::new();
ws.window = 10;
ws.window_max = 10;
ws.on_window_complete();
assert_eq!(ws.window, 10); }
#[test]
fn test_window_decrease_on_timeout() {
let mut ws = WindowState::new();
ws.on_timeout();
assert_eq!(ws.window, 3);
assert_eq!(ws.window_max, 8);
}
#[test]
fn test_window_min_floor() {
let mut ws = WindowState::new();
ws.window = RESOURCE_WINDOW_MIN;
ws.on_timeout();
assert_eq!(ws.window, RESOURCE_WINDOW_MIN); }
#[test]
fn test_fast_rate_detection() {
let mut ws = WindowState::new();
for _ in 0..3 {
ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
}
assert_eq!(ws.fast_rate_rounds, 3);
assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW);
ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
assert_eq!(ws.fast_rate_rounds, 4);
assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_FAST); }
#[test]
fn test_fast_rate_rounds_never_reset() {
let mut ws = WindowState::new();
ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
assert_eq!(ws.fast_rate_rounds, 1);
ws.update_req_resp_rate(1.0);
assert_eq!(ws.fast_rate_rounds, 1);
}
#[test]
fn test_fast_rate_rounds_cap() {
let mut ws = WindowState::new();
for _ in 0..10 {
ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
}
assert_eq!(ws.fast_rate_rounds, RESOURCE_FAST_RATE_THRESHOLD);
}
#[test]
fn test_very_slow_detection() {
let mut ws = WindowState::new();
assert_eq!(ws.fast_rate_rounds, 0);
ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
assert_eq!(ws.very_slow_rate_rounds, 1);
assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW);
ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
assert_eq!(ws.very_slow_rate_rounds, 2);
assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_VERY_SLOW); }
#[test]
fn test_very_slow_blocked_by_fast() {
let mut ws = WindowState::new();
ws.update_data_rate(RESOURCE_RATE_FAST + 1.0);
assert_eq!(ws.fast_rate_rounds, 1);
ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
assert_eq!(ws.very_slow_rate_rounds, 0);
}
#[test]
fn test_restore_window() {
let mut ws = WindowState::new();
ws.restore(8);
assert_eq!(ws.window, 8);
}
#[test]
fn test_window_never_below_min() {
let mut ws = WindowState::new();
ws.window = 2;
ws.window_min = 2;
ws.on_timeout();
assert_eq!(ws.window, 2);
}
#[test]
fn test_window_never_above_max() {
let mut ws = WindowState::new();
ws.window = 10;
ws.window_max = 10;
ws.on_window_complete();
assert_eq!(ws.window, 10);
}
}