const REGION_SIZE: usize = 16;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Slot<T> {
pub(crate) buckets: Vec<u64>,
region_sums: Vec<u64>,
pub(crate) total: u64,
pub(crate) data: Option<T>,
}
impl<T> Slot<T> {
pub(crate) fn new(num_buckets: usize) -> Self {
let num_regions = num_buckets.div_ceil(REGION_SIZE);
Self {
buckets: vec![0; num_buckets],
region_sums: vec![0; num_regions],
total: 0,
data: None,
}
}
pub(crate) fn from_buckets(buckets: Vec<u64>, data: Option<T>) -> Self {
let total = buckets.iter().sum();
let region_sums = buckets.chunks(REGION_SIZE).map(|chunk| chunk.iter().sum()).collect();
Self {
buckets,
region_sums,
total,
data,
}
}
#[inline]
pub(crate) fn record_n(&mut self, bucket_index: usize, count: u64) {
self.buckets[bucket_index] += count;
self.region_sums[bucket_index / REGION_SIZE] += count;
self.total += count;
}
#[inline]
pub(crate) fn count_at(&self, index: usize) -> u64 {
self.buckets[index]
}
#[inline]
pub(crate) fn total(&self) -> u64 {
self.total
}
pub(crate) fn subtract(&mut self, other: &Slot<T>) {
(0..self.buckets.len()).for_each(|i| {
self.buckets[i] -= other.buckets[i];
});
(0..self.region_sums.len()).for_each(|r| {
self.region_sums[r] -= other.region_sums[r];
});
self.total -= other.total;
}
pub(crate) fn add(&mut self, other: &Slot<T>) {
(0..self.buckets.len()).for_each(|i| {
self.buckets[i] += other.buckets[i];
});
(0..self.region_sums.len()).for_each(|r| {
self.region_sums[r] += other.region_sums[r];
});
self.total += other.total;
}
#[inline]
pub(crate) fn bucket_at_rank(&self, rank: u64) -> Option<(usize, u64)> {
let mut cumulative = 0u64;
let mut region_start = None;
for (r, &sum) in self.region_sums.iter().enumerate() {
let next = cumulative + sum;
if next >= rank {
region_start = Some(r * REGION_SIZE);
break;
}
cumulative = next;
}
let region_start = region_start?;
let region_end = (region_start + REGION_SIZE).min(self.buckets.len());
for i in region_start..region_end {
cumulative += self.buckets[i];
if cumulative >= rank {
let cumulative_before = cumulative - self.buckets[i];
return Some((i, cumulative_before));
}
}
None
}
pub(crate) fn clear(&mut self) {
self.buckets.fill(0);
self.region_sums.fill(0);
self.total = 0;
self.data = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_buckets() {
let slot: Slot<&str> = Slot::from_buckets(vec![1, 2, 3, 4], Some("tag"));
assert_eq!(slot.buckets, vec![1, 2, 3, 4]);
assert_eq!(slot.total(), 10);
assert_eq!(slot.data, Some("tag"));
let empty: Slot<()> = Slot::from_buckets(vec![0, 0, 0], None);
assert_eq!(empty.total(), 0);
}
#[test]
fn test_from_buckets_region_sums() {
let mut buckets = vec![0u64; 20];
buckets[0] = 5;
buckets[15] = 3; buckets[16] = 7;
let slot: Slot<()> = Slot::from_buckets(buckets, None);
assert_eq!(slot.region_sums[0], 8); assert_eq!(slot.region_sums[1], 7);
assert_eq!(slot.total(), 15);
}
#[test]
fn test_new() {
let slot: Slot<()> = Slot::new(10);
assert_eq!(slot.buckets.len(), 10);
assert_eq!(slot.total(), 0);
assert_eq!(slot.data, None);
assert_eq!(slot.region_sums.len(), 1); }
#[test]
fn test_new_region_count() {
let slot: Slot<()> = Slot::new(252);
assert_eq!(slot.region_sums.len(), 16); }
#[test]
fn test_record_n_and_count_at() {
let mut slot: Slot<()> = Slot::new(10);
slot.record_n(3, 5);
slot.record_n(3, 2);
slot.record_n(7, 1);
assert_eq!(slot.count_at(3), 7);
assert_eq!(slot.count_at(7), 1);
assert_eq!(slot.count_at(0), 0);
assert_eq!(slot.total(), 8);
assert_eq!(slot.region_sums[0], 8);
}
#[test]
fn test_record_n_across_regions() {
let mut slot: Slot<()> = Slot::new(32);
slot.record_n(0, 10); slot.record_n(15, 5); slot.record_n(16, 3);
assert_eq!(slot.region_sums[0], 15);
assert_eq!(slot.region_sums[1], 3);
assert_eq!(slot.total(), 18);
}
#[test]
fn test_total() {
let mut slot: Slot<()> = Slot::new(10);
slot.record_n(0, 3);
slot.record_n(5, 7);
assert_eq!(slot.total(), 10);
}
#[test]
fn test_add() {
let mut a: Slot<()> = Slot::new(4);
a.record_n(0, 10);
a.record_n(1, 20);
let mut b: Slot<()> = Slot::new(4);
b.record_n(1, 5);
b.record_n(3, 7);
a.add(&b);
assert_eq!(a.buckets, vec![10, 25, 0, 7]);
assert_eq!(a.total(), 42);
assert_eq!(a.region_sums[0], 42);
}
#[test]
fn test_subtract() {
let mut a: Slot<()> = Slot::new(4);
a.record_n(0, 10);
a.record_n(1, 20);
a.record_n(2, 30);
a.record_n(3, 40);
assert_eq!(a.total(), 100);
let mut b: Slot<()> = Slot::new(4);
b.record_n(0, 1);
b.record_n(1, 2);
b.record_n(2, 3);
b.record_n(3, 4);
a.subtract(&b);
assert_eq!(a.buckets, vec![9, 18, 27, 36]);
assert_eq!(a.total(), 90);
assert_eq!(a.region_sums[0], 90);
}
#[test]
fn test_bucket_at_rank() {
let mut slot: Slot<()> = Slot::new(4);
slot.record_n(0, 3);
slot.record_n(2, 5);
slot.record_n(3, 2);
assert_eq!(slot.bucket_at_rank(1), Some((0, 0)));
assert_eq!(slot.bucket_at_rank(3), Some((0, 0)));
assert_eq!(slot.bucket_at_rank(4), Some((2, 3)));
assert_eq!(slot.bucket_at_rank(8), Some((2, 3)));
assert_eq!(slot.bucket_at_rank(9), Some((3, 8)));
assert_eq!(slot.bucket_at_rank(10), Some((3, 8)));
assert_eq!(slot.bucket_at_rank(11), None);
assert_eq!(slot.bucket_at_rank(0), Some((0, 0)));
let empty: Slot<()> = Slot::new(4);
assert_eq!(empty.bucket_at_rank(1), None);
}
#[test]
fn test_bucket_at_rank_across_regions() {
let mut slot: Slot<()> = Slot::new(32);
slot.record_n(0, 10); slot.record_n(15, 5); slot.record_n(16, 3); slot.record_n(31, 7);
assert_eq!(slot.bucket_at_rank(1), Some((0, 0)));
assert_eq!(slot.bucket_at_rank(10), Some((0, 0)));
assert_eq!(slot.bucket_at_rank(11), Some((15, 10)));
assert_eq!(slot.bucket_at_rank(15), Some((15, 10)));
assert_eq!(slot.bucket_at_rank(16), Some((16, 15)));
assert_eq!(slot.bucket_at_rank(18), Some((16, 15)));
assert_eq!(slot.bucket_at_rank(19), Some((31, 18)));
assert_eq!(slot.bucket_at_rank(25), Some((31, 18)));
}
#[test]
fn test_clear() {
let mut slot: Slot<String> = Slot::new(10);
slot.record_n(0, 5);
slot.record_n(5, 10);
slot.data = Some("test".to_string());
slot.clear();
assert_eq!(slot.total(), 0);
assert!(slot.buckets.iter().all(|&c| c == 0));
assert!(slot.region_sums.iter().all(|&c| c == 0));
assert_eq!(slot.data, None);
}
}