use std::io;
#[cfg(target_os = "linux")]
pub(crate) mod linux;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct Stats {
pub received: u64,
pub dropped: u64,
pub freezes: u64,
}
impl Stats {
#[must_use]
pub const fn has_drops(&self) -> bool {
self.dropped > 0
}
pub(crate) fn accumulate(&mut self, delta: Stats) {
self.received = self.received.wrapping_add(delta.received);
self.dropped = self.dropped.wrapping_add(delta.dropped);
self.freezes = self.freezes.wrapping_add(delta.freezes);
}
}
pub(crate) trait RawChannel {
fn send(&mut self, frame: &[u8]) -> io::Result<usize>;
fn send_batch(&mut self, frames: &[&[u8]]) -> io::Result<usize> {
let mut sent = 0;
for frame in frames {
match self.send(frame) {
Ok(_) => sent += 1,
Err(e) if sent > 0 && e.kind() == io::ErrorKind::WouldBlock => break,
Err(e) => return Err(e),
}
}
Ok(sent)
}
}
#[cfg(test)]
mod tests {
use super::*;
proptest::proptest! {
#[test]
#[cfg_attr(miri, ignore = "proptest is slow under Miri and covers safe arithmetic")]
fn stats_accumulate_folds_deltas(
deltas in proptest::collection::vec(
(0u64..u64::MAX / 2, 0u64..u64::MAX / 2, 0u64..u64::MAX / 2),
0..20,
)
) {
let mut total = Stats::default();
for &(received, dropped, freezes) in &deltas {
total.accumulate(Stats { received, dropped, freezes });
}
let sum = |f: fn(&(u64, u64, u64)) -> u64| {
deltas.iter().fold(0u64, |acc, d| acc.wrapping_add(f(d)))
};
proptest::prop_assert_eq!(total.received, sum(|d| d.0));
proptest::prop_assert_eq!(total.dropped, sum(|d| d.1));
proptest::prop_assert_eq!(total.freezes, sum(|d| d.2));
proptest::prop_assert_eq!(total.has_drops(), total.dropped > 0);
}
}
}