use core::{
cell::{Cell, UnsafeCell},
marker::PhantomData,
mem::MaybeUninit,
ptr::{addr_of_mut, NonNull},
};
use std::boxed::Box;
struct Block<T, const N: usize> {
next: Cell<Option<NonNull<Block<T, N>>>>,
values: [UnsafeCell<MaybeUninit<T>>; N],
begin: Cell<usize>,
end: Cell<usize>,
}
impl<T, const N: usize> Block<T, N> {
fn alloc() -> NonNull<Self> {
let mut block = Box::<Self>::new_uninit();
let ptr = block.as_mut_ptr();
unsafe {
addr_of_mut!((*ptr).next).write(Cell::new(None));
addr_of_mut!((*ptr).begin).write(Cell::new(0));
addr_of_mut!((*ptr).end).write(Cell::new(0));
NonNull::new_unchecked(Box::into_raw(block.assume_init()))
}
}
}
impl<T, const N: usize> Drop for Block<T, N> {
fn drop(&mut self) {
let begin = self.begin.get();
let end = self.end.get();
for i in begin..end {
unsafe { (*self.values[i].get()).assume_init_drop() };
}
}
}
pub(crate) struct BlockList<T, const N: usize = 32> {
head: NonNull<Block<T, N>>,
tail: NonNull<Block<T, N>>,
len: usize,
_marker: PhantomData<T>,
}
impl<T, const N: usize> BlockList<T, N> {
pub(super) fn new() -> Self {
let first = Block::<T, N>::alloc();
Self {
head: first,
tail: first,
len: 0,
_marker: PhantomData,
}
}
#[inline(always)]
pub(super) fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub(super) fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
pub(super) fn push(&mut self, item: T) {
let tail = unsafe { self.tail.as_ref() };
let end = tail.end.get();
if end < N {
unsafe { (*tail.values[end].get()).write(item) };
tail.end.set(end + 1);
} else {
let new = Block::<T, N>::alloc();
let new_block = unsafe { new.as_ref() };
unsafe { (*new_block.values[0].get()).write(item) };
new_block.end.set(1);
tail.next.set(Some(new));
self.tail = new;
}
self.len += 1;
}
#[inline(always)]
pub(super) fn pop(&mut self) -> Option<T> {
loop {
let head = unsafe { self.head.as_ref() };
let begin = head.begin.get();
let end = head.end.get();
if begin < end {
let item = unsafe { (*head.values[begin].get()).assume_init_read() };
head.begin.set(begin + 1);
self.len -= 1;
return Some(item);
}
if self.head == self.tail {
return None;
}
let next = head.next.get().expect("a non-tail block always has a next");
let old = self.head;
self.head = next;
unsafe { drop(Box::from_raw(old.as_ptr())) };
}
}
}
impl<T, const N: usize> Drop for BlockList<T, N> {
fn drop(&mut self) {
let mut cur = Some(self.head);
while let Some(ptr) = cur {
let block = unsafe { Box::from_raw(ptr.as_ptr()) };
cur = block.next.get();
}
}
}
#[cfg(all(test, feature = "std"))]
mod tests;