use core::mem;
use core::pin::Pin;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator};
use super::{Arena, ExpectAlloc};
use crate::Alloc;
use crate::arc::Arc;
use crate::r#box::Box;
use crate::internal::chunk_ref::ChunkRef;
use crate::internal::constants::max_smart_ptr_align;
use crate::internal::thin_dst::{AtomicStrong, LocalStrong};
use crate::internal::uninit::Uninit;
use crate::internal::{Chunk, thin_dst};
use crate::rc::Rc;
#[cfg_attr(test, mutants::skip)] #[inline]
const fn worst_case_payload<T>() -> usize {
mem::size_of::<T>().saturating_add(mem::align_of::<T>())
}
#[cfg_attr(test, mutants::skip)] #[inline]
fn worst_case_strong_payload<S: thin_dst::Strong, T>() -> usize {
let align = mem::align_of::<T>();
let value_bytes = if mem::size_of::<T>() == 0 { 1 } else { mem::size_of::<T>() };
thin_dst::strong_prefix_bytes_for(align, 0)
.saturating_add(value_bytes)
.saturating_add(S::block_align(align))
}
pub(in crate::arena) const MAX_SMART_PTR_ALIGN: usize = max_smart_ptr_align();
impl<A: Allocator + Clone> Arena<A> {
#[inline]
pub fn alloc_arc<T: Send + Sync>(&self, value: T) -> Arc<T, A>
where
A: Send + Sync,
{
self.try_alloc_arc_with::<T, _>(move || value).expect_alloc()
}
#[inline]
pub fn try_alloc_arc<T: Send + Sync>(&self, value: T) -> Result<Arc<T, A>, AllocError>
where
A: Send + Sync,
{
self.try_alloc_arc_with::<T, _>(move || value)
}
#[inline]
pub fn alloc_arc_with<T: Send + Sync, F: FnOnce() -> T>(&self, f: F) -> Arc<T, A>
where
A: Send + Sync,
{
self.try_alloc_arc_with::<T, F>(f).expect_alloc()
}
#[inline]
pub fn try_alloc_arc_with<T: Send + Sync, F: FnOnce() -> T>(&self, f: F) -> Result<Arc<T, A>, AllocError>
where
A: Send + Sync,
{
self.impl_alloc_smart_prefixed_with::<AtomicStrong, T, F>(f)
}
#[inline]
pub fn alloc_rc<T>(&self, value: T) -> Rc<T, A> {
self.try_alloc_rc_with::<T, _>(move || value).expect_alloc()
}
#[inline]
pub fn try_alloc_rc<T>(&self, value: T) -> Result<Rc<T, A>, AllocError> {
self.try_alloc_rc_with::<T, _>(move || value)
}
#[inline]
pub fn alloc_rc_with<T, F: FnOnce() -> T>(&self, f: F) -> Rc<T, A> {
self.try_alloc_rc_with::<T, F>(f).expect_alloc()
}
#[inline]
pub fn try_alloc_rc_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<Rc<T, A>, AllocError> {
self.impl_alloc_smart_prefixed_with::<LocalStrong, T, F>(f)
}
#[inline]
pub fn alloc_box<T>(&self, value: T) -> Box<T, A> {
(self.impl_alloc_box_with::<T, _>(move || value)).expect_alloc()
}
#[inline]
pub fn try_alloc_box<T>(&self, value: T) -> Result<Box<T, A>, AllocError> {
self.impl_alloc_box_with::<T, _>(move || value)
}
#[inline]
pub fn alloc_box_with<T, F: FnOnce() -> T>(&self, f: F) -> Box<T, A> {
(self.impl_alloc_box_with::<T, F>(f)).expect_alloc()
}
#[inline]
pub fn try_alloc_box_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<Box<T, A>, AllocError> {
self.impl_alloc_box_with::<T, F>(f)
}
#[must_use]
#[inline]
pub fn alloc_box_pin<T>(&self, value: T) -> Pin<Box<T, A>>
where
A: 'static,
{
Box::into_pin(self.alloc_box(value))
}
#[inline]
pub fn try_alloc_box_pin<T>(&self, value: T) -> Result<Pin<Box<T, A>>, AllocError>
where
A: 'static,
{
self.try_alloc_box(value).map(Box::into_pin)
}
#[must_use]
#[inline]
pub fn alloc_box_pin_with<T, F: FnOnce() -> T>(&self, f: F) -> Pin<Box<T, A>>
where
A: 'static,
{
Box::into_pin(self.alloc_box_with(f))
}
#[inline]
pub fn try_alloc_box_pin_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<Pin<Box<T, A>>, AllocError>
where
A: 'static,
{
self.try_alloc_box_with(f).map(Box::into_pin)
}
#[must_use]
#[inline]
pub fn alloc_arc_pin<T: Send + Sync>(&self, value: T) -> Pin<Arc<T, A>>
where
A: Send + Sync + 'static,
{
Arc::into_pin(self.alloc_arc(value))
}
#[inline]
pub fn try_alloc_arc_pin<T: Send + Sync>(&self, value: T) -> Result<Pin<Arc<T, A>>, AllocError>
where
A: Send + Sync + 'static,
{
self.try_alloc_arc(value).map(Arc::into_pin)
}
#[must_use]
#[inline]
pub fn alloc_arc_pin_with<T: Send + Sync, F: FnOnce() -> T>(&self, f: F) -> Pin<Arc<T, A>>
where
A: Send + Sync + 'static,
{
Arc::into_pin(self.alloc_arc_with(f))
}
#[inline]
pub fn try_alloc_arc_pin_with<T: Send + Sync, F: FnOnce() -> T>(&self, f: F) -> Result<Pin<Arc<T, A>>, AllocError>
where
A: Send + Sync + 'static,
{
self.try_alloc_arc_with(f).map(Arc::into_pin)
}
#[must_use]
#[inline]
pub fn alloc_rc_pin<T>(&self, value: T) -> Pin<Rc<T, A>>
where
A: 'static,
{
Rc::into_pin(self.alloc_rc(value))
}
#[inline]
pub fn try_alloc_rc_pin<T>(&self, value: T) -> Result<Pin<Rc<T, A>>, AllocError>
where
A: 'static,
{
self.try_alloc_rc(value).map(Rc::into_pin)
}
#[must_use]
#[inline]
pub fn alloc_rc_pin_with<T, F: FnOnce() -> T>(&self, f: F) -> Pin<Rc<T, A>>
where
A: 'static,
{
Rc::into_pin(self.alloc_rc_with(f))
}
#[inline]
pub fn try_alloc_rc_pin_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<Pin<Rc<T, A>>, AllocError>
where
A: 'static,
{
self.try_alloc_rc_with(f).map(Rc::into_pin)
}
#[inline]
pub fn alloc<T>(&self, value: T) -> Alloc<'_, T> {
self.impl_alloc_value_with::<T, _>(move || value).expect_alloc()
}
#[inline]
pub fn try_alloc<T>(&self, value: T) -> Result<Alloc<'_, T>, AllocError> {
self.impl_alloc_value_with::<T, _>(move || value)
}
#[inline]
pub fn alloc_with<T, F: FnOnce() -> T>(&self, f: F) -> Alloc<'_, T> {
self.impl_alloc_value_with::<T, F>(f).expect_alloc()
}
#[inline]
pub fn try_alloc_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<Alloc<'_, T>, AllocError> {
self.impl_alloc_value_with::<T, F>(f)
}
#[inline(always)]
fn impl_alloc_value_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<Alloc<'_, T>, AllocError> {
let slot = self.alloc_value_with_raw::<T, F>(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_value_with"
)]
#[inline(always)]
fn alloc_value_with_raw<T, F: FnOnce() -> T>(&self, f: F) -> Result<&mut T, AllocError> {
if const { mem::align_of::<T>() >= MAX_SMART_PTR_ALIGN } {
return Err(AllocError);
}
if let Some(u) = self.try_reserve_local::<T>() {
return Ok(u.init(f()));
}
self.alloc_value_refill_with::<T, F>(f)
}
#[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_value_refill_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<&mut T, AllocError> {
loop {
if let Some(u) = self.try_reserve_local::<T>() {
return Ok(u.init(f()));
}
let wcp = worst_case_payload::<T>();
if self.is_oversized(wcp) {
return self.alloc_oversized_value_with::<T, F>(wcp, f);
}
self.refill(wcp)?;
}
}
#[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_value_with<T, F: FnOnce() -> T>(&self, wcp: usize, f: F) -> Result<&mut T, AllocError> {
let mutator = self.acquire_oversized_local_mutator(wcp)?;
let ticket = mutator
.try_alloc_uninit::<T>()
.expect("dedicated oversized chunk sized to fit one value");
let value_ptr = ticket.init_raw(f());
self.retain_oversized_local_mutator(mutator);
Ok(unsafe { &mut *value_ptr.as_ptr() })
}
#[inline(always)]
fn impl_alloc_box_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<Box<T, A>, AllocError> {
self.impl_alloc_smart_with::<T, F>(f)
.map(|ptr| unsafe { Box::from_raw(ptr.cast::<u8>()) })
}
#[inline(always)]
fn impl_alloc_smart_prefixed_with<S: thin_dst::Strong, T, F: FnOnce() -> T>(&self, f: F) -> Result<S::Ptr<T, A>, AllocError> {
let thin = self.alloc_smart_prefixed_with_raw::<S, T, F>(f)?;
Ok(unsafe { S::adopt::<T, A>(thin) })
}
#[inline(always)]
#[cfg_attr(test, mutants::skip)] fn alloc_smart_prefixed_with_raw<S: thin_dst::Strong, T, F: FnOnce() -> T>(&self, f: F) -> Result<NonNull<u8>, AllocError> {
if const { mem::align_of::<T>() >= MAX_SMART_PTR_ALIGN } {
return Err(AllocError);
}
let mut f = Some(f);
loop {
if let Some((uninit, chunk_ptr)) = self.try_reserve_arc_value::<S, T>() {
let chunk_ref = self.acquire_current_chunk_ref(chunk_ptr);
let f = f.take().expect("closure taken twice");
let ptr = init_smart_slot::<T, A, _>(uninit, chunk_ref, f);
return Ok(ptr.cast::<u8>());
}
let wcp = worst_case_strong_payload::<S, T>();
if self.is_oversized(wcp) {
let f = f.take().expect("closure taken twice");
return self.alloc_oversized_smart_prefixed_with::<S, T, F>(wcp, f);
}
self.refill(wcp)?;
}
}
#[inline(always)]
#[cfg_attr(test, mutants::skip)] fn impl_alloc_smart_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<NonNull<T>, AllocError> {
if const { mem::align_of::<T>() >= MAX_SMART_PTR_ALIGN } {
return Err(AllocError);
}
loop {
if const { mem::size_of::<T>() == 0 } && self.current().try_alloc(1, 1).is_none() {
self.refill(worst_case_payload::<T>())?;
continue;
}
if let Some((uninit, chunk_ptr)) = self.try_reserve_shared::<T>() {
let chunk_ref = self.acquire_current_chunk_ref(chunk_ptr);
return Ok(init_smart_slot::<T, A, F>(uninit, chunk_ref, f));
}
let wcp = worst_case_payload::<T>();
if self.is_oversized(wcp) {
return self.alloc_oversized_smart_with::<T, F>(wcp, f);
}
self.refill(wcp)?;
}
}
#[cold]
#[inline(never)]
fn alloc_oversized_smart_with<T, F: FnOnce() -> T>(&self, wcp: usize, f: F) -> Result<NonNull<T>, AllocError> {
let (mutator, chunk_ptr) = self.acquire_oversized_shared_mutator(wcp)?;
let ticket = mutator
.try_alloc_uninit::<T>()
.expect("dedicated oversized chunk sized to fit one value");
let chunk_ref = acquire_chunk_ref::<A>(chunk_ptr);
let ptr = init_smart_slot::<T, A, F>(ticket, chunk_ref, f);
drop(mutator);
Ok(ptr)
}
#[cold]
#[inline(never)]
fn alloc_oversized_smart_prefixed_with<S: thin_dst::Strong, T, F: FnOnce() -> T>(
&self,
wcp: usize,
f: F,
) -> Result<NonNull<u8>, AllocError> {
let (mutator, chunk_ptr) = self.acquire_oversized_shared_mutator(wcp)?;
let (ticket, _chunk) = mutator
.try_alloc_arc_value::<S, T>()
.expect("dedicated oversized chunk sized to fit one smart value + strong prefix");
let chunk_ref = acquire_chunk_ref::<A>(chunk_ptr);
let ptr = init_smart_slot::<T, A, F>(ticket, chunk_ref, f);
drop(mutator);
Ok(ptr.cast::<u8>())
}
}
#[inline(always)]
fn init_smart_slot<T, A: Allocator + Clone, F: FnOnce() -> T>(uninit: Uninit<'_, T>, chunk_ref: ChunkRef<A>, f: F) -> NonNull<T> {
let value = f();
let _ = chunk_ref.forget();
uninit.init_raw(value)
}
#[inline(always)]
pub(crate) fn acquire_chunk_ref<A: Allocator + Clone>(chunk_ptr: NonNull<Chunk<A>>) -> ChunkRef<A> {
unsafe {
chunk_ptr.as_ref().inc_ref();
ChunkRef::<A>::adopt(chunk_ptr)
}
}