use crate::nic;
use crate::layer::{eth, ip};
use crate::wire::Payload;
#[derive(Copy, Clone, Debug, Hash)]
pub struct PrngLoss {
pub threshold: u32,
pub count: u32,
pub reset: u32,
pub lossrate: Option<u32>,
pub prng: Xoroshiro256,
}
pub struct Lossy<'a, I>(pub I, pub &'a mut PrngLoss);
pub struct LossyHandle<H: ?Sized> {
prng: *mut PrngLoss,
handle: *mut H,
}
#[derive(Copy, Clone, Debug, Hash)]
pub struct Xoroshiro256 {
state: [u64; 4],
}
impl PrngLoss {
pub fn uniform(rate: Option<u32>, seed: u64) -> Self {
PrngLoss {
threshold: 1,
count: 0,
reset: 0,
lossrate: rate,
prng: Xoroshiro256::new(seed),
}
}
pub fn lossy<I>(&mut self, layer: I) -> Lossy<I> {
Lossy(layer, self)
}
pub fn pulsed(high: u32, length: u32) -> Self {
assert!(length > 0, "Pulse length must not be zero");
assert!(high <= length, "Length of high signals must be shorter than total length");
PrngLoss {
threshold: high,
count: length - 1,
reset: length - 1,
lossrate: Some(u32::max_value()),
prng: Xoroshiro256::new(0),
}
}
pub fn next_pass(&mut self) -> bool {
let in_window = self.count < self.threshold;
let fate_drop = Some(self.roll()) <= self.lossrate;
let ncount = self.count.checked_sub(1)
.unwrap_or(self.reset);
self.count = ncount;
!in_window || !fate_drop
}
fn roll(&mut self) -> u32 {
(self.prng.next() & u64::from(!0u32)) as u32
}
}
impl Xoroshiro256 {
pub fn new(seed: u64) -> Self {
Xoroshiro256 {
state: [seed, 0, 0, 0],
}
}
pub fn next(&mut self) -> u64 {
let s = &mut self.state;
let result_starstar = s[1]
.wrapping_mul(5)
.rotate_left(7)
.wrapping_mul(9);
let t = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = s[3].rotate_left(45);
result_starstar
}
}
impl<H: ?Sized> LossyHandle<H> {
fn new<'a>(
uninit: &'a mut core::mem::MaybeUninit<Self>,
prng: &'a mut PrngLoss,
handle: &'a mut H,
) -> &'a mut Self {
unsafe {
(*uninit.as_mut_ptr()).prng = prng;
(*uninit.as_mut_ptr()).handle = handle;
&mut *uninit.as_mut_ptr()
}
}
}
impl<H, P, I> nic::Recv<H, P> for Lossy<'_, I>
where
H: nic::Handle + ?Sized,
P: Payload + ?Sized,
I: nic::Recv<LossyHandle<H>, P>,
{
fn receive(&mut self, packet: nic::Packet<H, P>) {
if !self.1.next_pass() {
return;
}
let nic::Packet { handle, payload } = packet;
let mut handle_mem = core::mem::MaybeUninit::uninit();
let handle = LossyHandle::new(
&mut handle_mem,
&mut *self.1,
handle);
let packet = nic::Packet {
handle,
payload,
};
self.0.receive(packet)
}
}
impl<H, P, I> nic::Send<H, P> for Lossy<'_, I>
where
H: nic::Handle + ?Sized,
P: Payload + ?Sized,
I: nic::Send<LossyHandle<H>, P>,
{
fn send(&mut self, packet: nic::Packet<H, P>) {
let nic::Packet { handle, payload } = packet;
let mut handle_mem = core::mem::MaybeUninit::uninit();
let handle = LossyHandle::new(
&mut handle_mem,
&mut *self.1,
handle);
self.0.send(nic::Packet {
handle: &mut *handle,
payload: &mut *payload,
});
}
}
impl<H: nic::Handle + ?Sized> nic::Handle for LossyHandle<H> {
fn queue(&mut self) -> crate::layer::Result<()> {
if unsafe { &mut *self.prng }.next_pass() {
unsafe { &mut *self.handle }.queue()
} else {
Ok(())
}
}
fn info(&self) -> &dyn nic::Info {
unsafe { &*self.handle }.info()
}
}
impl<D> nic::Device for Lossy<'_, D>
where
D: nic::Device,
{
type Handle = LossyHandle<D::Handle>;
type Payload = D::Payload;
fn personality(&self) -> nic::Personality {
self.0.personality()
}
fn tx(&mut self, max: usize, sender: impl nic::Send<Self::Handle, Self::Payload>)
-> crate::layer::Result<usize>
{
self.0.tx(max, Lossy(sender, self.1))
}
fn rx(&mut self, max: usize, receptor: impl nic::Recv<Self::Handle, Self::Payload>)
-> crate::layer::Result<usize>
{
self.0.rx(max, Lossy(receptor, self.1))
}
}
impl<P, I> eth::Recv<P> for Lossy<'_, I>
where
P: Payload,
I: eth::Recv<P>,
{
fn receive(&mut self, packet: eth::InPacket<P>) {
if !self.1.next_pass() {
return;
}
let mut handle_mem = core::mem::MaybeUninit::uninit();
let loss = &mut self.1;
let eth::InPacket { mut control, frame } = packet;
let control = control
.borrow_mut()
.wrap(|inner| LossyHandle::new(
&mut handle_mem, loss, inner));
let packet = eth::InPacket { control, frame, };
self.0.receive(packet)
}
}
impl<P, I> eth::Send<P> for Lossy<'_, I>
where
P: Payload,
I: eth::Send<P>,
{
fn send(&mut self, packet: eth::RawPacket<P>) {
let mut handle_mem = core::mem::MaybeUninit::uninit();
let loss = &mut self.1;
let eth::RawPacket { mut control, payload } = packet;
let control = control
.borrow_mut()
.wrap(|inner| LossyHandle::new(
&mut handle_mem, loss, inner));
let packet = eth::RawPacket { control, payload, };
self.0.send(packet);
}
}
impl<P, I> ip::Recv<P> for Lossy<'_, I>
where
P: Payload,
I: ip::Recv<P>,
{
fn receive(&mut self, packet: ip::InPacket<P>) {
if !self.1.next_pass() {
return;
}
let mut handle_mem = core::mem::MaybeUninit::uninit();
let loss = &mut self.1;
let ip::InPacket { mut control, packet } = packet;
let control = control
.borrow_mut()
.wrap(|inner| LossyHandle::new(
&mut handle_mem, loss, inner));
let packet = ip::InPacket { control, packet, };
self.0.receive(packet)
}
}
impl<P, I> ip::Send<P> for Lossy<'_, I>
where
P: Payload,
I: ip::Send<P>,
{
fn send(&mut self, packet: ip::RawPacket<P>) {
let mut handle_mem = core::mem::MaybeUninit::uninit();
let loss = &mut self.1;
let ip::RawPacket { mut control, payload } = packet;
let control = control
.borrow_mut()
.wrap(|inner| LossyHandle::new(
&mut handle_mem, loss, inner));
let packet = ip::RawPacket { control, payload, };
self.0.send(packet);
}
}
#[cfg(test)]
mod tests {
use super::PrngLoss;
#[test]
fn pulsed() {
let mut prng = PrngLoss::pulsed(1, 10);
let count = (0..100)
.filter(|_| !prng.next_pass())
.count();
assert_eq!(count, 10);
prng = PrngLoss::pulsed(1, 1);
let count = (0..100)
.filter(|_| !prng.next_pass())
.count();
assert_eq!(count, 100);
prng = PrngLoss::pulsed(1, 10);
prng.lossrate = Some(!0 >> 1);
let count = (0..100)
.filter(|_| !prng.next_pass())
.count();
assert!(count <= 10);
}
}