use crate::error::{EmbeddedError, Result};
use core::mem::MaybeUninit;
pub struct FixedBuffer<T, const N: usize> {
data: [MaybeUninit<T>; N],
len: usize,
}
impl<T, const N: usize> FixedBuffer<T, N> {
pub const fn new() -> Self {
Self {
data: unsafe { MaybeUninit::uninit().assume_init() },
len: 0,
}
}
pub const fn capacity(&self) -> usize {
N
}
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub const fn is_full(&self) -> bool {
self.len >= N
}
pub fn push(&mut self, item: T) -> Result<()> {
if self.is_full() {
return Err(EmbeddedError::BufferTooSmall {
required: 1,
available: 0,
});
}
self.data[self.len].write(item);
self.len += 1;
Ok(())
}
pub fn pop(&mut self) -> Result<T> {
if self.is_empty() {
return Err(EmbeddedError::InvalidParameter);
}
self.len -= 1;
let item = unsafe { self.data[self.len].assume_init_read() };
Ok(item)
}
pub fn get(&self, index: usize) -> Result<&T> {
if index >= self.len {
return Err(EmbeddedError::OutOfBounds {
index,
max: self.len.saturating_sub(1),
});
}
let item = unsafe { self.data[index].assume_init_ref() };
Ok(item)
}
pub fn get_mut(&mut self, index: usize) -> Result<&mut T> {
if index >= self.len {
return Err(EmbeddedError::OutOfBounds {
index,
max: self.len.saturating_sub(1),
});
}
let item = unsafe { self.data[index].assume_init_mut() };
Ok(item)
}
pub fn clear(&mut self) {
for i in 0..self.len {
unsafe {
self.data[i].assume_init_drop();
}
}
self.len = 0;
}
pub fn as_slice(&self) -> &[T] {
unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.len) }
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { core::slice::from_raw_parts_mut(self.data.as_mut_ptr().cast(), self.len) }
}
pub fn extend_from_slice(&mut self, items: &[T]) -> Result<()>
where
T: Copy,
{
if self.len + items.len() > N {
return Err(EmbeddedError::BufferTooSmall {
required: items.len(),
available: N - self.len,
});
}
for item in items {
self.data[self.len].write(*item);
self.len += 1;
}
Ok(())
}
}
impl<T, const N: usize> Default for FixedBuffer<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> Drop for FixedBuffer<T, N> {
fn drop(&mut self) {
self.clear();
}
}
pub struct RingBuffer<T: Copy, const N: usize> {
data: [T; N],
read_pos: usize,
write_pos: usize,
full: bool,
}
impl<T: Copy, const N: usize> RingBuffer<T, N> {
pub fn new(default: T) -> Self {
Self {
data: [default; N],
read_pos: 0,
write_pos: 0,
full: false,
}
}
pub const fn capacity(&self) -> usize {
N
}
pub fn len(&self) -> usize {
if self.full {
N
} else if self.write_pos >= self.read_pos {
self.write_pos - self.read_pos
} else {
N - self.read_pos + self.write_pos
}
}
pub fn is_empty(&self) -> bool {
!self.full && self.read_pos == self.write_pos
}
pub const fn is_full(&self) -> bool {
self.full
}
pub fn write(&mut self, item: T) -> Result<()> {
if self.full {
return Err(EmbeddedError::BufferTooSmall {
required: 1,
available: 0,
});
}
self.data[self.write_pos] = item;
self.write_pos = (self.write_pos + 1) % N;
if self.write_pos == self.read_pos {
self.full = true;
}
Ok(())
}
pub fn read(&mut self) -> Result<T> {
if self.is_empty() {
return Err(EmbeddedError::InvalidParameter);
}
let item = self.data[self.read_pos];
self.read_pos = (self.read_pos + 1) % N;
self.full = false;
Ok(item)
}
pub fn peek(&self) -> Result<T> {
if self.is_empty() {
return Err(EmbeddedError::InvalidParameter);
}
Ok(self.data[self.read_pos])
}
pub fn clear(&mut self) {
self.read_pos = 0;
self.write_pos = 0;
self.full = false;
}
}
#[repr(C, align(64))]
pub struct AlignedBuffer<const N: usize> {
data: [u8; N],
}
impl<const N: usize> AlignedBuffer<N> {
pub const fn new() -> Self {
Self { data: [0u8; N] }
}
pub fn as_slice(&self) -> &[u8] {
&self.data
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.data
}
pub fn as_ptr(&self) -> *const u8 {
self.data.as_ptr()
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.data.as_mut_ptr()
}
pub const fn alignment(&self) -> usize {
64
}
pub fn verify_alignment(&self) -> bool {
self.as_ptr() as usize % 64 == 0
}
}
impl<const N: usize> Default for AlignedBuffer<N> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_buffer() {
let mut buffer = FixedBuffer::<u32, 8>::new();
assert_eq!(buffer.len(), 0);
assert!(buffer.is_empty());
buffer.push(1).expect("push failed");
buffer.push(2).expect("push failed");
assert_eq!(buffer.len(), 2);
assert_eq!(*buffer.get(0).expect("get failed"), 1);
assert_eq!(*buffer.get(1).expect("get failed"), 2);
let item = buffer.pop().expect("pop failed");
assert_eq!(item, 2);
assert_eq!(buffer.len(), 1);
}
#[test]
fn test_fixed_buffer_overflow() {
let mut buffer = FixedBuffer::<u32, 2>::new();
buffer.push(1).expect("push failed");
buffer.push(2).expect("push failed");
let result = buffer.push(3);
assert!(matches!(result, Err(EmbeddedError::BufferTooSmall { .. })));
}
#[test]
fn test_ring_buffer() {
let mut buffer = RingBuffer::<u32, 4>::new(0);
assert!(buffer.is_empty());
buffer.write(1).expect("write failed");
buffer.write(2).expect("write failed");
assert_eq!(buffer.len(), 2);
let item = buffer.read().expect("read failed");
assert_eq!(item, 1);
buffer.write(3).expect("write failed");
buffer.write(4).expect("write failed");
buffer.write(5).expect("write failed");
assert!(buffer.is_full());
assert!(buffer.write(6).is_err());
}
#[test]
fn test_aligned_buffer() {
let buffer = AlignedBuffer::<256>::new();
assert_eq!(buffer.alignment(), 64);
assert!(buffer.verify_alignment());
}
}