use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::fmt;
use crate::math::Transcendental;
mod buffer_trait;
mod delay;
mod fan;
mod pipe;
mod registry;
mod ring;
mod storage;
mod tape;
pub use buffer_trait::{Buffer, FixedBuffer, HeapBuffer};
pub use delay::DelayLine;
pub use fan::{FanInBuffer, FanOutBuffer};
pub use pipe::PipeBuffer;
pub use registry::BufferRegistry;
pub use ring::RingBuffer;
pub use storage::{AtomicCell, AtomicCellError};
pub use tape::TapeLoop;
pub const CACHE_LINE_SIZE: usize = 64;
pub const DEFAULT_BUFFER_SIZE: usize = 1024;
pub const MAX_BUFFER_SIZE: usize = 65536;
pub const MIN_BUFFER_SIZE: usize = 16;
#[repr(align(64))]
pub struct AtomicStats {
writes: AtomicU64,
reads: AtomicU64,
underflows: AtomicU64,
overflows: AtomicU64,
peak_fill: AtomicUsize,
}
impl AtomicStats {
pub const fn new() -> Self {
Self {
writes: AtomicU64::new(0),
reads: AtomicU64::new(0),
underflows: AtomicU64::new(0),
overflows: AtomicU64::new(0),
peak_fill: AtomicUsize::new(0),
}
}
#[inline(always)]
pub fn record_write(&self) {
self.writes.fetch_add(1, Ordering::Relaxed);
}
#[inline(always)]
pub fn record_read(&self) {
self.reads.fetch_add(1, Ordering::Relaxed);
}
#[inline(always)]
pub fn record_underflow(&self) {
self.underflows.fetch_add(1, Ordering::Relaxed);
}
#[inline(always)]
pub fn record_overflow(&self) {
self.overflows.fetch_add(1, Ordering::Relaxed);
}
#[inline(always)]
pub fn update_peak(&self, current_fill: usize) {
let mut peak = self.peak_fill.load(Ordering::Relaxed);
while current_fill > peak {
match self.peak_fill.compare_exchange_weak(
peak,
current_fill,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(new_peak) => peak = new_peak,
}
}
}
pub fn snapshot(&self) -> BufferStats {
BufferStats {
writes: self.writes.load(Ordering::Relaxed),
reads: self.reads.load(Ordering::Relaxed),
underflows: self.underflows.load(Ordering::Relaxed),
overflows: self.overflows.load(Ordering::Relaxed),
fill_level: 0.0, peak_fill: self.peak_fill.load(Ordering::Relaxed) as f32 / 1000.0,
}
}
pub fn reset(&self) {
self.writes.store(0, Ordering::Relaxed);
self.reads.store(0, Ordering::Relaxed);
self.underflows.store(0, Ordering::Relaxed);
self.overflows.store(0, Ordering::Relaxed);
self.peak_fill.store(0, Ordering::Relaxed);
}
}
impl Default for AtomicStats {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for AtomicStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AtomicStats")
.field("writes", &self.writes.load(Ordering::Relaxed))
.field("reads", &self.reads.load(Ordering::Relaxed))
.field("underflows", &self.underflows.load(Ordering::Relaxed))
.field("overflows", &self.overflows.load(Ordering::Relaxed))
.field(
"peak_fill",
&(self.peak_fill.load(Ordering::Relaxed) as f32 / 1000.0),
)
.finish()
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct BufferStats {
pub writes: u64,
pub reads: u64,
pub underflows: u64,
pub overflows: u64,
pub fill_level: f32,
pub peak_fill: f32,
}
impl BufferStats {
pub fn new() -> Self {
Self::default()
}
pub fn success_rate(&self) -> f32 {
if self.writes == 0 {
1.0
} else {
self.reads as f32 / self.writes as f32
}
}
pub fn error_rate(&self) -> f32 {
let total = self.writes + self.reads;
if total == 0 {
0.0
} else {
(self.underflows + self.overflows) as f32 / total as f32
}
}
}
pub mod utils {
use super::*;
#[inline(always)]
pub fn copy_safe<T: Copy>(src: &[T], dst: &mut [T]) -> usize {
let len = src.len().min(dst.len());
dst[..len].copy_from_slice(&src[..len]);
len
}
#[inline(always)]
pub fn zero_fill<T: Default + Copy>(slice: &mut [T]) {
for item in slice.iter_mut() {
*item = T::default();
}
}
#[inline(always)]
pub fn mix_with_gain<T>(src: &[T], dst: &mut [T], gain: T)
where
T: Transcendental + core::ops::Mul<Output = T> + core::ops::Add<Output = T>,
{
let len = src.len().min(dst.len());
for i in 0..len {
dst[i] += src[i] * gain;
}
}
#[inline(always)]
pub fn apply_gain<T>(slice: &mut [T], gain: T)
where
T: Transcendental + core::ops::Mul<Output = T>,
{
for item in slice.iter_mut() {
*item *= gain;
}
}
#[inline(always)]
pub fn calculate_rms<T>(slice: &[T]) -> f64
where
T: Transcendental + core::ops::Mul<Output = T> + core::iter::Sum,
{
let sum_squares: T = slice.iter().map(|&x| x * x).sum();
let sum_f64: f64 = sum_squares.to_f64();
(sum_f64 / slice.len() as f64).sqrt()
}
#[inline(always)]
pub fn calculate_peak<T>(slice: &[T]) -> f64
where
T: Transcendental + PartialOrd,
{
slice.iter().map(|&x| x.to_f64().abs()).fold(0.0, f64::max)
}
}
pub mod prelude {
pub use super::{
utils,
AtomicCell,
AtomicCellError,
Buffer,
BufferError,
BufferResult,
BufferStats,
DelayLine,
FanInBuffer,
FanOutBuffer,
PipeBuffer,
RingBuffer,
CACHE_LINE_SIZE,
DEFAULT_BUFFER_SIZE,
MAX_BUFFER_SIZE,
MIN_BUFFER_SIZE,
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum BufferError {
#[error("Buffer is empty")]
Empty,
#[error("Buffer is full")]
Full,
#[error("Invalid index: {0}")]
InvalidIndex(usize),
#[error("Buffer is disconnected")]
Disconnected,
#[error("Operation would block")]
WouldBlock,
#[error("Buffer overflow")]
Overflow,
#[error("Buffer underflow")]
Underflow,
#[error("Invalid buffer size: {0}")]
InvalidSize(usize),
}
#[allow(unsafe_code)]
pub fn array_from_fn<T, const N: usize>(mut f: impl FnMut(usize) -> T) -> [T; N] {
use core::mem::MaybeUninit;
let mut array: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
for (i, item) in array.iter_mut().enumerate() {
*item = MaybeUninit::new(f(i));
}
unsafe { core::mem::transmute_copy(&array) }
}
pub type BufferResult<T> = Result<T, BufferError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atomic_stats() {
let stats = AtomicStats::new();
stats.record_write();
stats.record_read();
stats.record_underflow();
stats.record_overflow();
stats.update_peak(500);
let snapshot = stats.snapshot();
assert_eq!(snapshot.writes, 1);
assert_eq!(snapshot.reads, 1);
assert_eq!(snapshot.underflows, 1);
assert_eq!(snapshot.overflows, 1);
assert!((snapshot.peak_fill - 0.5).abs() < 0.001);
}
#[test]
fn test_buffer_stats() {
let stats = BufferStats {
writes: 100,
reads: 95,
underflows: 3,
overflows: 2,
fill_level: 0.5,
peak_fill: 0.8,
};
assert!((stats.success_rate() - 0.95).abs() < 0.001);
assert!((stats.error_rate() - 0.02564).abs() < 0.001);
}
#[test]
fn test_utils() {
let mut dst = [0.0; 4];
let src = [1.0, 2.0, 3.0];
let copied = utils::copy_safe(&src, &mut dst);
assert_eq!(copied, 3);
assert_eq!(dst[0], 1.0);
assert_eq!(dst[1], 2.0);
assert_eq!(dst[2], 3.0);
utils::zero_fill(&mut dst[..3]);
assert_eq!(dst[0], 0.0);
assert_eq!(dst[1], 0.0);
assert_eq!(dst[2], 0.0);
let mut mix_dst = [1.0, 1.0, 1.0];
utils::mix_with_gain(&[2.0, 2.0, 2.0], &mut mix_dst, 0.5);
assert_eq!(mix_dst[0], 2.0);
let rms = utils::calculate_rms(&[1.0, -1.0, 1.0, -1.0]);
assert!((rms - 1.0).abs() < 1e-6);
let peak = utils::calculate_peak(&[0.5, -0.8, 0.3, -0.9]);
assert!((peak - 0.9).abs() < 1e-6);
}
#[test]
fn test_constants() {
assert_eq!(CACHE_LINE_SIZE, 64);
assert!(MAX_BUFFER_SIZE > MIN_BUFFER_SIZE);
assert!(DEFAULT_BUFFER_SIZE >= MIN_BUFFER_SIZE);
assert!(DEFAULT_BUFFER_SIZE <= MAX_BUFFER_SIZE);
}
#[test]
fn test_buffer_error_display() {
assert_eq!(format!("{}", BufferError::Empty), "Buffer is empty");
assert_eq!(format!("{}", BufferError::Full), "Buffer is full");
assert_eq!(
format!("{}", BufferError::InvalidIndex(5)),
"Invalid index: 5"
);
}
#[test]
fn test_atomic_cell_basic() {
let cell = AtomicCell::new(42);
assert_eq!(cell.load(), 42);
cell.store(100);
assert_eq!(cell.load(), 100);
}
#[test]
fn test_atomic_cell_try_new() {
let cell = AtomicCell::try_new(42).unwrap();
assert_eq!(cell.load(), 42);
}
#[test]
fn test_atomic_cell_default() {
let cell = AtomicCell::<i32>::default();
assert_eq!(cell.load(), 0);
}
}