use super::RealArray;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
pub trait RingBuf {
type Item;
fn new() -> Self;
fn with_capacity(cap: usize) -> Self;
fn capacity(&self) -> usize;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn can_push(&self) -> bool;
fn push(&mut self, item: Self::Item);
fn pop(&mut self) -> Self::Item;
}
pub struct ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
buffer: MaybeUninit<A>,
size: usize,
recv_idx: usize,
send_idx: usize,
_phantom: PhantomData<T>,
}
impl<T, A> core::fmt::Debug for ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
fn fmt(
&self,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error> {
f.debug_struct("ArrayBuf")
.field("size", &self.size)
.field("cap", &self.capacity())
.finish()
}
}
impl<T, A> ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
fn next_idx(&mut self, last_idx: usize) -> usize {
if last_idx + 1 == self.capacity() {
return 0;
}
last_idx + 1
}
}
impl<T, A> RingBuf for ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
type Item = T;
fn new() -> Self {
ArrayBuf {
buffer: MaybeUninit::uninit(),
send_idx: 0,
recv_idx: 0,
size: 0,
_phantom: PhantomData,
}
}
fn with_capacity(_cap: usize) -> Self {
Self::new()
}
#[inline]
fn capacity(&self) -> usize {
A::LEN
}
#[inline]
fn len(&self) -> usize {
self.size
}
#[inline]
fn can_push(&self) -> bool {
self.len() != self.capacity()
}
#[inline]
fn push(&mut self, value: Self::Item) {
assert!(self.can_push());
unsafe {
let arr_ptr = self.buffer.as_mut_ptr() as *mut T;
arr_ptr.add(self.send_idx).write(value);
}
self.send_idx = self.next_idx(self.send_idx);
self.size += 1;
}
#[inline]
fn pop(&mut self) -> Self::Item {
assert!(self.size > 0);
let val = unsafe {
let arr_ptr = self.buffer.as_mut_ptr() as *mut T;
arr_ptr.add(self.recv_idx).read()
};
self.recv_idx = self.next_idx(self.recv_idx);
self.size -= 1;
val
}
}
impl<T, A> Drop for ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
fn drop(&mut self) {
while self.size > 0 {
unsafe {
let arr_ptr = self.buffer.as_mut_ptr() as *mut T;
arr_ptr.add(self.recv_idx).drop_in_place();
}
self.recv_idx = self.next_idx(self.recv_idx);
self.size -= 1;
}
}
}
#[cfg(feature = "alloc")]
mod if_alloc {
use super::*;
use alloc::collections::VecDeque;
pub struct FixedHeapBuf<T> {
buffer: VecDeque<T>,
cap: usize,
}
impl<T> core::fmt::Debug for FixedHeapBuf<T> {
fn fmt(
&self,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error> {
f.debug_struct("FixedHeapBuf")
.field("size", &self.buffer.len())
.field("cap", &self.cap)
.finish()
}
}
impl<T> RingBuf for FixedHeapBuf<T> {
type Item = T;
fn new() -> Self {
FixedHeapBuf {
buffer: VecDeque::new(),
cap: 0,
}
}
fn with_capacity(cap: usize) -> Self {
FixedHeapBuf {
buffer: VecDeque::with_capacity(cap),
cap,
}
}
#[inline]
fn capacity(&self) -> usize {
self.cap
}
#[inline]
fn len(&self) -> usize {
self.buffer.len()
}
#[inline]
fn can_push(&self) -> bool {
self.buffer.len() != self.cap
}
#[inline]
fn push(&mut self, value: Self::Item) {
assert!(self.can_push());
self.buffer.push_back(value);
}
#[inline]
fn pop(&mut self) -> Self::Item {
assert!(self.buffer.len() > 0);
self.buffer.pop_front().unwrap()
}
}
pub struct GrowingHeapBuf<T> {
buffer: VecDeque<T>,
limit: usize,
}
impl<T> core::fmt::Debug for GrowingHeapBuf<T> {
fn fmt(
&self,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error> {
f.debug_struct("GrowingHeapBuf")
.field("size", &self.buffer.len())
.field("limit", &self.limit)
.finish()
}
}
impl<T> RingBuf for GrowingHeapBuf<T> {
type Item = T;
fn new() -> Self {
GrowingHeapBuf {
buffer: VecDeque::new(),
limit: 0,
}
}
fn with_capacity(limit: usize) -> Self {
GrowingHeapBuf {
buffer: VecDeque::new(),
limit,
}
}
#[inline]
fn capacity(&self) -> usize {
self.limit
}
#[inline]
fn len(&self) -> usize {
self.buffer.len()
}
#[inline]
fn can_push(&self) -> bool {
self.buffer.len() != self.limit
}
#[inline]
fn push(&mut self, value: Self::Item) {
debug_assert!(self.can_push());
self.buffer.push_back(value);
}
#[inline]
fn pop(&mut self) -> Self::Item {
debug_assert!(self.buffer.len() > 0);
self.buffer.pop_front().unwrap()
}
}
}
#[cfg(feature = "alloc")]
pub use if_alloc::*;
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
use crate::buffer::ring_buffer::if_alloc::FixedHeapBuf;
fn test_ring_buf<Buf: RingBuf<Item = u32>>(mut buf: Buf) {
assert_eq!(5, buf.capacity());
assert_eq!(0, buf.len());
assert_eq!(true, buf.is_empty());
assert_eq!(true, buf.can_push());
buf.push(1);
buf.push(2);
buf.push(3);
assert_eq!(5, buf.capacity());
assert_eq!(3, buf.len());
assert_eq!(false, buf.is_empty());
assert_eq!(true, buf.can_push());
assert_eq!(1, buf.pop());
assert_eq!(2, buf.pop());
assert_eq!(1, buf.len());
assert_eq!(false, buf.is_empty());
assert_eq!(3, buf.pop());
assert_eq!(0, buf.len());
assert_eq!(true, buf.is_empty());
for (i, val) in [4, 5, 6, 7, 8].iter().enumerate() {
buf.push(*val);
assert_eq!(i + 1, buf.len());
assert_eq!(i != 4, buf.can_push());
assert_eq!(false, buf.is_empty());
}
for (i, val) in [4, 5, 6, 7, 8].iter().enumerate() {
assert_eq!(*val, buf.pop());
assert_eq!(4 - i, buf.len());
assert_eq!(true, buf.can_push());
assert_eq!(i == 4, buf.is_empty());
}
}
#[test]
fn test_array_ring_buf() {
let buf = ArrayBuf::<u32, [u32; 5]>::new();
test_ring_buf(buf);
}
#[test]
fn test_heap_ring_buf() {
let buf = FixedHeapBuf::<u32>::with_capacity(5);
test_ring_buf(buf);
}
#[test]
fn test_growing_ring_buf() {
let buf = GrowingHeapBuf::<u32>::with_capacity(5);
test_ring_buf(buf);
}
}