use core::ptr::NonNull;
use alloc_crate::vec::Vec;
pub unsafe trait BatchReader {
type Item;
fn read_buffer(&mut self) -> &[Self::Item];
unsafe fn advance(&mut self, n: usize);
unsafe fn release(&mut self) {}
}
pub struct ReadGuard<'a, R: BatchReader> {
receiver: &'a mut R,
data: NonNull<[R::Item]>,
consumed: usize,
}
impl<'a, R: BatchReader> ReadGuard<'a, R> {
pub(crate) fn new(receiver: &'a mut R) -> Self {
let slice = receiver.read_buffer();
let data = NonNull::from_ref(slice);
Self {
receiver,
data,
consumed: 0,
}
}
#[inline]
pub fn len(&self) -> usize {
unsafe { self.data.as_ref() }.len() - self.consumed
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn as_slice(&self) -> &[R::Item] {
unsafe { &self.data.as_ref()[self.consumed..] }
}
pub fn advance(&mut self, n: usize) {
assert!(n <= self.len(), "advancing beyond available items");
self.consumed += n;
}
pub fn drain_into(&mut self, dst: &mut Vec<R::Item>) -> usize {
let slice = self.as_slice();
let len = slice.len();
if len == 0 {
return 0;
}
dst.reserve(len);
let dst_len = dst.len();
unsafe {
core::ptr::copy_nonoverlapping(slice.as_ptr(), dst.as_mut_ptr().add(dst_len), len);
dst.set_len(dst_len + len);
}
self.consumed += len;
len
}
pub unsafe fn drain_into_ptr(&mut self, dst: *mut R::Item) -> usize {
let slice = self.as_slice();
let len = slice.len();
if len == 0 {
return 0;
}
unsafe {
core::ptr::copy_nonoverlapping(slice.as_ptr(), dst, len);
}
self.consumed += len;
len
}
pub fn copy_into(&mut self, dst: &mut [R::Item]) -> usize
where
R::Item: Copy,
{
let slice = self.as_slice();
let n = slice.len().min(dst.len());
if n > 0 {
dst[..n].copy_from_slice(&slice[..n]);
self.consumed += n;
}
n
}
pub unsafe fn advance_unchecked(&mut self, n: usize) {
self.consumed += n;
}
}
impl<R: BatchReader> Iterator for ReadGuard<'_, R> {
type Item = R::Item;
fn next(&mut self) -> Option<Self::Item> {
let slice = unsafe { self.data.as_ref() };
if self.consumed >= slice.len() {
return None;
}
let item = unsafe { core::ptr::read(&slice[self.consumed]) };
self.consumed += 1;
Some(item)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len();
(remaining, Some(remaining))
}
}
impl<R: BatchReader> ExactSizeIterator for ReadGuard<'_, R> {}
impl<R: BatchReader> Drop for ReadGuard<'_, R> {
fn drop(&mut self) {
if unsafe { self.data.as_ref() }.is_empty() {
return;
}
unsafe {
if self.consumed > 0 {
self.receiver.advance(self.consumed);
}
self.receiver.release();
}
}
}