use super::vec::FixedVec;
use crate::shim::atomic::{AtomicUsize, Ordering};
use core::fmt;
use core::ptr;
pub struct RingBufCore<T, const N: usize> {
buffer: FixedVec<T, N>,
capacity: usize,
mask: usize,
write_idx: AtomicUsize,
read_idx: AtomicUsize,
}
impl<T, const N: usize> RingBufCore<T, N> {
pub fn new(capacity: usize) -> Self {
let actual_capacity = round_to_power_of_two(capacity);
let mask = actual_capacity - 1;
let mut buffer = FixedVec::with_capacity(actual_capacity);
unsafe {
buffer.set_len(actual_capacity);
}
Self {
buffer,
capacity: actual_capacity,
mask,
write_idx: AtomicUsize::new(0),
read_idx: AtomicUsize::new(0),
}
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn mask(&self) -> usize {
self.mask
}
#[inline]
pub fn write_idx(&self) -> &AtomicUsize {
&self.write_idx
}
#[inline]
pub fn read_idx(&self) -> &AtomicUsize {
&self.read_idx
}
#[inline]
pub fn len(&self) -> usize {
let write = self.write_idx.load(Ordering::Acquire);
let read = self.read_idx.load(Ordering::Acquire);
write.wrapping_sub(read).min(self.capacity)
}
#[inline]
pub fn is_empty(&self) -> bool {
let write = self.write_idx.load(Ordering::Acquire);
let read = self.read_idx.load(Ordering::Acquire);
write == read
}
#[inline]
pub fn is_full(&self) -> bool {
let write = self.write_idx.load(Ordering::Acquire);
let read = self.read_idx.load(Ordering::Acquire);
write.wrapping_sub(read) >= self.capacity
}
#[inline]
pub unsafe fn write_at(&self, index: usize, value: T) {
unsafe {
let ptr = self.buffer.get_unchecked_ptr(index).cast::<T>() as *mut T;
ptr.write(value);
}
}
#[inline]
pub unsafe fn read_at(&self, index: usize) -> T {
unsafe {
let ptr = self.buffer.get_unchecked_ptr(index).cast::<T>();
ptr.read()
}
}
#[inline]
pub unsafe fn replace_at(&self, index: usize, value: T) -> T {
unsafe {
let ptr = self.buffer.get_unchecked_ptr(index).cast::<T>() as *mut T;
ptr::replace(ptr, value)
}
}
#[inline]
pub unsafe fn peek_at(&self, index: usize) -> &T {
unsafe {
let ptr = self.buffer.get_unchecked_ptr(index).cast::<T>();
&*ptr
}
}
#[inline]
pub unsafe fn buffer_ptr(&self) -> *const T {
self.buffer.as_ptr().cast::<T>()
}
#[inline]
pub unsafe fn buffer_ptr_at(&self, index: usize) -> *const T {
unsafe { self.buffer.get_unchecked_ptr(index).cast() }
}
}
impl<T: Copy, const N: usize> RingBufCore<T, N> {
pub unsafe fn copy_from_slice(&self, start_write: usize, values: &[T], count: usize) {
if count == 0 {
return;
}
unsafe {
let start_index = start_write & self.mask;
if count <= self.capacity - start_index {
let dst = self.buffer.get_unchecked_ptr(start_index).cast::<T>() as *mut T;
ptr::copy_nonoverlapping(values.as_ptr(), dst, count);
} else {
let first_part = self.capacity - start_index;
let second_part = count - first_part;
let dst1 = self.buffer.get_unchecked_ptr(start_index).cast::<T>() as *mut T;
ptr::copy_nonoverlapping(values.as_ptr(), dst1, first_part);
let dst2 = self.buffer.get_unchecked_ptr(0).cast::<T>() as *mut T;
ptr::copy_nonoverlapping(values.as_ptr().add(first_part), dst2, second_part);
}
}
}
pub unsafe fn copy_to_slice(&self, start_read: usize, dest: &mut [T], count: usize) {
if count == 0 {
return;
}
unsafe {
let start_index = start_read & self.mask;
if count <= self.capacity - start_index {
let src = self.buffer.get_unchecked_ptr(start_index).cast::<T>();
ptr::copy_nonoverlapping(src, dest.as_mut_ptr(), count);
} else {
let first_part = self.capacity - start_index;
let second_part = count - first_part;
let src1 = self.buffer.get_unchecked_ptr(start_index).cast::<T>();
ptr::copy_nonoverlapping(src1, dest.as_mut_ptr(), first_part);
let src2 = self.buffer.get_unchecked_ptr(0).cast::<T>();
ptr::copy_nonoverlapping(src2, dest.as_mut_ptr().add(first_part), second_part);
}
}
}
}
impl<T: Clone, const N: usize> Clone for RingBufCore<T, N> {
fn clone(&self) -> Self {
let capacity = self.capacity;
let mask = self.mask;
let read = self.read_idx.load(Ordering::Relaxed);
let write = self.write_idx.load(Ordering::Relaxed);
let mut new_buffer = FixedVec::with_capacity(capacity);
unsafe {
new_buffer.set_len(capacity);
let mut i = read;
while i != write {
let idx = i & mask;
let val = self.peek_at(idx);
let ptr = new_buffer.get_unchecked_mut_ptr(idx).cast::<T>();
ptr.write(val.clone());
i = i.wrapping_add(1);
}
}
Self {
buffer: new_buffer,
capacity,
mask,
write_idx: AtomicUsize::new(write),
read_idx: AtomicUsize::new(read),
}
}
}
impl<T: fmt::Debug, const N: usize> fmt::Debug for RingBufCore<T, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RingBufCore")
.field("capacity", &self.capacity)
.field("mask", &self.mask)
.field("write_idx", &self.write_idx.load(Ordering::Relaxed))
.field("read_idx", &self.read_idx.load(Ordering::Relaxed))
.finish()
}
}
unsafe impl<T: Send, const N: usize> Send for RingBufCore<T, N> {}
unsafe impl<T: Send, const N: usize> Sync for RingBufCore<T, N> {}
#[inline]
pub fn round_to_power_of_two(capacity: usize) -> usize {
if capacity == 0 {
1
} else {
capacity.next_power_of_two()
}
}
#[cfg(all(test, not(feature = "loom")))]
mod tests {
use super::*;
#[test]
fn test_round_to_power_of_two() {
assert_eq!(round_to_power_of_two(0), 1);
assert_eq!(round_to_power_of_two(1), 1);
assert_eq!(round_to_power_of_two(2), 2);
assert_eq!(round_to_power_of_two(3), 4);
assert_eq!(round_to_power_of_two(5), 8);
assert_eq!(round_to_power_of_two(8), 8);
assert_eq!(round_to_power_of_two(9), 16);
}
#[test]
fn test_core_basic() {
let core: RingBufCore<i32, 32> = RingBufCore::new(4);
assert_eq!(core.capacity(), 4);
assert_eq!(core.mask(), 3);
assert!(core.is_empty());
assert!(!core.is_full());
}
#[test]
fn test_core_write_read() {
let core: RingBufCore<i32, 32> = RingBufCore::new(4);
unsafe {
core.write_at(0, 42);
let value = core.read_at(0);
assert_eq!(value, 42);
}
}
#[test]
fn test_core_batch_copy_no_wrap() {
let core: RingBufCore<i32, 32> = RingBufCore::new(8);
let values = [1, 2, 3, 4];
unsafe {
core.copy_from_slice(0, &values, 4);
let mut dest = [0i32; 4];
core.copy_to_slice(0, &mut dest, 4);
assert_eq!(dest, [1, 2, 3, 4]);
}
}
#[test]
fn test_core_batch_copy_with_wrap() {
let core: RingBufCore<i32, 32> = RingBufCore::new(4);
let values = [1, 2, 3];
unsafe {
core.copy_from_slice(3, &values, 3);
let mut dest = [0i32; 3];
core.copy_to_slice(3, &mut dest, 3);
assert_eq!(dest, [1, 2, 3]);
}
}
}