use std::time::Duration;
use std::time::Instant;
#[derive(Debug)]
pub struct Pacer {
enabled: bool,
capacity: usize,
used: usize,
rate: u64,
last_update: Instant,
next_time: Instant,
max_datagram_size: usize,
last_packet_size: Option<usize>,
iv: Duration,
max_pacing_rate: Option<u64>,
}
impl Pacer {
pub fn new(
enabled: bool, capacity: usize, rate: u64, max_datagram_size: usize,
max_pacing_rate: Option<u64>,
) -> Self {
let capacity = capacity / max_datagram_size * max_datagram_size;
let pacing_rate = if let Some(max_rate) = max_pacing_rate {
max_rate.min(rate)
} else {
rate
};
Pacer {
enabled,
capacity,
used: 0,
rate: pacing_rate,
last_update: Instant::now(),
next_time: Instant::now(),
max_datagram_size,
last_packet_size: None,
iv: Duration::ZERO,
max_pacing_rate,
}
}
pub fn enabled(&self) -> bool {
self.enabled
}
pub fn rate(&self) -> u64 {
self.rate
}
pub fn max_pacing_rate(&self) -> Option<u64> {
self.max_pacing_rate
}
pub fn update(&mut self, capacity: usize, rate: u64, now: Instant) {
let capacity = capacity / self.max_datagram_size * self.max_datagram_size;
if self.capacity != capacity {
self.reset(now);
}
self.capacity = capacity;
self.rate = if let Some(max_rate) = self.max_pacing_rate {
max_rate.min(rate)
} else {
rate
};
}
fn reset(&mut self, now: Instant) {
self.used = 0;
self.last_update = now;
self.next_time = self.next_time.max(now);
self.last_packet_size = None;
self.iv = Duration::ZERO;
}
pub fn send(&mut self, packet_size: usize, now: Instant) {
if self.rate() == 0 {
self.reset(now);
return;
}
if !self.iv.is_zero() {
self.next_time = self.next_time.max(now) + self.iv;
self.iv = Duration::ZERO;
}
let interval =
Duration::from_secs_f64(self.capacity as f64 / self.rate() as f64);
let elapsed = now.saturating_duration_since(self.last_update);
if elapsed > interval {
self.reset(now);
}
self.used += packet_size;
let same_size = if let Some(last_packet_size) = self.last_packet_size {
last_packet_size == packet_size
} else {
true
};
self.last_packet_size = Some(packet_size);
if self.used >= self.capacity || !same_size {
self.iv =
Duration::from_secs_f64(self.used as f64 / self.rate() as f64);
self.used = 0;
self.last_update = now;
self.last_packet_size = None;
};
}
pub fn next_time(&self) -> Instant {
self.next_time
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pacer_update() {
let datagram_size = 1200;
let max_burst = datagram_size * 10;
let pacing_rate = 100_000;
let mut p = Pacer::new(true, max_burst, pacing_rate, datagram_size, None);
let now = Instant::now();
p.send(6000, now);
assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
p.send(6000, now);
assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
let now = now + Duration::from_millis(5);
p.send(1000, now);
let interval = max_burst as f64 / pacing_rate as f64;
assert_eq!(p.next_time() - now, Duration::from_secs_f64(interval));
}
#[test]
fn pacer_idle() {
let datagram_size = 1200;
let max_burst = datagram_size * 10;
let pacing_rate = 100_000;
let mut p = Pacer::new(true, max_burst, pacing_rate, datagram_size, None);
let now = Instant::now();
p.send(6000, now);
assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
let now = now + Duration::from_millis(200);
p.send(6000, now);
assert_eq!(p.next_time(), now);
}
#[test]
fn pacer_set_max_pacing_rate() {
let datagram_size = 1200;
let max_burst = datagram_size * 10;
let pacing_rate = 100_000;
let max_pacing_rate = 50_000;
let mut p = Pacer::new(
true,
max_burst,
pacing_rate,
datagram_size,
Some(max_pacing_rate),
);
let now = Instant::now();
p.send(6000, now);
assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
p.send(6000, now);
assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
let now = now + Duration::from_millis(5);
p.send(12000, now);
let second_burst_send_time = p.next_time();
let interval = max_burst as f64 / max_pacing_rate as f64;
assert_eq!(
second_burst_send_time - now,
Duration::from_secs_f64(interval)
);
let now = now + Duration::from_millis(5);
p.update(max_burst, 75_000, now);
p.send(12000, now);
let third_burst_send_time = p.next_time();
assert_eq!(
third_burst_send_time - second_burst_send_time,
Duration::from_secs_f64(interval)
);
}
}