use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use rtrb::{Consumer, Producer, RingBuffer};
pub fn raw_ring(capacity_samples: usize) -> (RawProducer, RawConsumer) {
let cap = capacity_samples.max(1);
let (prod, cons) = RingBuffer::<f32>::new(cap);
let overflow = Arc::new(AtomicU64::new(0));
(
RawProducer {
inner: prod,
overflow: overflow.clone(),
},
RawConsumer {
inner: cons,
overflow,
},
)
}
pub struct RawProducer {
inner: Producer<f32>,
overflow: Arc<AtomicU64>,
}
impl RawProducer {
pub fn push_slice(&mut self, samples: &[f32]) -> usize {
if samples.is_empty() {
return 0;
}
let free = self.inner.slots();
let writable = free.min(samples.len());
if writable > 0 {
if let Ok(mut chunk) = self.inner.write_chunk_uninit(writable) {
let (a, b) = chunk.as_mut_slices();
let (head, tail) = samples.split_at(a.len().min(samples.len()));
for (dst, &src) in a.iter_mut().zip(head.iter()) {
dst.write(src);
}
let tail = &tail[..b.len().min(tail.len())];
for (dst, &src) in b.iter_mut().zip(tail.iter()) {
dst.write(src);
}
unsafe { chunk.commit_all() };
}
}
let dropped = samples.len() - writable;
if dropped > 0 {
self.overflow.fetch_add(dropped as u64, Ordering::Relaxed);
}
writable
}
pub fn overflow_count(&self) -> u64 {
self.overflow.load(Ordering::Relaxed)
}
}
pub struct RawConsumer {
inner: Consumer<f32>,
overflow: Arc<AtomicU64>,
}
impl RawConsumer {
pub fn pop_slice(&mut self, dst: &mut [f32]) -> usize {
let avail = self.inner.slots();
let n = avail.min(dst.len());
if n == 0 {
return 0;
}
if let Ok(chunk) = self.inner.read_chunk(n) {
let (a, b) = chunk.as_slices();
let (alen, blen) = (a.len(), b.len());
dst[..alen].copy_from_slice(a);
dst[alen..alen + blen].copy_from_slice(b);
chunk.commit_all();
alen + blen
} else {
0
}
}
pub fn pop(&mut self) -> Option<f32> {
self.inner.pop().ok()
}
pub fn available(&self) -> usize {
self.inner.slots()
}
pub fn overflow_count(&self) -> u64 {
self.overflow.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_and_pop_roundtrip() {
let (mut p, mut c) = raw_ring(16);
let n = p.push_slice(&[1.0, 2.0, 3.0, 4.0]);
assert_eq!(n, 4);
let mut out = [0.0f32; 4];
let got = c.pop_slice(&mut out);
assert_eq!(got, 4);
assert_eq!(out, [1.0, 2.0, 3.0, 4.0]);
assert_eq!(p.overflow_count(), 0);
}
#[test]
fn overflow_counts_dropped_and_never_blocks() {
let (mut p, mut c) = raw_ring(4);
let written = p.push_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(written, 4);
assert_eq!(p.overflow_count(), 2);
assert_eq!(c.overflow_count(), 2);
let mut out = [0.0f32; 8];
let got = c.pop_slice(&mut out);
assert_eq!(got, 4);
assert_eq!(&out[..4], &[1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn wraps_around() {
let (mut p, mut c) = raw_ring(4);
p.push_slice(&[1.0, 2.0, 3.0]);
let mut out = [0.0f32; 2];
c.pop_slice(&mut out); assert_eq!(out, [1.0, 2.0]);
let w = p.push_slice(&[4.0, 5.0, 6.0]);
assert_eq!(w, 3);
let mut out2 = [0.0f32; 4];
let got = c.pop_slice(&mut out2);
assert_eq!(got, 4);
assert_eq!(out2, [3.0, 4.0, 5.0, 6.0]);
}
#[test]
fn push_empty_is_noop() {
let (mut p, _c) = raw_ring(4);
assert_eq!(p.push_slice(&[]), 0);
assert_eq!(p.overflow_count(), 0);
}
#[test]
fn full_ring_drops_entire_push() {
let (mut p, _c) = raw_ring(4);
assert_eq!(p.push_slice(&[1.0, 2.0, 3.0, 4.0]), 4); let w = p.push_slice(&[5.0; 5]);
assert_eq!(w, 0, "満杯なら 1 つも書けない");
assert_eq!(
p.overflow_count(),
5,
"全 5 サンプルが overflow に計上される"
);
}
#[test]
fn pop_slice_respects_available_and_dst_len() {
let (mut p, mut c) = raw_ring(8);
p.push_slice(&[1.0, 2.0, 3.0]);
assert_eq!(c.available(), 3);
let mut big = [0.0f32; 16];
assert_eq!(c.pop_slice(&mut big), 3);
assert_eq!(&big[..3], &[1.0, 2.0, 3.0]);
assert_eq!(c.available(), 0);
assert_eq!(c.pop_slice(&mut big), 0);
}
#[test]
fn overflow_counter_exceeds_u32_max() {
let (mut p, _c) = raw_ring(1);
assert_eq!(p.push_slice(&[0.0]), 1);
let big = vec![0.0f32; 1000];
let over_u32 = u64::from(u32::MAX) + 2_000;
let mut total_dropped = 0u64;
while total_dropped < over_u32 {
let w = p.push_slice(&big);
assert_eq!(w, 0, "満杯なので 1 つも書けない");
total_dropped += big.len() as u64;
}
assert!(
p.overflow_count() > u64::from(u32::MAX),
"overflow は u32::MAX を超えて積み上がる: {}",
p.overflow_count()
);
}
#[test]
fn single_pop_is_fifo_then_none() {
let (mut p, mut c) = raw_ring(4);
p.push_slice(&[10.0, 20.0]);
assert_eq!(c.pop(), Some(10.0));
assert_eq!(c.pop(), Some(20.0));
assert_eq!(c.pop(), None);
}
#[test]
fn zero_capacity_is_clamped_to_one() {
let (mut p, mut c) = raw_ring(0);
assert_eq!(
p.push_slice(&[7.0, 8.0]),
1,
"容量 1 に丸められ 1 サンプルだけ入る"
);
assert_eq!(p.overflow_count(), 1);
assert_eq!(c.pop(), Some(7.0));
}
#[test]
fn overflow_count_is_shared_between_ends() {
let (mut p, c) = raw_ring(2);
p.push_slice(&[1.0, 2.0, 3.0, 4.0]); assert_eq!(p.overflow_count(), 2);
assert_eq!(
c.overflow_count(),
2,
"consumer 側も同じ overflow を観測する"
);
}
}