use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::ptr;
use bun_alloc::AllocError;
const PAGE_SIZE_MIN: usize = 4096;
pub enum LinearFifoBufferType {
Static(usize),
Slice,
Dynamic,
}
pub trait LinearFifoBuffer<T> {
const POWERS_OF_TWO: bool;
const DYNAMIC: bool;
fn as_slice(&self) -> &[T];
fn as_mut_slice(&mut self) -> &mut [T];
#[inline]
fn len(&self) -> usize {
self.as_slice().len()
}
fn realloc(&mut self, _new_size: usize) -> Result<(), AllocError> {
unreachable!("realloc on non-Dynamic LinearFifo buffer")
}
fn alloc_swap(&mut self, _new_size: usize) -> Result<Box<[MaybeUninit<T>]>, AllocError> {
unreachable!("alloc_swap on non-Dynamic LinearFifo buffer")
}
}
#[inline(always)]
fn assume_init_slice<T>(s: &[MaybeUninit<T>]) -> &[T] {
unsafe { &*(ptr::from_ref::<[MaybeUninit<T>]>(s) as *const [T]) }
}
#[inline(always)]
fn assume_init_slice_mut<T>(s: &mut [MaybeUninit<T>]) -> &mut [T] {
unsafe { &mut *(ptr::from_mut::<[MaybeUninit<T>]>(s) as *mut [T]) }
}
#[inline(always)]
fn shift_down_one<T>(slice: &mut [T]) {
if slice.len() <= 1 {
return;
}
unsafe { ptr::copy(slice.as_ptr().add(1), slice.as_mut_ptr(), slice.len() - 1) };
}
#[cfg(debug_assertions)]
#[inline(always)]
fn poison<T>(slice: &mut [T], n: usize) {
debug_assert!(n <= slice.len());
unsafe {
ptr::write_bytes(
slice.as_mut_ptr().cast::<u8>(),
0xAA,
n * mem::size_of::<T>(),
)
};
}
pub struct StaticBuffer<T, const N: usize>([MaybeUninit<T>; N]);
impl<T, const N: usize> LinearFifoBuffer<T> for StaticBuffer<T, N> {
const POWERS_OF_TWO: bool = N.is_power_of_two();
const DYNAMIC: bool = false;
#[inline]
fn as_slice(&self) -> &[T] {
assume_init_slice(self.0.as_slice())
}
#[inline]
fn as_mut_slice(&mut self) -> &mut [T] {
assume_init_slice_mut(self.0.as_mut_slice())
}
}
pub struct SliceBuffer<'a, T>(&'a mut [T]);
impl<'a, T> LinearFifoBuffer<T> for SliceBuffer<'a, T> {
const POWERS_OF_TWO: bool = false; const DYNAMIC: bool = false;
#[inline]
fn as_slice(&self) -> &[T] {
self.0
}
#[inline]
fn as_mut_slice(&mut self) -> &mut [T] {
self.0
}
}
pub struct DynamicBuffer<T>(Box<[MaybeUninit<T>]>);
impl<T> LinearFifoBuffer<T> for DynamicBuffer<T> {
const POWERS_OF_TWO: bool = true; const DYNAMIC: bool = true;
#[inline]
fn as_slice(&self) -> &[T] {
assume_init_slice(&self.0)
}
#[inline]
fn as_mut_slice(&mut self) -> &mut [T] {
assume_init_slice_mut(&mut self.0)
}
fn realloc(&mut self, new_size: usize) -> Result<(), AllocError> {
let mut new = Box::<[T]>::new_uninit_slice(new_size);
let n = self.0.len().min(new_size);
unsafe { ptr::copy_nonoverlapping(self.0.as_ptr(), new.as_mut_ptr(), n) };
self.0 = new;
Ok(())
}
fn alloc_swap(&mut self, new_size: usize) -> Result<Box<[MaybeUninit<T>]>, AllocError> {
let new = Box::<[T]>::new_uninit_slice(new_size);
Ok(mem::replace(&mut self.0, new))
}
}
pub struct LinearFifo<T, B: LinearFifoBuffer<T>> {
buf: B,
head: usize,
count: usize,
_marker: PhantomData<T>,
}
impl<T, const N: usize> LinearFifo<T, StaticBuffer<T, N>> {
pub fn init() -> Self {
Self {
buf: StaticBuffer([const { MaybeUninit::uninit() }; N]),
head: 0,
count: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> LinearFifo<T, SliceBuffer<'a, T>> {
pub fn init(buf: &'a mut [T]) -> Self {
Self {
buf: SliceBuffer(buf),
head: 0,
count: 0,
_marker: PhantomData,
}
}
}
impl<T> LinearFifo<T, DynamicBuffer<T>> {
pub fn init() -> Self {
Self {
buf: DynamicBuffer(Box::new([])),
head: 0,
count: 0,
_marker: PhantomData,
}
}
}
impl<T, B: LinearFifoBuffer<T>> LinearFifo<T, B> {
#[inline]
fn buf_len(&self) -> usize {
self.buf.len()
}
#[inline]
pub fn capacity(&self) -> usize {
self.buf.len()
}
#[inline]
pub fn reset_head_if_empty(&mut self) {
if self.count == 0 {
self.head = 0;
}
}
pub fn realign(&mut self) {
let buf_len = self.buf_len();
if buf_len - self.head >= self.count {
let count = self.count;
let head = self.head;
let buf = self.buf.as_mut_slice();
unsafe { ptr::copy(buf.as_ptr().add(head), buf.as_mut_ptr(), count) };
self.head = 0;
} else {
let mut tmp_bytes = [MaybeUninit::<u8>::uninit(); PAGE_SIZE_MIN / 2];
let tmp_ptr: *mut u8 = tmp_bytes.as_mut_ptr().cast::<u8>();
let t_size = mem::size_of::<T>();
let tmp_len = (PAGE_SIZE_MIN / 2) / t_size;
while self.head != 0 {
let n = self.head.min(tmp_len);
let m = buf_len - n;
let buf = self.buf.as_mut_slice();
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr().cast::<u8>(), tmp_ptr, n * t_size);
ptr::copy(buf.as_ptr().add(n), buf.as_mut_ptr(), m);
ptr::copy_nonoverlapping(
tmp_ptr,
buf.as_mut_ptr().add(m).cast::<u8>(),
n * t_size,
);
}
self.head -= n;
}
}
#[cfg(debug_assertions)]
{
let count = self.count;
let unused = &mut self.buf.as_mut_slice()[count..];
unsafe {
ptr::write_bytes(
unused.as_mut_ptr().cast::<u8>(),
0xAA,
std::mem::size_of_val(unused),
);
}
}
}
pub fn shrink(&mut self, size: usize) {
debug_assert!(size >= self.count);
if B::DYNAMIC {
self.realign();
match self.buf.realloc(size) {
Ok(()) => {}
Err(AllocError) => return, }
}
}
#[deprecated(note = "deprecated; call `ensure_unused_capacity` or `ensure_total_capacity`")]
pub fn ensure_capacity(&mut self, _size: usize) {
unreachable!("deprecated; call ensure_unused_capacity or ensure_total_capacity");
}
pub fn ensure_total_capacity(&mut self, size: usize) -> Result<(), AllocError> {
if self.buf_len() >= size {
return Ok(());
}
if B::DYNAMIC {
self.realign();
let new_size = if B::POWERS_OF_TWO {
size.checked_next_power_of_two().ok_or(AllocError)?
} else {
size
};
let count = self.count;
let old = self.buf.alloc_swap(new_size)?;
if count > 0 {
let new = self.buf.as_mut_slice();
unsafe {
ptr::copy_nonoverlapping(old.as_ptr().cast::<T>(), new.as_mut_ptr(), count);
}
}
self.head = 0;
Ok(())
} else {
Err(AllocError)
}
}
pub fn ensure_unused_capacity(&mut self, size: usize) -> Result<(), AllocError> {
if self.writable_length() >= size {
return Ok(());
}
let total = self.count.checked_add(size).ok_or(AllocError)?;
self.ensure_total_capacity(total)
}
#[inline]
pub fn readable_length(&self) -> usize {
self.count
}
fn readable_slice_mut(&mut self, offset: usize) -> &mut [T] {
if offset > self.count {
return &mut [];
}
let buf_len = self.buf_len();
let head = self.head;
let count = self.count;
let buf = self.buf.as_mut_slice();
let mut start = head + offset;
if start >= buf_len {
start -= buf_len;
&mut buf[start..start + (count - offset)]
} else {
let end = (head + count).min(buf_len);
&mut buf[start..end]
}
}
pub fn readable_slice(&self, offset: usize) -> &[T] {
if offset > self.count {
return &[];
}
let buf_len = self.buf_len();
let buf = self.buf.as_slice();
let mut start = self.head + offset;
if start >= buf_len {
start -= buf_len;
&buf[start..start + (self.count - offset)]
} else {
let end = (self.head + self.count).min(buf_len);
&buf[start..end]
}
}
pub fn discard(&mut self, count: usize) {
debug_assert!(count <= self.count);
#[cfg(debug_assertions)]
{
let slice_len = self.readable_slice_mut(0).len();
if slice_len >= count {
poison(self.readable_slice_mut(0), count);
} else {
poison(self.readable_slice_mut(0), slice_len);
let rem = count - slice_len;
poison(self.readable_slice_mut(slice_len), rem);
}
}
let mut head = self.head + count;
if B::POWERS_OF_TWO {
head &= self.buf_len().wrapping_sub(1);
} else {
head %= self.buf_len();
}
self.head = head;
self.count -= count;
}
pub fn read_item(&mut self) -> Option<T> {
if self.count == 0 {
return None;
}
let c = unsafe { ptr::read(self.buf.as_slice().as_ptr().add(self.head)) };
self.discard(1);
Some(c)
}
pub fn read(&mut self, dst: &mut [T]) -> usize
where
T: Copy,
{
let total = dst.len();
let mut dst_left = &mut dst[..];
while !dst_left.is_empty() {
let slice = self.readable_slice(0);
if slice.is_empty() {
break;
}
let n = slice.len().min(dst_left.len());
dst_left[..n].copy_from_slice(&slice[..n]);
self.discard(n);
dst_left = &mut dst_left[n..];
}
total - dst_left.len()
}
#[inline]
pub fn writable_length(&self) -> usize {
self.buf_len() - self.count
}
pub fn writable_slice(&mut self, offset: usize) -> &mut [T] {
let buf_len = self.buf_len();
if offset > buf_len {
return &mut [];
}
let head = self.head;
let count = self.count;
let writable = buf_len - count;
let buf = self.buf.as_mut_slice();
let tail = head + offset + count;
if tail < buf_len {
&mut buf[tail..]
} else {
let start = tail - buf_len;
&mut buf[start..start + (writable - offset)]
}
}
pub fn writable_with_size(&mut self, size: usize) -> Result<&mut [T], AllocError> {
self.ensure_unused_capacity(size)?;
if self.writable_slice(0).len() < size {
self.realign();
}
let slice = self.writable_slice(0);
debug_assert!(slice.len() >= size);
Ok(&mut slice[..size])
}
pub fn update(&mut self, count: usize) {
debug_assert!(self.count + count <= self.buf_len());
self.count += count;
}
pub fn write_assume_capacity(&mut self, src: &[T])
where
T: Copy,
{
debug_assert!(self.writable_length() >= src.len());
let mut src_left = src;
while !src_left.is_empty() {
let n = {
let writable = self.writable_slice(0);
debug_assert!(!writable.is_empty());
let n = writable.len().min(src_left.len());
writable[..n].copy_from_slice(&src_left[..n]);
n
};
self.update(n);
src_left = &src_left[n..];
}
}
pub fn write_item(&mut self, item: T) -> Result<(), AllocError> {
self.ensure_unused_capacity(1)?;
self.write_item_assume_capacity(item);
Ok(())
}
pub fn write_item_assume_capacity(&mut self, item: T) {
let mut tail = self.head + self.count;
if B::POWERS_OF_TWO {
tail &= self.buf_len() - 1;
} else {
tail %= self.buf_len();
}
unsafe { ptr::write(self.buf.as_mut_slice().as_mut_ptr().add(tail), item) };
self.update(1);
}
pub fn write(&mut self, src: &[T]) -> Result<(), AllocError>
where
T: Copy,
{
self.ensure_unused_capacity(src.len())?;
self.write_assume_capacity(src);
Ok(())
}
fn rewind(&mut self, count: usize) {
debug_assert!(self.writable_length() >= count);
let mut head = self.head + (self.buf_len() - count);
if B::POWERS_OF_TWO {
head &= self.buf_len() - 1;
} else {
head %= self.buf_len();
}
self.head = head;
self.count += count;
}
pub fn unget(&mut self, src: &[T]) -> Result<(), AllocError>
where
T: Copy,
{
self.ensure_unused_capacity(src.len())?;
self.rewind(src.len());
let slice_len = {
let s = self.readable_slice_mut(0);
let n = s.len().min(src.len());
s[..n].copy_from_slice(&src[..n]);
s.len()
};
if src.len() > slice_len {
let slice2 = self.readable_slice_mut(slice_len);
slice2[..src.len() - slice_len].copy_from_slice(&src[slice_len..]);
}
Ok(())
}
pub fn peek_item(&self, offset: usize) -> T
where
T: Copy,
{
debug_assert!(offset < self.count);
let mut index = self.head + offset;
if B::POWERS_OF_TWO {
index &= self.buf_len() - 1;
} else {
index %= self.buf_len();
}
self.buf.as_slice()[index]
}
pub fn peek_item_mut(&mut self, offset: usize) -> &mut T {
debug_assert!(offset < self.count);
let mut index = self.head + offset;
if B::POWERS_OF_TWO {
index &= self.buf_len() - 1;
} else {
index %= self.buf_len();
}
&mut self.buf.as_mut_slice()[index]
}
pub fn ordered_remove_item(&mut self, offset: usize) {
if offset == 0 {
return self.discard(1);
}
debug_assert!(offset < self.count);
let buf_len = self.buf_len();
let head = self.head;
let count = self.count;
if buf_len - head >= count {
let buf = self.buf.as_mut_slice();
shift_down_one(&mut buf[head + offset..]);
} else {
let mut index = head + offset;
if B::POWERS_OF_TWO {
index &= buf_len - 1;
} else {
index %= buf_len;
}
let buf = self.buf.as_mut_slice();
if index < head {
shift_down_one(&mut buf[index..count - head]);
} else {
let wrap = unsafe { ptr::read(buf.as_ptr()) };
shift_down_one(&mut buf[index..]);
unsafe { ptr::write(buf.as_mut_ptr().add(buf_len - 1), wrap) };
shift_down_one(&mut buf[..head - count]);
}
}
self.count -= 1;
}
pub fn pump<R, W, E>(&mut self, mut src_reader: R, dest_writer: &mut W) -> Result<(), E>
where
R: FnMut(&mut [T]) -> Result<usize, E>,
W: FnMut(&[T]) -> Result<usize, E>,
{
debug_assert!(self.buf_len() > 0);
loop {
if self.writable_length() > 0 {
let n = {
let ws = self.writable_slice(0);
src_reader(ws)?
};
if n == 0 {
break; }
self.update(n);
}
let written = {
let rs = self.readable_slice(0);
dest_writer(rs)?
};
self.discard(written);
}
while self.readable_length() > 0 {
let written = {
let rs = self.readable_slice(0);
dest_writer(rs)?
};
self.discard(written);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
type DynFifoU8 = LinearFifo<u8, DynamicBuffer<u8>>;
#[test]
fn discard_zero_from_empty_buffer_should_not_error_on_overflow() {
let mut fifo = DynFifoU8::init();
fifo.discard(0);
}
#[test]
fn linear_fifo_u8_dynamic() {
let mut fifo = DynFifoU8::init();
fifo.write(b"HELLO").unwrap();
assert_eq!(5usize, fifo.readable_length());
assert_eq!(b"HELLO", fifo.readable_slice(0));
{
for i in 0..5 {
fifo.write(&[fifo.peek_item(i)]).unwrap();
}
assert_eq!(10usize, fifo.readable_length());
assert_eq!(b"HELLOHELLO", fifo.readable_slice(0));
}
{
assert_eq!(b'H', fifo.read_item().unwrap());
assert_eq!(b'E', fifo.read_item().unwrap());
assert_eq!(b'L', fifo.read_item().unwrap());
assert_eq!(b'L', fifo.read_item().unwrap());
assert_eq!(b'O', fifo.read_item().unwrap());
}
assert_eq!(5usize, fifo.readable_length());
{
assert_eq!(11usize, fifo.writable_length());
assert_eq!(6usize, fifo.writable_slice(0).len());
fifo.write_assume_capacity(b"6<chars<11");
assert_eq!(b"HELLO6<char", fifo.readable_slice(0));
assert_eq!(b"s<11", fifo.readable_slice(11));
assert_eq!(b"11", fifo.readable_slice(13));
assert_eq!(b"", fifo.readable_slice(15));
fifo.discard(11);
assert_eq!(b"s<11", fifo.readable_slice(0));
fifo.discard(4);
assert_eq!(0usize, fifo.readable_length());
}
{
let buf = fifo.writable_with_size(12).unwrap();
assert_eq!(12usize, buf.len());
for i in 0..10 {
buf[i] = i as u8 + b'a';
}
fifo.update(10);
assert_eq!(b"abcdefghij", fifo.readable_slice(0));
}
{
fifo.unget(b"prependedstring").unwrap();
let mut result = [0u8; 30];
let n = fifo.read(&mut result);
assert_eq!(b"prependedstringabcdefghij", &result[..n]);
fifo.unget(b"b").unwrap();
fifo.unget(b"a").unwrap();
let n = fifo.read(&mut result);
assert_eq!(b"ab", &result[..n]);
}
fifo.shrink(0);
}
#[test]
fn linear_fifo_generic_u8_static() {
let mut fifo = LinearFifo::<u8, StaticBuffer<u8, 32>>::init();
fifo.write(&[0, 1, 1, 0, 1]).unwrap();
assert_eq!(5usize, fifo.readable_length());
{
assert_eq!(0u8, fifo.read_item().unwrap());
assert_eq!(1u8, fifo.read_item().unwrap());
assert_eq!(1u8, fifo.read_item().unwrap());
assert_eq!(0u8, fifo.read_item().unwrap());
assert_eq!(1u8, fifo.read_item().unwrap());
assert_eq!(0usize, fifo.readable_length());
}
{
fifo.write_item(1).unwrap();
fifo.write_item(1).unwrap();
fifo.write_item(1).unwrap();
assert_eq!(3usize, fifo.readable_length());
}
{
let mut read_buf = [0u8; 3];
let n = fifo.read(&mut read_buf);
assert_eq!(3usize, n); }
}
}