#![allow(dead_code)]
use std::time::{Duration, Instant};
pub fn percentile(sorted: &[i64], p: f64) -> Option<i64> {
if sorted.is_empty() {
return None;
}
let idx = ((sorted.len() as f64 - 1.0) * p.clamp(0.0, 1.0)).round() as usize;
Some(sorted[idx])
}
pub fn latency_line(samples: &mut [i64]) -> String {
if samples.is_empty() {
return "n=0 (no samples)".to_string();
}
samples.sort_unstable();
let us = |p: f64| percentile(samples, p).unwrap() as f64 / 1000.0;
format!(
"n={} p50 {:.1} us p90 {:.1} us p99 {:.1} us p99.9 {:.1} us max {:.1} us min {:.1} us",
samples.len(),
us(0.50),
us(0.90),
us(0.99),
us(0.999),
samples[samples.len() - 1] as f64 / 1000.0,
samples[0] as f64 / 1000.0
)
}
pub fn elapsed_ns(earlier: u64, later: u64) -> i64 {
later as i64 - earlier as i64
}
#[derive(Debug, Clone)]
pub struct SeqTracker {
seen: Vec<u64>,
max_seq: u64,
unique: u64,
duplicates: u64,
out_of_order: u64,
out_of_range: u64,
lowest: u64,
highest: u64,
}
impl SeqTracker {
pub fn new(max_seq: u64) -> Self {
Self {
seen: Vec::new(),
max_seq,
unique: 0,
duplicates: 0,
out_of_order: 0,
out_of_range: 0,
lowest: 0,
highest: 0,
}
}
pub fn record(&mut self, seq: u64) -> bool {
if seq == 0 || seq > self.max_seq {
self.out_of_range += 1;
return false;
}
let (word, bit) = ((seq / 64) as usize, 1u64 << (seq % 64));
if self.seen.len() <= word {
self.seen.resize(word + 1, 0);
}
if self.seen[word] & bit != 0 {
self.duplicates += 1;
return false;
}
self.seen[word] |= bit;
self.unique += 1;
if seq < self.highest {
self.out_of_order += 1;
}
self.highest = self.highest.max(seq);
self.lowest = if self.lowest == 0 {
seq
} else {
self.lowest.min(seq)
};
true
}
pub fn contains(&self, seq: u64) -> bool {
let word = (seq / 64) as usize;
word < self.seen.len() && self.seen[word] & (1u64 << (seq % 64)) != 0
}
pub fn missing_in(&self, lo: u64, hi: u64) -> u64 {
if lo > hi {
return 0;
}
(lo..=hi).filter(|&s| !self.contains(s)).count() as u64
}
pub fn unique(&self) -> u64 {
self.unique
}
pub fn duplicates(&self) -> u64 {
self.duplicates
}
pub fn out_of_order(&self) -> u64 {
self.out_of_order
}
pub fn out_of_range(&self) -> u64 {
self.out_of_range
}
pub fn lowest(&self) -> u64 {
self.lowest
}
pub fn highest(&self) -> u64 {
self.highest
}
pub fn summary(&self, lo: u64, hi: u64) -> String {
format!(
"received {} of {} (lost {}), duplicates {}, out of order {}, out of range {}",
self.unique,
hi.saturating_sub(lo) + u64::from(hi >= lo),
self.missing_in(lo, hi),
self.duplicates,
self.out_of_order,
self.out_of_range
)
}
}
#[derive(Debug, Clone)]
pub struct Pacer {
start: Instant,
num: u128,
den: u128,
k: u64,
}
impl Pacer {
pub fn every(period: Duration) -> Self {
Self::with_ratio(period.as_nanos(), 1)
}
pub fn per_second(rate: u64, burst: u64) -> Self {
Self::with_ratio(burst.max(1) as u128 * 1_000_000_000, rate.max(1) as u128)
}
fn with_ratio(num: u128, den: u128) -> Self {
Self {
start: Instant::now(),
num,
den,
k: 0,
}
}
pub fn offset(&self, k: u64) -> Duration {
let ns = k as u128 * self.num / self.den;
Duration::from_nanos(ns.min(u64::MAX as u128) as u64)
}
pub fn wait_next(&mut self) {
let deadline = self.start + self.offset(self.k);
while Instant::now() < deadline {
core::hint::spin_loop();
}
self.k += 1;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Received {
Complete,
Idle,
}
pub fn receive_until<T>(
mut poll: impl FnMut() -> Option<T>,
seq_of: impl Fn(&T) -> u64,
last: u64,
first_wait: Duration,
idle: Duration,
mut on_record: impl FnMut(&T),
) -> Received {
let mut last_arrival = Instant::now();
let mut any = false;
let mut highest = 0u64;
let mut polls = 0u32;
loop {
match poll() {
Some(record) => {
on_record(&record);
any = true;
let seq = seq_of(&record);
if seq >= last {
return Received::Complete;
}
if seq > highest {
highest = seq;
last_arrival = Instant::now();
}
}
None => core::hint::spin_loop(),
}
polls = polls.wrapping_add(1);
if polls.is_multiple_of(256) {
let limit = if any { idle } else { first_wait };
if last_arrival.elapsed() > limit {
return Received::Idle;
}
}
}
}
pub fn parse_arg<T: std::str::FromStr>(name: &str, value: &str) -> T
where
T::Err: std::fmt::Display,
{
value.parse().unwrap_or_else(|e| {
eprintln!("invalid value {:?} for {}: {}", value, name, e);
std::process::exit(2)
})
}
pub fn parse_flag(value: &str) -> bool {
matches!(value, "1" | "true" | "yes")
}