use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::ptr::{self, NonNull};
use core::{mem, slice};
use super::uninit::Uninit;
pub(crate) struct ArenaBuf<'a, T> {
ptr: NonNull<T>,
len: usize,
cap: usize,
_phantom: PhantomData<&'a mut [T]>,
}
impl<'a, T> ArenaBuf<'a, T> {
#[inline]
pub(crate) const fn new() -> Self {
let cap = if mem::size_of::<T>() == 0 { usize::MAX } else { 0 };
Self {
ptr: NonNull::dangling(),
len: 0,
cap,
_phantom: PhantomData,
}
}
#[inline]
pub(crate) const fn len(&self) -> usize {
self.len
}
#[inline]
pub(crate) const fn cap(&self) -> usize {
self.cap
}
#[inline]
pub(crate) const fn remaining_cap(&self) -> usize {
self.cap - self.len
}
#[inline]
pub(crate) const fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}
#[allow(
clippy::needless_pass_by_ref_mut,
reason = "mutable self required for API safety: callers must hold exclusive access to obtain a mutable pointer"
)]
#[inline]
pub(crate) const fn as_mut_ptr(&mut self) -> *mut T {
self.ptr.as_ptr()
}
#[inline]
pub(crate) fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
#[inline]
pub(crate) fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
#[inline]
pub(crate) fn push_within_cap(&mut self, value: T) -> Result<(), T> {
if self.len == self.cap {
return Err(value);
}
unsafe {
ptr::write(self.ptr.as_ptr().add(self.len), value);
}
self.len += 1;
Ok(())
}
#[inline]
pub(crate) fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
self.len -= 1;
Some(unsafe { ptr::read(self.ptr.as_ptr().add(self.len)) })
}
#[inline]
#[cfg_attr(test, mutants::skip)] pub(crate) fn replace_buffer(&mut self, fresh: Uninit<'a, [T]>) {
let (new_ptr, new_cap) = fresh.into_raw_buffer();
unsafe { self.replace_buffer_raw(new_ptr, new_cap) };
}
#[inline]
pub(crate) unsafe fn replace_buffer_raw(&mut self, new_ptr: NonNull<T>, new_cap: usize) {
debug_assert!(new_cap >= self.len, "replace_buffer_raw: new capacity below live length");
if self.len > 0 {
unsafe {
ptr::copy_nonoverlapping(self.ptr.as_ptr(), new_ptr.as_ptr(), self.len);
}
}
self.ptr = new_ptr;
self.cap = new_cap;
}
#[inline]
pub(crate) fn extend_copy(&mut self, src: &[T])
where
T: Copy,
{
debug_assert!(self.remaining_cap() >= src.len(), "extend_copy: insufficient capacity");
if src.is_empty() {
return;
}
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), self.ptr.as_ptr().add(self.len), src.len());
}
self.len += src.len();
}
#[inline]
pub(crate) fn clear(&mut self) {
self.truncate(0);
}
#[inline]
pub(crate) fn truncate(&mut self, new_len: usize) {
if new_len >= self.len {
return;
}
let drop_count = self.len - new_len;
self.len = new_len;
if const { mem::needs_drop::<T>() } {
unsafe {
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.ptr.as_ptr().add(new_len), drop_count));
}
}
}
#[inline]
pub(crate) fn swap_remove(&mut self, idx: usize) -> Option<T> {
if idx >= self.len {
return None;
}
let last = self.len - 1;
if idx != last {
self.as_mut_slice().swap(idx, last);
}
self.pop()
}
#[inline]
#[cfg_attr(test, mutants::skip)] pub(crate) fn insert_within_cap(&mut self, idx: usize, value: T) {
debug_assert!(idx <= self.len, "insert_within_cap: idx out of bounds");
debug_assert!(self.remaining_cap() >= 1, "insert_within_cap: no space");
unsafe {
let base = self.ptr.as_ptr().add(idx);
if idx < self.len {
ptr::copy(base, base.add(1), self.len - idx);
}
ptr::write(base, value);
}
self.len += 1;
}
#[inline]
#[cfg_attr(test, mutants::skip)] pub(crate) fn remove(&mut self, idx: usize) -> Option<T> {
if idx >= self.len {
return None;
}
let value = unsafe {
let base = self.ptr.as_ptr().add(idx);
let value = ptr::read(base);
let tail = self.len - idx - 1;
if tail > 0 {
ptr::copy(base.add(1), base, tail);
}
value
};
self.len -= 1;
Some(value)
}
#[inline]
pub(crate) const unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(new_len <= self.cap, "set_len: new_len exceeds cap");
self.len = new_len;
}
#[inline]
pub(crate) fn split_off_buf(&mut self, at: usize) -> Self {
debug_assert!(at <= self.len, "split_off_buf: at exceeds len");
let tail_len = self.len - at;
if const { mem::size_of::<T>() == 0 } {
self.len = at;
return ArenaBuf {
ptr: NonNull::dangling(),
len: tail_len,
cap: usize::MAX,
_phantom: PhantomData,
};
}
let tail_ptr = unsafe { NonNull::new_unchecked(self.ptr.as_ptr().add(at)) };
let tail_cap = self.cap - at;
self.len = at;
self.cap = at;
ArenaBuf {
ptr: tail_ptr,
len: tail_len,
cap: tail_cap,
_phantom: PhantomData,
}
}
#[inline]
pub(crate) fn try_absorb_adjacent(&mut self, other: &mut Self) -> bool {
debug_assert!(mem::size_of::<T>() != 0, "try_absorb_adjacent: not for ZSTs");
if self.len != self.cap {
return false;
}
let self_end = self.ptr.as_ptr().wrapping_add(self.cap);
if !ptr::eq(self_end.cast_const(), other.ptr.as_ptr().cast_const()) {
return false;
}
self.cap += other.cap;
self.len += other.len;
other.ptr = NonNull::dangling();
other.len = 0;
other.cap = 0;
true
}
#[inline]
pub(crate) const unsafe fn set_cap(&mut self, new_cap: usize) {
debug_assert!(new_cap >= self.len, "set_cap: new_cap below len");
self.cap = new_cap;
}
#[inline]
pub(crate) fn drain_all(&mut self) -> DrainAll<'a, T> {
let len = self.len;
self.len = 0;
DrainAll {
ptr: self.ptr,
head: 0,
tail: len,
_marker: PhantomData,
}
}
}
pub(crate) struct DrainAll<'a, T> {
ptr: NonNull<T>,
head: usize,
tail: usize,
_marker: PhantomData<&'a mut [T]>,
}
impl<T> Iterator for DrainAll<'_, T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
if self.head == self.tail {
return None;
}
let value = unsafe { ptr::read(self.ptr.as_ptr().add(self.head)) };
self.head += 1;
Some(value)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.tail - self.head;
(n, Some(n))
}
}
impl<T> DoubleEndedIterator for DrainAll<'_, T> {
#[inline]
fn next_back(&mut self) -> Option<T> {
if self.head == self.tail {
return None;
}
self.tail -= 1;
Some(unsafe { ptr::read(self.ptr.as_ptr().add(self.tail)) })
}
}
impl<T> ExactSizeIterator for DrainAll<'_, T> {}
impl<T> FusedIterator for DrainAll<'_, T> {}
impl<T> Drop for DrainAll<'_, T> {
#[inline]
#[cfg_attr(test, mutants::skip)] fn drop(&mut self) {
if const { mem::needs_drop::<T>() } && self.head < self.tail {
let len = self.tail - self.head;
unsafe {
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.ptr.as_ptr().add(self.head), len));
}
}
}
}
impl<T> Drop for ArenaBuf<'_, T> {
#[inline]
fn drop(&mut self) {
self.truncate(0);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_off_buf_zst_splits_via_len_only() {
let mut buf: ArenaBuf<()> = ArenaBuf::new();
for _ in 0..10 {
buf.push_within_cap(()).expect("ZST push always fits");
}
let tail = buf.split_off_buf(4);
assert_eq!(buf.len(), 4);
assert_eq!(tail.len(), 6);
drop(tail);
}
}