use std::ops::Deref;
use std::mem::MaybeUninit;
use std::fmt::Debug;
pub struct Buffer<T: Copy, const N: usize> {
buffer: [MaybeUninit<T>; N],
len: usize
}
impl<T: Copy, const N: usize> Buffer<T, N> {
#[inline(always)]
pub fn new() -> Self {
Self { buffer: [MaybeUninit::uninit(); N], len: 0 }
}
#[inline(always)]
pub fn push(&mut self, item: T) {
if self.len >= N {
panic!("buffer overflow: capacity {} reached", N)
}
self.buffer[self.len].write(item);
self.len += 1;
}
#[inline(always)]
pub unsafe fn push_unchecked(&mut self, item: T) {
debug_assert!(self.len < N, "buffer overflow: capacity {} reached", N);
unsafe {
self.buffer.get_unchecked_mut(self.len)
}.write(item);
self.len += 1;
}
#[inline(always)]
pub fn pop(&mut self) -> Option<T> {
if self.len > 0 {
self.len -= 1;
Some(unsafe {
self.buffer[self.len].assume_init()
})
} else {
None
}
}
#[inline(always)]
pub unsafe fn pop_unchecked(&mut self) -> T {
debug_assert!(self.len > 0, "buffer is empty");
self.len -= 1;
unsafe { self.buffer.get_unchecked(self.len).assume_init() }
}
#[inline(always)]
pub fn clear(&mut self) {
self.len = 0;
}
#[inline(always)]
pub fn as_slice(&self) -> &[T] {
unsafe {
let arr_ptr: *const T = self.buffer.as_ptr() as *const T;
std::slice::from_raw_parts(arr_ptr, self.len)
}
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
pub fn is_full(&self) -> bool {
self.len == N
}
#[inline(always)]
pub fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub fn capacity(&self) -> usize {
N
}
}
impl<T: Copy, const N: usize> Deref for Buffer<T, N> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T: Copy + Debug, const N: usize> Debug for Buffer<T, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.as_slice()).finish()
}
}
#[macro_export]
macro_rules! buf {
($cap:expr; $($element:expr),*) => {
{
let mut buffer = Buffer::<_, $cap>::new();
$(
buffer.push($element);
)*
buffer
}
};
($($element:expr),*) => {
{
let mut buffer = Buffer::new();
$(
buffer.push($element);
)*
buffer
}
};
}