use core::mem;
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
}
}
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, 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, 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>(&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>(&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, 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, 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();
let refill_hint = worst_case_slice_payload::<T>(len);
loop {
if let Some(u) = self.try_reserve_local_slice::<T>(len) {
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of_val(src));
return Ok(u.init_copy_from_slice(src));
}
if self.is_oversized_local(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");
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of_val(src));
ticket.init_copy_from_slice_ptr(src)
})?;
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_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();
let refill_hint = worst_case_slice_payload::<T>(len);
loop {
if const { mem::needs_drop::<T>() } {
if let Some(u) = self.try_reserve_local_slice_with_drop::<T>(len) {
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of_val(src));
return Ok(u.init_clone_from_slice(src));
}
} else if let Some(u) = self.try_reserve_local_slice::<T>(len) {
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of_val(src));
return Ok(u.init_clone_from_slice(src));
}
if self.is_oversized_local(refill_hint) {
let mut ptr = self.alloc_oversized_local_with(refill_hint, |mutator| {
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of_val(src));
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(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_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)?;
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");
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of::<T>() * len);
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");
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of::<T>() * len);
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| {
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of::<T>() * len);
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)?;
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");
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of::<T>() * len);
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");
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of::<T>() * len);
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| {
#[cfg(feature = "stats")]
self.record_alloc(mem::size_of::<T>() * len);
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)?;
}
}
}