use dambi::{ByteBufferMut, Bytes};
#[repr(C)]
pub(crate) struct FrameRing<const CAP: usize> {
buf: ByteBufferMut,
head: u16,
tail: u16,
}
impl<const CAP: usize> FrameRing<CAP> {
#[inline(always)]
pub(crate) fn new() -> Self {
const {
assert!(CAP <= u16::MAX as usize, "FrameRing CAP must fit u16");
}
Self {
buf: ByteBufferMut::with_capacity(CAP),
head: 0,
tail: 0,
}
}
#[must_use = "remainder dropped if ignored"]
#[inline(always)]
pub(crate) fn append(&mut self, src: &[u8]) -> usize {
let head = self.head as usize;
let n = src.len().min(CAP - head);
if n > 0 {
self.buf.ensure_unique_for_mutate(head);
unsafe {
std::ptr::copy_nonoverlapping(src.as_ptr(), self.buf.data_mut_ptr().add(head), n);
}
self.head = (head + n) as u16;
}
n
}
#[inline(always)]
pub(crate) fn drain<F>(&mut self, mut f: F)
where
F: FnMut(&Bytes) -> Option<usize>,
{
loop {
let t = self.tail as usize;
let h = self.head as usize;
if h <= t {
break;
}
let pending = Bytes::from_byte_buffer_range(self.buf.share(), t as u32, (h - t) as u32);
let Some(consumed) = f(&pending) else {
break;
};
if consumed > h - t {
break;
}
self.tail = (t + consumed) as u16;
}
self.compact();
}
fn compact(&mut self) {
let t = self.tail as usize;
let h = self.head as usize;
if t == 0 {
return;
}
if t >= h {
self.head = 0;
self.tail = 0;
return;
}
let unparsed = h - t;
self.buf.ensure_unique_for_mutate(h);
unsafe {
std::ptr::copy(
self.buf.data_ptr().add(t),
self.buf.data_mut_ptr(),
unparsed,
);
}
self.head = unparsed as u16;
self.tail = 0;
}
}
impl<const CAP: usize> Default for FrameRing<CAP> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}