pub struct BoolWindow {
bits: *mut u64,
words: usize,
capacity: usize,
head: usize,
count: u64,
failures: u32,
}
unsafe impl Send for BoolWindow {}
impl BoolWindow {
#[inline]
fn ring(&self) -> &[u64] {
unsafe { core::slice::from_raw_parts(self.bits, self.words) }
}
#[inline]
fn ring_mut(&mut self) -> &mut [u64] {
unsafe { core::slice::from_raw_parts_mut(self.bits, self.words) }
}
#[inline]
pub fn new(sample_count: usize) -> Result<Self, crate::ConfigError> {
if sample_count == 0 {
return Err(crate::ConfigError::Invalid(
"BoolWindow capacity must be > 0",
));
}
let words = sample_count.div_ceil(64);
let mut vec = core::mem::ManuallyDrop::new(alloc::vec![0u64; words]);
let bits = vec.as_mut_ptr();
Ok(Self {
bits,
words,
capacity: sample_count,
head: 0,
count: 0,
failures: 0,
})
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn record(&mut self, success: bool) {
let head = self.head;
let capacity = self.capacity;
let count = self.count;
let word = head / 64;
let bit = head % 64;
let mask = 1u64 << bit;
let bits = unsafe { core::slice::from_raw_parts_mut(self.bits, self.words) };
let mut failures = self.failures;
let was_failure = ((bits[word] >> bit) & 1) as u32;
if count >= capacity as u64 {
failures -= was_failure;
}
let fail_bit = (!success) as u64;
bits[word] = (bits[word] & !mask) | (fail_bit << bit);
failures += fail_bit as u32;
self.failures = failures;
self.head = (head + 1) % capacity;
if count < capacity as u64 {
self.count = count + 1;
}
}
#[inline]
#[must_use]
pub fn failure_rate(&self) -> f64 {
if self.count == 0 {
0.0
} else {
self.failures as f64 / self.count as f64
}
}
#[inline]
#[must_use]
pub fn success_rate(&self) -> f64 {
1.0 - self.failure_rate()
}
#[inline]
#[must_use]
pub fn failures(&self) -> u32 {
self.failures
}
#[inline]
#[must_use]
pub fn count(&self) -> u64 {
self.count
}
#[inline]
#[must_use]
pub fn is_full(&self) -> bool {
self.count >= self.capacity as u64
}
#[inline]
pub fn reset(&mut self) {
self.ring_mut().fill(0);
self.head = 0;
self.count = 0;
self.failures = 0;
}
}
impl Drop for BoolWindow {
fn drop(&mut self) {
unsafe {
let _ = alloc::vec::Vec::from_raw_parts(self.bits, 0, self.words);
}
}
}
impl Clone for BoolWindow {
fn clone(&self) -> Self {
let mut vec = alloc::vec![0u64; self.words];
vec.copy_from_slice(self.ring());
let mut cloned = core::mem::ManuallyDrop::new(vec);
let bits = cloned.as_mut_ptr();
Self {
bits,
words: self.words,
capacity: self.capacity,
head: self.head,
count: self.count,
failures: self.failures,
}
}
}
impl core::fmt::Debug for BoolWindow {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BoolWindow")
.field("capacity", &self.capacity)
.field("count", &self.count)
.field("failures", &self.failures)
.finish()
}
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use super::*;
#[test]
fn empty() {
let bw = BoolWindow::new(64).unwrap();
assert_eq!(bw.count(), 0);
assert_eq!(bw.capacity(), 64);
assert_eq!(bw.failure_rate(), 0.0);
assert_eq!(bw.success_rate(), 1.0);
}
#[test]
fn all_success() {
let mut bw = BoolWindow::new(64).unwrap();
for _ in 0..64 {
bw.record(true);
}
assert_eq!(bw.failure_rate(), 0.0);
assert_eq!(bw.failures(), 0);
}
#[test]
fn all_failure() {
let mut bw = BoolWindow::new(64).unwrap();
for _ in 0..64 {
bw.record(false);
}
assert_eq!(bw.failure_rate(), 1.0);
assert_eq!(bw.failures(), 64);
}
#[test]
fn half_and_half() {
let mut bw = BoolWindow::new(64).unwrap();
for i in 0..64 {
bw.record(i % 2 == 0);
}
assert!((bw.failure_rate() - 0.5).abs() < 1e-10);
}
#[test]
fn window_rolls() {
let mut bw = BoolWindow::new(64).unwrap();
for _ in 0..64 {
bw.record(false);
}
assert_eq!(bw.failures(), 64);
for _ in 0..64 {
bw.record(true);
}
assert_eq!(bw.failures(), 0);
}
#[test]
fn arbitrary_size() {
let mut bw = BoolWindow::new(100).unwrap();
assert_eq!(bw.capacity(), 100);
for _ in 0..100 {
bw.record(false);
}
assert_eq!(bw.failures(), 100);
assert!(bw.is_full());
for _ in 0..100 {
bw.record(true);
}
assert_eq!(bw.failures(), 0);
}
#[test]
fn priming() {
let mut bw = BoolWindow::new(64).unwrap();
bw.record(false);
assert_eq!(bw.count(), 1);
assert!(!bw.is_full());
assert_eq!(bw.failure_rate(), 1.0);
}
#[test]
fn reset() {
let mut bw = BoolWindow::new(64).unwrap();
for _ in 0..64 {
bw.record(false);
}
bw.reset();
assert_eq!(bw.count(), 0);
assert_eq!(bw.failures(), 0);
}
#[test]
fn rejects_zero_capacity() {
assert!(matches!(
BoolWindow::new(0),
Err(crate::ConfigError::Invalid(_))
));
}
}