use std::sync::atomic::AtomicU64;
use crate::raw::Atomic;
pub use crate::raw::bucket::Bucket;
#[derive(Clone, Copy, Debug)]
pub enum BoundsFilter {
AllowNegative,
RejectNegative,
}
pub struct HistogramCore {
buckets: Vec<BucketCell>,
count: AtomicU64,
sum: AtomicU64,
}
struct BucketCell {
upper_bound: f64,
count: AtomicU64,
}
impl BucketCell {
fn new(upper_bound: f64) -> Self {
Self { upper_bound, count: AtomicU64::new(0) }
}
fn inc(&self) {
self.count.inc_by(1);
}
fn load(&self) -> Bucket {
Bucket::new(self.upper_bound, self.count.get())
}
}
impl HistogramCore {
pub fn from_bounds(buckets: impl IntoIterator<Item = f64>, filter: BoundsFilter) -> Self {
let mut upper_bounds = buckets
.into_iter()
.filter(|upper_bound| {
if upper_bound.is_nan() {
return false;
}
match filter {
BoundsFilter::AllowNegative => true,
BoundsFilter::RejectNegative => upper_bound.is_sign_positive(),
}
})
.collect::<Vec<_>>();
upper_bounds.sort_by(|a, b| a.partial_cmp(b).expect("upper_bound must not be NaN"));
upper_bounds.dedup();
match upper_bounds.last() {
Some(last) if last.is_finite() => upper_bounds.push(f64::INFINITY),
None => upper_bounds.push(f64::INFINITY),
_ => { },
}
let buckets = upper_bounds.into_iter().map(BucketCell::new).collect::<Vec<_>>();
Self { buckets, count: AtomicU64::new(0), sum: AtomicU64::new(0f64.to_bits()) }
}
pub fn observe(&self, value: f64) {
self.count.inc_by(1);
self.sum.inc_by(value);
let idx = self.bucket_index(value);
self.buckets[idx].inc();
}
pub fn bucket_index(&self, value: f64) -> usize {
self.buckets.partition_point(|bucket| bucket.upper_bound < value)
}
pub fn snapshot(&self) -> HistogramSnapshot {
let buckets = self.buckets.iter().map(BucketCell::load).collect();
let count = self.count.get();
let sum = self.sum.get();
HistogramSnapshot { buckets, count, sum }
}
}
#[derive(Clone)]
pub struct HistogramSnapshot {
buckets: Vec<Bucket>,
count: u64,
sum: f64,
}
impl HistogramSnapshot {
pub fn buckets(&self) -> &[Bucket] {
&self.buckets
}
pub const fn count(&self) -> u64 {
self.count
}
pub const fn sum(&self) -> f64 {
self.sum
}
}