use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct IoRingBuffer {
buffer: UnsafeCell<Vec<f32>>,
write_index: AtomicUsize,
read_index: AtomicUsize,
mask: usize,
}
unsafe impl Send for IoRingBuffer {}
unsafe impl Sync for IoRingBuffer {}
impl IoRingBuffer {
pub fn new(capacity: usize) -> Self {
let cap = capacity.next_power_of_two();
Self {
buffer: UnsafeCell::new(vec![0.0f32; cap]),
write_index: AtomicUsize::new(0),
read_index: AtomicUsize::new(0),
mask: cap - 1,
}
}
pub fn capacity(&self) -> usize {
unsafe { (*self.buffer.get()).len() }
}
pub fn len(&self) -> usize {
let w = self.write_index.load(Ordering::Acquire);
let r = self.read_index.load(Ordering::Acquire);
w.wrapping_sub(r) & self.mask
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn write(&self, data: &[f32]) -> usize {
let cap = unsafe { (*self.buffer.get()).len() };
let w = self.write_index.load(Ordering::Relaxed);
let r = self.read_index.load(Ordering::Acquire);
let available = cap - (w.wrapping_sub(r) & self.mask) - 1;
let to_write = data.len().min(available);
let buf = unsafe { &mut *self.buffer.get() };
for i in 0..to_write {
let idx = (w + i) & self.mask;
buf[idx] = data[i];
}
self.write_index
.store(w.wrapping_add(to_write), Ordering::Release);
to_write
}
pub fn read(&self, data: &mut [f32]) -> usize {
let r = self.read_index.load(Ordering::Relaxed);
let w = self.write_index.load(Ordering::Acquire);
let available = w.wrapping_sub(r) & self.mask;
let to_read = data.len().min(available);
let buf = unsafe { &*self.buffer.get() };
for i in 0..to_read {
let idx = (r + i) & self.mask;
data[i] = buf[idx];
}
self.read_index
.store(r.wrapping_add(to_read), Ordering::Release);
to_read
}
pub fn clear(&self) {
self.read_index
.store(self.write_index.load(Ordering::Acquire), Ordering::Release);
}
pub fn clear_with_zeros(&self) {
let buf = unsafe { &mut *self.buffer.get() };
buf.fill(0.0);
self.write_index.store(0, Ordering::Release);
self.read_index.store(0, Ordering::Release);
}
}