use std::io::Result;
use std::net::SocketAddr;
use std::time::Duration;
use cfg_if::cfg_if;
use rand;
use rand::{thread_rng, Rng};
use crate::transport::PacketReceiver;
use crate::utils::ready_buffer::ReadyBuffer;
cfg_if! {
if #[cfg(any(test))] {
use mock_instant::Instant;
} else {
use std::time::Instant;
}
}
#[derive(Clone)]
pub struct LinkConditionerConfig {
pub incoming_latency: Duration,
pub incoming_jitter: Duration,
pub incoming_loss: f32,
}
pub struct ConditionedPacketReceiver<T: PacketReceiver, P: Eq> {
packet_receiver: T,
config: LinkConditionerConfig,
pub time_queue: ReadyBuffer<Instant, P>,
last_packet: Option<P>,
}
impl<T: PacketReceiver, P: Eq> ConditionedPacketReceiver<T, P> {
pub fn new(packet_receiver: T, link_conditioner_config: &LinkConditionerConfig) -> Self {
ConditionedPacketReceiver {
packet_receiver,
config: link_conditioner_config.clone(),
time_queue: ReadyBuffer::new(),
last_packet: None,
}
}
}
fn condition_packet<P: Eq>(
config: &LinkConditionerConfig,
time_queue: &mut ReadyBuffer<Instant, P>,
packet: P,
) {
let mut rng = thread_rng();
if rng.gen_range(0.0..1.0) <= config.incoming_loss {
return;
}
let mut latency: i32 = config.incoming_latency.as_millis() as i32;
let mut packet_timestamp = Instant::now();
if config.incoming_jitter > Duration::default() {
let jitter: i32 = config.incoming_jitter.as_millis() as i32;
latency += rng.gen_range(-jitter..jitter);
}
if latency > 0 {
packet_timestamp += Duration::from_millis(latency as u64);
}
time_queue.add_item(packet_timestamp, packet);
}
impl<T: PacketReceiver> PacketReceiver for ConditionedPacketReceiver<T, (SocketAddr, Box<[u8]>)> {
fn recv(&mut self) -> Result<Option<(&mut [u8], SocketAddr)>> {
loop {
match self.packet_receiver.recv() {
Ok(option) => match option {
None => break,
Some((data, addr)) => condition_packet(
&self.config,
&mut self.time_queue,
(addr, data.to_vec().into_boxed_slice()),
),
},
Err(err) => {
return Err(err);
}
}
}
match self.time_queue.pop_item(&Instant::now()) {
Some((_, (addr, data))) => {
self.last_packet = Some((addr, data));
Ok(Some((self.last_packet.as_mut().unwrap().1.as_mut(), addr)))
}
None => Ok(None),
}
}
}
impl LinkConditionerConfig {
pub fn new(incoming_latency: Duration, incoming_jitter: Duration, incoming_loss: f32) -> Self {
LinkConditionerConfig {
incoming_latency,
incoming_jitter,
incoming_loss,
}
}
pub fn good_condition() -> Self {
LinkConditionerConfig {
incoming_latency: Duration::from_millis(40),
incoming_jitter: Duration::from_millis(6),
incoming_loss: 0.002,
}
}
pub fn average_condition() -> Self {
LinkConditionerConfig {
incoming_latency: Duration::from_millis(170),
incoming_jitter: Duration::from_millis(45),
incoming_loss: 0.02,
}
}
pub fn poor_condition() -> Self {
LinkConditionerConfig {
incoming_latency: Duration::from_millis(300),
incoming_jitter: Duration::from_millis(84),
incoming_loss: 0.04,
}
}
}