#[cfg(feature = "serde")]
use core::fmt;
use core::ops::Sub;
#[cfg(feature = "serde")]
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{SeqAccess, Visitor},
};
#[derive(Debug, Clone, Copy)]
pub struct Histogram {
pub(crate) buckets: [usize; 64],
}
impl Default for Histogram {
fn default() -> Self {
Self {
buckets: [0usize; 64],
}
}
}
impl Sub<&Histogram> for &Histogram {
type Output = Histogram;
fn sub(self, old: &Histogram) -> Histogram {
let mut out = [0usize; 64];
for (i, (b_base, b_old)) in self.buckets.iter().zip(&old.buckets).enumerate() {
out[i] = b_base.saturating_sub(*b_old);
}
Histogram { buckets: out }
}
}
impl Sub<Histogram> for Histogram {
type Output = Histogram;
fn sub(self, old: Histogram) -> Histogram {
&self - &old
}
}
impl Sub<&Histogram> for Histogram {
type Output = Histogram;
#[allow(clippy::op_ref)]
fn sub(self, old: &Histogram) -> Histogram {
&self - old
}
}
impl Sub<Histogram> for &Histogram {
type Output = Histogram;
#[allow(clippy::op_ref)]
fn sub(self, old: Histogram) -> Histogram {
self - &old
}
}
impl Histogram {
pub const fn from_buckets(buckets: [usize; 64]) -> Self {
Self { buckets }
}
pub const fn buckets(&self) -> &[usize; 64] {
&self.buckets
}
pub fn total(&self) -> usize {
self.buckets.iter().sum()
}
pub fn is_empty(&self) -> bool {
self.buckets.iter().all(|b| *b == 0)
}
pub fn quantile(&self, q: f64) -> Option<usize> {
if !(0.0..=1.0).contains(&q) {
return None;
}
let total = self.total();
if total == 0 {
return None;
}
let float_target = (total as f64) * q;
let mut target = float_target as usize;
if (target as f64) < float_target {
target += 1;
}
let target = target.max(1).min(total);
let mut cumulative = 0usize;
for (k, &count) in self.buckets.iter().enumerate() {
if count == 0 {
continue;
}
let next = cumulative + count;
if next >= target {
let lo = 1usize << k;
let width = lo; let offset_in_bucket = target - cumulative - 1;
let interp = (offset_in_bucket as u128 * width as u128) / count as u128;
return Some(lo + interp as usize);
}
cumulative = next;
}
None
}
}
#[cfg(feature = "serde")]
impl Serialize for Histogram {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.buckets.as_slice().serialize(s)
}
}
#[cfg(feature = "serde")]
struct BucketVisitor;
#[cfg(feature = "serde")]
impl<'de> Visitor<'de> for BucketVisitor {
type Value = Histogram;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an array of 64 usize values")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut out = [0usize; 64];
for (i, slot) in out.iter_mut().enumerate() {
*slot = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(i, &self))?;
}
Ok(Histogram { buckets: out })
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Histogram {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
d.deserialize_tuple(64, BucketVisitor)
}
}