use core::alloc::Layout;
use core::ptr::{self, NonNull};
use allocator_api2::alloc::{AllocError, Allocator};
use crate::Arena;
use crate::arena::alloc_value::acquire_chunk_ref;
use crate::internal::chunk_ref::ChunkRef;
use crate::internal::constants::max_smart_ptr_align;
const MAX_SMART_PTR_ALIGN: usize = max_smart_ptr_align();
unsafe impl<A: Allocator + Clone> Allocator for &Arena<A> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
if layout.align() >= MAX_SMART_PTR_ALIGN {
return Err(AllocError);
}
if layout.size() == 0 {
let dangling = unsafe { NonNull::new_unchecked(ptr::without_provenance_mut::<u8>(layout.align())) };
return Ok(NonNull::slice_from_raw_parts(dangling, 0));
}
let refill_hint = layout.size().saturating_add(layout.align());
loop {
if let Some((slot, chunk_ptr)) = self.current().try_alloc_with_chunk(layout.size(), layout.align()) {
let chunk_ref = self.acquire_current_chunk_ref(chunk_ptr);
let ptr = slot.as_non_null();
let _ = chunk_ref.forget();
return Ok(NonNull::slice_from_raw_parts(ptr, layout.size()));
}
if self.is_oversized(refill_hint) {
return self.alloc_oversized_shared_with(refill_hint, |mutator, chunk_ptr| {
let (slot, _chunk) = mutator
.try_alloc_with_chunk(layout.size(), layout.align())
.expect("dedicated oversized chunk sized to fit allocation + alignment slack");
let chunk_ref = acquire_chunk_ref::<A>(chunk_ptr);
let ptr = slot.as_non_null();
let _ = chunk_ref.forget();
NonNull::slice_from_raw_parts(ptr, layout.size())
});
}
self.refill(refill_hint)?;
}
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() == 0 {
return;
}
let _ref: ChunkRef<A> = unsafe { ChunkRef::from_value_ptr(ptr) };
}
unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let new = self.allocate(new_layout)?;
let copy_bytes = old_layout.size().min(new_layout.size());
if copy_bytes != 0 {
unsafe {
ptr::copy_nonoverlapping(ptr.as_ptr(), new.cast::<u8>().as_ptr(), copy_bytes);
}
}
unsafe { self.deallocate(ptr, old_layout) };
Ok(new)
}
}
unsafe impl<A: Allocator + Clone> allocator_api2_02::alloc::Allocator for &Arena<A> {
#[inline]
#[allow(clippy::map_err_ignore, reason = "AllocError carries no payload; only the variant is bridged")]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, allocator_api2_02::alloc::AllocError> {
<&Arena<A> as Allocator>::allocate(self, layout).map_err(|_| allocator_api2_02::alloc::AllocError)
}
#[inline]
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
unsafe { <&Arena<A> as Allocator>::deallocate(self, ptr, layout) };
}
#[inline]
#[allow(clippy::map_err_ignore, reason = "AllocError carries no payload; only the variant is bridged")]
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, allocator_api2_02::alloc::AllocError> {
unsafe { <&Arena<A> as Allocator>::grow(self, ptr, old_layout, new_layout) }.map_err(|_| allocator_api2_02::alloc::AllocError)
}
}
#[cfg(test)]
mod tests {
use core::alloc::Layout;
use allocator_api2_02::alloc::Allocator as LegacyAllocator;
use crate::Arena;
#[test]
fn arena_backs_legacy_allocator_api2() {
let arena = Arena::new();
let handle: &Arena = &arena;
let layout = Layout::from_size_align(64, 8).unwrap();
let p = LegacyAllocator::allocate(&handle, layout).expect("legacy allocate");
let new_layout = Layout::from_size_align(128, 8).unwrap();
let p = unsafe { LegacyAllocator::grow(&handle, p.cast::<u8>(), layout, new_layout) }.expect("legacy grow");
unsafe { LegacyAllocator::deallocate(&handle, p.cast::<u8>(), new_layout) };
let over_aligned = Layout::from_size_align(8, super::MAX_SMART_PTR_ALIGN).unwrap();
LegacyAllocator::allocate(&handle, over_aligned).expect_err("over-aligned request must be rejected");
}
}