use core::hint::assert_unchecked;
use core::mem;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator};
use super::{Arena, ExpectAlloc};
use crate::internal::constants::CHUNK_ALIGN;
use crate::internal::drop_entry::DropEntry;
#[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 reject_drop_slice_too_long<T>(len: usize) -> Result<(), AllocError> {
if mem::needs_drop::<T>() && len > u16::MAX as usize {
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);
let base = value_bytes.saturating_add(mem::align_of::<T>());
if mem::needs_drop::<T>() {
base.saturating_add(mem::size_of::<DropEntry>())
} else {
base
}
}
#[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]
#[allow(clippy::mut_from_ref, reason = "Simple references: see Self::try_alloc_with")]
#[inline]
pub fn alloc_slice_copy<T: Copy>(&self, slice: impl AsRef<[T]>) -> &mut [T] {
(self.impl_alloc_slice_copy::<T>(slice.as_ref())).expect_alloc()
}
#[allow(clippy::mut_from_ref, reason = "simple references: see Self::try_alloc_with")]
#[inline]
pub fn try_alloc_slice_copy<T: Copy>(&self, slice: impl AsRef<[T]>) -> Result<&mut [T], AllocError> {
self.impl_alloc_slice_copy::<T>(slice.as_ref())
}
#[must_use]
#[inline]
pub fn alloc_slice_fill_with<T: Send, F: FnMut(usize) -> T>(&self, len: usize, f: F) -> &mut [T] {
(self.impl_alloc_slice_fill_with::<T, F>(len, f)).expect_alloc()
}
#[allow(clippy::mut_from_ref, reason = "simple references: see Self::try_alloc_with")]
#[inline]
pub fn try_alloc_slice_fill_with<T: Send, F: FnMut(usize) -> T>(&self, len: usize, f: F) -> Result<&mut [T], AllocError> {
self.impl_alloc_slice_fill_with::<T, F>(len, f)
}
#[must_use]
#[allow(clippy::mut_from_ref, reason = "Simple references: see Self::try_alloc_with")]
#[inline]
pub fn alloc_slice_clone<T: Clone + Send>(&self, slice: impl AsRef<[T]>) -> &mut [T] {
(self.impl_alloc_slice_clone::<T>(slice.as_ref())).expect_alloc()
}
#[allow(clippy::mut_from_ref, reason = "simple references: see Self::try_alloc_with")]
#[inline]
pub fn try_alloc_slice_clone<T: Clone + Send>(&self, slice: impl AsRef<[T]>) -> Result<&mut [T], AllocError> {
self.impl_alloc_slice_clone::<T>(slice.as_ref())
}
#[must_use]
#[inline]
pub fn alloc_slice_fill_iter<T: Send, I>(&self, iter: I) -> &mut [T]
where
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
(self.impl_alloc_slice_fill_iter::<T, I::IntoIter>(iter.into_iter())).expect_alloc()
}
#[inline]
#[allow(clippy::mut_from_ref, reason = "see `try_alloc_with`")]
pub fn try_alloc_slice_fill_iter<T: Send, I>(&self, iter: I) -> Result<&mut [T], AllocError>
where
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
self.impl_alloc_slice_fill_iter::<T, I::IntoIter>(iter.into_iter())
}
#[allow(clippy::mut_from_ref, reason = "Simple references: see Self::try_alloc_with")]
#[inline(always)]
fn impl_alloc_slice_copy<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 = "Simple references: see Self::try_alloc_with")]
#[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_local(refill_hint) {
return Ok(Some(self.alloc_oversized_slice_copy::<T>(refill_hint, src)?));
}
self.refill_local(refill_hint)?;
Ok(None)
}
#[cold]
#[inline(never)]
#[allow(clippy::mut_from_ref, reason = "Simple references: see Self::try_alloc_with")]
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() })
}
#[allow(clippy::mut_from_ref, reason = "Simple references: see Self::try_alloc_with")]
#[inline(always)]
fn impl_alloc_slice_clone<T: Clone>(&self, src: &[T]) -> Result<&mut [T], AllocError> {
reject_over_aligned::<T>()?;
reject_drop_slice_too_long::<T>(src.len())?;
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 const { mem::needs_drop::<T>() } {
if let Some(u) = self.try_reserve_local_slice_with_drop::<T>(len) {
return Ok(u.init_clone_from_slice(src));
}
} else 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 = "Simple references: see Self::try_alloc_with")]
#[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_local(refill_hint) {
let mut ptr = self.alloc_oversized_local_with(refill_hint, |mutator| {
if const { mem::needs_drop::<T>() } {
let ticket = mutator
.try_alloc_uninit_slice_with_drop::<T>(len)
.expect("dedicated oversized chunk sized to fit slice + drop entry");
ticket.init_with_ptr(|i| src[i].clone())
} else {
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_local(refill_hint)?;
Ok(None)
}
#[allow(clippy::mut_from_ref, reason = "Simple references: see Self::try_alloc_with")]
#[inline(always)]
fn impl_alloc_slice_fill_with<T, F: FnMut(usize) -> T>(&self, len: usize, f: F) -> Result<&mut [T], AllocError> {
reject_over_aligned::<T>()?;
reject_drop_slice_too_long::<T>(len)?;
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 const { mem::needs_drop::<T>() } {
if let Some(u) = self.try_reserve_local_slice_with_drop::<T>(len) {
let f = f.take().expect("with closure taken twice");
return Ok(u.init_with(f));
}
} else 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_local(refill_hint) {
let f = f.take().expect("with closure taken twice");
let mut ptr = self.alloc_oversized_local_with(refill_hint, |mutator| {
if const { mem::needs_drop::<T>() } {
let ticket = mutator
.try_alloc_uninit_slice_with_drop::<T>(len)
.expect("dedicated oversized chunk sized to fit slice + drop entry");
ticket.init_with_ptr(f)
} else {
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_local(refill_hint)?;
}
}
#[allow(clippy::mut_from_ref, reason = "Simple references: see Self::try_alloc_with")]
#[inline(always)]
fn impl_alloc_slice_fill_iter<T, I: ExactSizeIterator<Item = T>>(&self, iter: I) -> Result<&mut [T], AllocError> {
reject_over_aligned::<T>()?;
let len = iter.len();
reject_drop_slice_too_long::<T>(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 const { mem::needs_drop::<T>() } {
if let Some(u) = self.try_reserve_local_slice_with_drop::<T>(len) {
let it = iter.take().expect("iterator taken twice");
return Ok(u.init_from_iter(it));
}
} else 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_local(refill_hint) {
let mut it = iter.take().expect("iterator taken twice");
let mut ptr = self.alloc_oversized_local_with(refill_hint, |mutator| {
if const { mem::needs_drop::<T>() } {
let ticket = mutator
.try_alloc_uninit_slice_with_drop::<T>(len)
.expect("dedicated oversized chunk sized to fit slice + drop entry");
ticket.init_with_ptr(|_| it.next().expect("ExactSizeIterator yielded fewer elements than reported"))
} else {
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_local(refill_hint)?;
}
}
}