use core::mem;
use core::ptr::{self, NonNull};
use allocator_api2::alloc::{AllocError, Allocator};
use super::Arena;
use super::alloc_value::acquire_chunk_ref;
use crate::internal::chunk_ref::ChunkRef;
use crate::internal::thin_dst;
pub(in crate::arena) const PREFIX_BYTES: usize = mem::size_of::<usize>();
#[inline]
#[cfg_attr(test, mutants::skip)] pub(in crate::arena) fn worst_case_thin_slice_payload<T>(len: usize) -> usize {
let elem_size = mem::size_of::<T>();
let elem_align = mem::align_of::<T>();
let payload_offset = PREFIX_BYTES.max(elem_align);
let value_bytes = elem_size.saturating_mul(len);
payload_offset
.saturating_add(value_bytes)
.saturating_add(elem_align)
}
impl<A: Allocator + Clone> Arena<A> {
#[inline(always)]
pub(in crate::arena) fn impl_alloc_prefixed_shared<T: Copy>(&self, src: &[T]) -> Result<NonNull<T>, AllocError> {
const {
assert!(
mem::align_of::<T>() <= mem::align_of::<usize>(),
"impl_alloc_prefixed_shared: T's align must not exceed usize's align (PREFIX_BYTES would otherwise not guarantee payload alignment)",
);
}
let elem_size = mem::size_of::<T>();
let elem_align = mem::align_of::<T>();
let len = src.len();
let payload_bytes = len.checked_mul(elem_size).ok_or(AllocError)?.max(elem_align);
let total = PREFIX_BYTES.checked_add(payload_bytes).ok_or(AllocError)?;
loop {
if let Some((uninit, chunk_ptr)) = self.current().try_alloc_with_chunk(total, elem_align) {
let chunk_ref: ChunkRef<A> = self.acquire_current_chunk_ref(chunk_ptr);
let payload = write_prefixed_payload::<T>(uninit.as_non_null(), src);
let _ = chunk_ref.forget();
return Ok(payload);
}
if self.is_oversized(total) {
return self.alloc_oversized_shared_with(total, |mutator, chunk_ptr| {
let (base, _chunk_unused) = mutator
.try_alloc_with_chunk(total, elem_align)
.expect("dedicated oversized chunk sized to fit prefixed payload");
let chunk_ref: ChunkRef<A> = acquire_chunk_ref::<A>(chunk_ptr);
let payload = write_prefixed_payload::<T>(base.as_non_null(), src);
let _ = chunk_ref.forget();
payload
});
}
self.refill(total)?;
}
}
}
impl<A: Allocator + Clone> Arena<A> {
#[inline(always)]
pub(in crate::arena) fn impl_alloc_prefixed_shared_arc<S: crate::internal::thin_dst::Strong, T: Copy>(
&self,
src: &[T],
) -> Result<NonNull<T>, AllocError> {
const {
assert!(
mem::align_of::<T>() <= mem::align_of::<usize>(),
"impl_alloc_prefixed_shared_arc: T's align must not exceed usize's align",
);
}
let len = src.len();
let payload_bytes = mem::size_of_val(src);
if let Some((uninit, chunk_ptr)) = unsafe { self.try_reserve_arc_slice_with_size::<S, T>(len, payload_bytes) } {
let chunk_ref: ChunkRef<A> = self.acquire_current_chunk_ref(chunk_ptr);
let slice_ptr = uninit.init_copy_from_slice_ptr(src);
let _ = chunk_ref.forget();
return Ok(slice_ptr.cast::<T>());
}
self.alloc_prefixed_shared_arc_refill::<S, T>(src, len, payload_bytes)
}
#[cold]
#[inline(never)]
fn alloc_prefixed_shared_arc_refill<S: crate::internal::thin_dst::Strong, T: Copy>(
&self,
src: &[T],
len: usize,
payload_bytes: usize,
) -> Result<NonNull<T>, AllocError> {
let bytes_needed = worst_case_strong_slice_payload::<S, T>(len);
loop {
let reserved = unsafe { self.try_reserve_arc_slice_with_size::<S, T>(len, payload_bytes) };
if let Some((uninit, chunk_ptr)) = reserved {
let chunk_ref: ChunkRef<A> = self.acquire_current_chunk_ref(chunk_ptr);
let slice_ptr = uninit.init_copy_from_slice_ptr(src);
let _ = chunk_ref.forget();
return Ok(slice_ptr.cast::<T>());
}
if self.is_oversized(bytes_needed) {
return self.alloc_oversized_shared_with(bytes_needed, |mutator, chunk_ptr| {
let (ticket, _chunk) = mutator
.try_alloc_arc_slice::<S, T>(len)
.expect("dedicated oversized chunk sized to fit prefixed Arc payload");
let chunk_ref: ChunkRef<A> = acquire_chunk_ref::<A>(chunk_ptr);
let slice_ptr = ticket.init_copy_from_slice_ptr(src);
let _ = chunk_ref.forget();
slice_ptr.cast::<T>()
});
}
self.refill(bytes_needed)?;
}
}
}
#[cfg_attr(test, mutants::skip)] #[inline]
pub(crate) fn worst_case_strong_slice_payload<S: thin_dst::Strong, T>(len: usize) -> usize {
let align = mem::align_of::<T>();
let value_bytes = mem::size_of::<T>().saturating_mul(len).max(1);
thin_dst::strong_prefix_bytes_for(align, mem::size_of::<usize>())
.saturating_add(value_bytes)
.saturating_add(S::block_align(align))
}
#[inline(always)]
fn write_prefixed_payload<T: Copy>(base: NonNull<u8>, src: &[T]) -> NonNull<T> {
let len = src.len();
unsafe {
ptr::write_unaligned(base.as_ptr().cast::<usize>(), len);
let payload_ptr = base.as_ptr().add(PREFIX_BYTES).cast::<T>();
ptr::copy_nonoverlapping(src.as_ptr(), payload_ptr, len);
NonNull::new_unchecked(payload_ptr)
}
}