#![deny(missing_docs)]
use std::time::Duration;
const N: usize = 30;
#[derive(Debug, Clone)]
pub struct Histogram {
buckets: [u64; N],
total: u64,
}
impl Default for Histogram {
fn default() -> Self {
Self::new()
}
}
impl Histogram {
pub fn new() -> Self {
Self {
buckets: [0; N],
total: 0,
}
}
pub fn record(&mut self, d: Duration) {
let micros = d.as_micros().max(1) as u64;
let idx = bucket_index(micros);
self.buckets[idx] += 1;
self.total += 1;
}
pub fn count(&self) -> u64 {
self.total
}
pub fn percentile(&self, q: f64) -> Duration {
if self.total == 0 {
return Duration::ZERO;
}
let target = (q.clamp(0.0, 1.0) * self.total as f64).ceil().max(1.0) as u64;
let mut running = 0u64;
for (i, &count) in self.buckets.iter().enumerate() {
if count == 0 {
continue;
}
running += count;
if running >= target {
let lo = bucket_lo_micros(i);
let hi = bucket_hi_micros(i);
return Duration::from_micros((lo + hi) / 2);
}
}
let lo = bucket_lo_micros(N - 1);
let hi = bucket_hi_micros(N - 1);
Duration::from_micros((lo + hi) / 2)
}
}
fn bucket_index(micros: u64) -> usize {
let i = 64 - micros.leading_zeros() - 1;
(i as usize).min(N - 1)
}
fn bucket_lo_micros(i: usize) -> u64 {
1u64 << i
}
fn bucket_hi_micros(i: usize) -> u64 {
(1u64 << (i + 1)) - 1
}