use super::{Bandwidth, Delay, DuplicatePattern, Duration, LossPattern, RwndDecision};
use super::{BwTrace, DelayPerPacketTrace, DelayTrace, DuplicateTrace, LossTrace, RwndTrace};
pub struct Peekable<Trace, Item> {
inner: Trace,
next: fn(&mut Trace) -> Option<Item>,
peeked: Option<Item>,
}
impl<Trace, Item> Peekable<Trace, Item> {
pub fn new(inner: Trace, next: fn(&mut Trace) -> Option<Item>) -> Self {
Self {
inner,
next,
peeked: None,
}
}
#[inline(always)]
fn update(&mut self) {
if self.peeked.is_none() {
self.peeked = (self.next)(&mut self.inner);
}
}
pub fn peek(&mut self) -> Option<&Item> {
self.update();
self.peeked.as_ref()
}
pub fn peek_mut(&mut self) -> Option<&mut Item> {
self.update();
self.peeked.as_mut()
}
#[inline(always)]
fn take_next(&mut self) -> Option<Item> {
self.update();
self.peeked.take()
}
}
macro_rules! impl_peekable_for {
($ext:ident, $trace:ident, $next:ident, $item:ty) => {
#[doc = concat!("Adds `peekable()` to every type that implements [`", stringify!($trace), "`].")]
#[doc = concat!("For `Box<dyn ", stringify!($trace), ">`, the same method is provided on the trait object itself.")]
pub trait $ext: $trace + Sized {
fn peekable(self) -> Peekable<Self, $item> {
Peekable::new(self, <Self as $trace>::$next)
}
}
impl<T: $trace> $ext for T {}
impl dyn $trace {
pub fn peekable(self: Box<Self>) -> Peekable<Box<Self>, $item> {
Peekable::new(self, |inner| inner.$next())
}
}
impl<T: $trace> $trace for Peekable<T, $item> {
fn $next(&mut self) -> Option<$item> {
self.take_next()
}
}
impl $trace for Peekable<Box<dyn $trace>, $item> {
fn $next(&mut self) -> Option<$item> {
self.take_next()
}
}
};
}
impl_peekable_for!(BwTracePeekExt, BwTrace, next_bw, (Bandwidth, Duration));
impl_peekable_for!(DelayTracePeekExt, DelayTrace, next_delay, (Delay, Duration));
impl_peekable_for!(
DelayPerPacketTracePeekExt,
DelayPerPacketTrace,
next_delay,
Delay
);
impl_peekable_for!(
LossTracePeekExt,
LossTrace,
next_loss,
(LossPattern, Duration)
);
impl_peekable_for!(
DuplicateTracePeekExt,
DuplicateTrace,
next_duplicate,
(DuplicatePattern, Duration)
);
impl_peekable_for!(
RwndTracePeekExt,
RwndTrace,
next_rwnd,
(RwndDecision, Duration)
);
#[cfg(test)]
mod test {
use super::*;
struct VecDelay(std::vec::IntoIter<Delay>);
impl VecDelay {
fn new(millis: &[u64]) -> Self {
let delays: Vec<_> = millis.iter().map(|&ms| Delay::from_millis(ms)).collect();
Self(delays.into_iter())
}
}
impl DelayTrace for VecDelay {
fn next_delay(&mut self) -> Option<(Delay, Duration)> {
self.0.next().map(|delay| (delay, Duration::from_secs(1)))
}
}
fn item(ms: u64) -> (Delay, Duration) {
(Delay::from_millis(ms), Duration::from_secs(1))
}
fn drain(trace: &mut impl DelayTrace) -> Vec<(Delay, Duration)> {
std::iter::from_fn(|| trace.next_delay()).collect()
}
#[test]
fn test_peek_does_not_consume() {
let mut trace = VecDelay::new(&[10, 20]).peekable();
assert_eq!(trace.peek(), Some(&item(10)));
assert_eq!(trace.peek(), Some(&item(10)));
assert_eq!(trace.next_delay(), Some(item(10)));
assert_eq!(trace.peek(), Some(&item(20)));
assert_eq!(trace.next_delay(), Some(item(20)));
assert_eq!(trace.peek(), None);
assert_eq!(trace.next_delay(), None);
}
#[test]
fn test_peek_mut_modifies_next() {
let mut trace = VecDelay::new(&[10, 20]).peekable();
if let Some((delay, _)) = trace.peek_mut() {
*delay = Delay::from_millis(15);
}
assert_eq!(trace.next_delay(), Some(item(15)));
assert_eq!(trace.next_delay(), Some(item(20)));
}
#[test]
fn test_peekable_is_drop_in_trace() {
let mut trace = VecDelay::new(&[10, 20, 30]).peekable();
assert_eq!(trace.peek(), Some(&item(10)));
assert_eq!(drain(&mut trace), vec![item(10), item(20), item(30)]);
}
#[test]
fn test_boxed_dyn_trace() {
let boxed: Box<dyn DelayTrace> = Box::new(VecDelay::new(&[10, 20]));
let mut trace = boxed.peekable();
assert_eq!(trace.peek(), Some(&item(10)));
assert_eq!(drain(&mut trace), vec![item(10), item(20)]);
}
#[test]
fn test_boxed_peekable_as_dyn_trace() {
let boxed: Box<dyn DelayTrace> = Box::new(VecDelay::new(&[10, 20]));
let mut trace: Box<dyn DelayTrace> = Box::new(boxed.peekable());
assert_eq!(trace.next_delay(), Some(item(10)));
assert_eq!(trace.next_delay(), Some(item(20)));
assert_eq!(trace.next_delay(), None);
}
#[cfg(feature = "delay-model")]
mod delay_model {
use super::*;
use crate::model::{DelayTraceConfig, StaticDelayConfig};
#[test]
fn test_static_delay() {
let mut trace = StaticDelayConfig::new()
.delay(Delay::from_millis(10))
.duration(Duration::from_secs(1))
.build()
.peekable();
assert_eq!(trace.peek(), Some(&item(10)));
assert_eq!(trace.next_delay(), Some(item(10)));
assert_eq!(trace.peek(), None);
assert_eq!(trace.next_delay(), None);
}
#[test]
fn test_model_from_config() {
let config = Box::new(
StaticDelayConfig::new()
.delay(Delay::from_millis(10))
.duration(Duration::from_secs(1)),
) as Box<dyn DelayTraceConfig>;
let mut trace = config.into_model().peekable();
assert_eq!(trace.peek(), Some(&item(10)));
assert_eq!(drain(&mut trace), vec![item(10)]);
}
}
#[cfg(feature = "delay-per-packet-model")]
mod delay_per_packet_model {
use super::*;
use crate::model::StaticDelayPerPacketConfig;
#[test]
fn test_boxed_static_delay_per_packet() {
let boxed: Box<dyn DelayPerPacketTrace> = StaticDelayPerPacketConfig::new()
.delay(Delay::from_millis(10))
.count(2)
.build()
.into();
let mut trace = boxed.peekable();
assert_eq!(trace.peek(), Some(&Delay::from_millis(10)));
assert_eq!(trace.next_delay(), Some(Delay::from_millis(10)));
assert_eq!(trace.next_delay(), Some(Delay::from_millis(10)));
assert_eq!(trace.peek(), None);
}
}
}