use core::mem;
use core::ptr::{self, NonNull};
use allocator_api2::alloc::{AllocError, Allocator};
use super::Arena;
use super::alloc_value::acquire_shared_chunk_ref;
use crate::internal::chunk_ref::ChunkRef;
pub(crate) const PREFIX_BYTES: usize = mem::size_of::<usize>();
#[inline]
#[cfg_attr(test, mutants::skip)] pub(crate) 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);
let base = payload_offset
.saturating_add(value_bytes)
.saturating_add(elem_align);
if mem::needs_drop::<T>() {
base.saturating_add(mem::size_of::<crate::internal::drop_entry::DropEntry>())
.saturating_add(mem::align_of::<crate::internal::drop_entry::DropEntry>())
} else {
base
}
}
impl<A: Allocator + Clone> Arena<A> {
#[inline(always)]
pub(crate) 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_shared().try_alloc_with_chunk(total, elem_align) {
let chunk_ref: ChunkRef<A> = acquire_shared_chunk_ref::<A>(chunk_ptr);
let payload = write_prefixed_payload::<T>(uninit.as_non_null(), src);
let _ = chunk_ref.forget();
#[cfg(feature = "stats")]
self.record_alloc(len.saturating_mul(elem_size));
return Ok(payload);
}
if self.is_oversized_shared(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_shared_chunk_ref::<A>(chunk_ptr);
let payload = write_prefixed_payload::<T>(base.as_non_null(), src);
let _ = chunk_ref.forget();
#[cfg(feature = "stats")]
self.record_alloc(len.saturating_mul(elem_size));
payload
});
}
self.refill_shared(total)?;
}
}
}
#[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)
}
}
#[inline]
#[cfg(feature = "utf16")]
pub(crate) unsafe fn read_prefix_len<T>(payload_ptr: NonNull<T>) -> usize {
unsafe { ptr::read_unaligned(payload_ptr.as_ptr().cast::<u8>().sub(PREFIX_BYTES).cast::<usize>()) }
}