use core::hint::assert_unchecked;
use core::mem;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator};
use super::{Arena, ExpectAlloc};
use crate::Alloc;
use crate::internal::constants::CHUNK_ALIGN;
#[inline(always)]
fn reject_over_aligned<T>() -> Result<(), AllocError> {
if const { mem::align_of::<T>() >= CHUNK_ALIGN } {
return Err(AllocError);
}
Ok(())
}
#[cfg_attr(test, mutants::skip)] #[inline]
fn worst_case_slice_payload<T>(len: usize) -> usize {
let value_bytes = mem::size_of::<T>().saturating_mul(len);
value_bytes.saturating_add(mem::align_of::<T>())
}
#[inline(always)]
#[allow(clippy::mut_from_ref, reason = "the returned empty slice borrows nothing")]
fn empty_slice<'a, T>() -> &'a mut [T] {
unsafe { core::slice::from_raw_parts_mut(NonNull::<T>::dangling().as_ptr(), 0) }
}
#[inline(always)]
#[cfg_attr(test, mutants::skip)]
fn assume_nonzero_len(len: usize) {
unsafe { assert_unchecked(len > 0) };
}
impl<A: Allocator + Clone> Arena<A> {
#[must_use]
#[inline]
pub fn alloc_slice_copy<T: Copy>(&self, slice: impl AsRef<[T]>) -> Alloc<'_, [T]> {
self.impl_alloc_slice_copy::<T>(slice.as_ref()).expect_alloc()
}
#[inline]
pub fn try_alloc_slice_copy<T: Copy>(&self, slice: impl AsRef<[T]>) -> Result<Alloc<'_, [T]>, AllocError> {
self.impl_alloc_slice_copy::<T>(slice.as_ref())
}
#[must_use]
#[inline]
pub fn alloc_slice_fill_with<T, F: FnMut(usize) -> T>(&self, len: usize, f: F) -> Alloc<'_, [T]> {
self.impl_alloc_slice_fill_with::<T, F>(len, f).expect_alloc()
}
#[inline]
pub fn try_alloc_slice_fill_with<T, F: FnMut(usize) -> T>(&self, len: usize, f: F) -> Result<Alloc<'_, [T]>, AllocError> {
self.impl_alloc_slice_fill_with::<T, F>(len, f)
}
#[must_use]
#[inline]
pub fn alloc_slice_clone<T: Clone>(&self, slice: impl AsRef<[T]>) -> Alloc<'_, [T]> {
self.impl_alloc_slice_clone::<T>(slice.as_ref()).expect_alloc()
}
#[inline]
pub fn try_alloc_slice_clone<T: Clone>(&self, slice: impl AsRef<[T]>) -> Result<Alloc<'_, [T]>, AllocError> {
self.impl_alloc_slice_clone::<T>(slice.as_ref())
}
#[must_use]
#[inline]
pub fn alloc_slice_fill_iter<T, I>(&self, iter: I) -> Alloc<'_, [T]>
where
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
self.impl_alloc_slice_fill_iter::<T, I::IntoIter>(iter.into_iter()).expect_alloc()
}
#[inline]
pub fn try_alloc_slice_fill_iter<T, I>(&self, iter: I) -> Result<Alloc<'_, [T]>, AllocError>
where
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
self.impl_alloc_slice_fill_iter::<T, I::IntoIter>(iter.into_iter())
}
#[inline(always)]
fn impl_alloc_slice_copy<T: Copy>(&self, src: &[T]) -> Result<Alloc<'_, [T]>, AllocError> {
let slot = self.alloc_slice_copy_raw::<T>(src)?;
Ok(unsafe { Alloc::from_mut(slot) })
}
#[allow(
clippy::mut_from_ref,
reason = "internal helper hands out a fresh, disjoint arena slot per call; the returned &mut is wrapped in an owning Alloc by impl_alloc_slice_copy"
)]
#[inline(always)]
fn alloc_slice_copy_raw<T: Copy>(&self, src: &[T]) -> Result<&mut [T], AllocError> {
reject_over_aligned::<T>()?;
let len = src.len();
if len == 0 {
return Ok(empty_slice::<T>());
}
assume_nonzero_len(len);
let size = mem::size_of_val(src);
loop {
if let Some(u) = self.try_reserve_local_slice_with_size::<T>(len, size) {
return Ok(u.init_copy_from_slice(src));
}
if let Some(slice) = self.refill_or_alloc_oversized_slice_copy::<T>(src)? {
return Ok(slice);
}
}
}
#[cold]
#[inline(never)]
#[allow(
clippy::mut_from_ref,
reason = "internal helper hands out a fresh, disjoint arena slot per call; the returned &mut is wrapped in an owning Alloc at the public boundary"
)]
#[cfg_attr(test, mutants::skip)]
fn refill_or_alloc_oversized_slice_copy<T: Copy>(&self, src: &[T]) -> Result<Option<&mut [T]>, AllocError> {
let refill_hint = worst_case_slice_payload::<T>(src.len());
if self.is_oversized(refill_hint) {
return Ok(Some(self.alloc_oversized_slice_copy::<T>(refill_hint, src)?));
}
self.refill(refill_hint)?;
Ok(None)
}
#[cold]
#[inline(never)]
#[allow(
clippy::mut_from_ref,
reason = "internal helper hands out a fresh, disjoint arena slot per call; the returned &mut is wrapped in an owning Alloc at the public boundary"
)]
fn alloc_oversized_slice_copy<T: Copy>(&self, refill_hint: usize, src: &[T]) -> Result<&mut [T], AllocError> {
let mutator = self.acquire_oversized_local_mutator(refill_hint)?;
let ticket = mutator
.try_alloc_uninit_slice::<T>(src.len())
.expect("dedicated oversized chunk sized to fit slice");
let mut ptr = ticket.init_copy_from_slice_ptr(src);
self.retain_oversized_local_mutator(mutator);
Ok(unsafe { ptr.as_mut() })
}
#[inline(always)]
fn impl_alloc_slice_clone<T: Clone>(&self, src: &[T]) -> Result<Alloc<'_, [T]>, AllocError> {
let slot = self.alloc_slice_clone_raw::<T>(src)?;
Ok(unsafe { Alloc::from_mut(slot) })
}
#[allow(
clippy::mut_from_ref,
reason = "internal helper hands out a fresh, disjoint arena slot per call; the returned &mut is wrapped in an owning Alloc by impl_alloc_slice_clone"
)]
#[inline(always)]
fn alloc_slice_clone_raw<T: Clone>(&self, src: &[T]) -> Result<&mut [T], AllocError> {
reject_over_aligned::<T>()?;
let len = src.len();
if len == 0 {
return Ok(empty_slice::<T>());
}
assume_nonzero_len(len);
let size = mem::size_of_val(src);
loop {
if let Some(u) = self.try_reserve_local_slice_with_size::<T>(len, size) {
return Ok(u.init_clone_from_slice(src));
}
if let Some(slice) = self.refill_or_alloc_oversized_slice_clone::<T>(src)? {
return Ok(slice);
}
}
}
#[cold]
#[inline(never)]
#[allow(
clippy::mut_from_ref,
reason = "internal helper hands out a fresh, disjoint arena slot per call; the returned &mut is wrapped in an owning Alloc at the public boundary"
)]
#[cfg_attr(test, mutants::skip)]
fn refill_or_alloc_oversized_slice_clone<T: Clone>(&self, src: &[T]) -> Result<Option<&mut [T]>, AllocError> {
let len = src.len();
let refill_hint = worst_case_slice_payload::<T>(len);
if self.is_oversized(refill_hint) {
let mut ptr = self.alloc_oversized_local_with(refill_hint, |mutator| {
let ticket = mutator
.try_alloc_uninit_slice::<T>(len)
.expect("dedicated oversized chunk sized to fit slice");
ticket.init_with_ptr(|i| src[i].clone())
})?;
return Ok(Some(unsafe { ptr.as_mut() }));
}
self.refill(refill_hint)?;
Ok(None)
}
#[inline(always)]
fn impl_alloc_slice_fill_with<T, F: FnMut(usize) -> T>(&self, len: usize, f: F) -> Result<Alloc<'_, [T]>, AllocError> {
let slot = self.alloc_slice_fill_with_raw::<T, F>(len, f)?;
Ok(unsafe { Alloc::from_mut(slot) })
}
#[allow(
clippy::mut_from_ref,
reason = "internal helper hands out a fresh, disjoint arena slot per call; the returned &mut is wrapped in an owning Alloc by impl_alloc_slice_fill_with"
)]
#[inline(always)]
fn alloc_slice_fill_with_raw<T, F: FnMut(usize) -> T>(&self, len: usize, f: F) -> Result<&mut [T], AllocError> {
reject_over_aligned::<T>()?;
if len == 0 {
return Ok(empty_slice::<T>());
}
assume_nonzero_len(len);
let refill_hint = worst_case_slice_payload::<T>(len);
let mut f = Some(f);
loop {
if let Some(u) = self.try_reserve_local_slice::<T>(len) {
let f = f.take().expect("with closure taken twice");
return Ok(u.init_with(f));
}
if self.is_oversized(refill_hint) {
let f = f.take().expect("with closure taken twice");
let mut ptr = self.alloc_oversized_local_with(refill_hint, |mutator| {
let ticket = mutator
.try_alloc_uninit_slice::<T>(len)
.expect("dedicated oversized chunk sized to fit slice");
ticket.init_with_ptr(f)
})?;
return Ok(unsafe { ptr.as_mut() });
}
self.refill(refill_hint)?;
}
}
#[inline(always)]
fn impl_alloc_slice_fill_iter<T, I: ExactSizeIterator<Item = T>>(&self, iter: I) -> Result<Alloc<'_, [T]>, AllocError> {
let slot = self.alloc_slice_fill_iter_raw::<T, I>(iter)?;
Ok(unsafe { Alloc::from_mut(slot) })
}
#[allow(
clippy::mut_from_ref,
reason = "internal helper hands out a fresh, disjoint arena slot per call; the returned &mut is wrapped in an owning Alloc by impl_alloc_slice_fill_iter"
)]
#[inline(always)]
fn alloc_slice_fill_iter_raw<T, I: ExactSizeIterator<Item = T>>(&self, iter: I) -> Result<&mut [T], AllocError> {
reject_over_aligned::<T>()?;
let len = iter.len();
if len == 0 {
drop(iter);
return Ok(empty_slice::<T>());
}
assume_nonzero_len(len);
let refill_hint = worst_case_slice_payload::<T>(len);
let mut iter = Some(iter);
loop {
if let Some(u) = self.try_reserve_local_slice::<T>(len) {
let it = iter.take().expect("iterator taken twice");
return Ok(u.init_from_iter(it));
}
if self.is_oversized(refill_hint) {
let it = iter.take().expect("iterator taken twice");
let mut ptr = self.alloc_oversized_local_with(refill_hint, |mutator| {
let ticket = mutator
.try_alloc_uninit_slice::<T>(len)
.expect("dedicated oversized chunk sized to fit slice");
ticket.init_from_iter_ptr(it)
})?;
return Ok(unsafe { ptr.as_mut() });
}
self.refill(refill_hint)?;
}
}
}