use core::{alloc::Layout, ops::Range, ptr::NonNull};
use crate::{
BaseAllocator, Bump, BumpScope, Checkpoint, WithoutDealloc, WithoutShrink,
alloc::{AllocError, Allocator},
layout::CustomLayout,
raw_bump::RawChunk,
settings::BumpAllocatorSettings,
stats::AnyStats,
traits::{assert_dyn_compatible, assert_implements},
};
pub trait Sealed {}
impl<B: Sealed + ?Sized> Sealed for &B {}
impl<B: Sealed + ?Sized> Sealed for &mut B {}
impl<B: Sealed> Sealed for WithoutDealloc<B> {}
impl<B: Sealed> Sealed for WithoutShrink<B> {}
impl<A, S> Sealed for Bump<A, S>
where
A: BaseAllocator<S::GuaranteedAllocated>,
S: BumpAllocatorSettings,
{
}
impl<A, S> Sealed for BumpScope<'_, A, S>
where
A: BaseAllocator<S::GuaranteedAllocated>,
S: BumpAllocatorSettings,
{
}
pub unsafe trait BumpAllocatorCore: Allocator + Sealed {
#[must_use]
fn any_stats(&self) -> AnyStats<'_>;
#[must_use]
fn checkpoint(&self) -> Checkpoint;
unsafe fn reset_to(&self, checkpoint: Checkpoint);
#[must_use]
fn is_claimed(&self) -> bool;
fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError>;
unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8>;
fn prepare_allocation_rev(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError>;
unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8>;
}
assert_dyn_compatible!(BumpAllocatorCore);
assert_implements! {
[BumpAllocatorCore + ?Sized]
Bump
&Bump
&mut Bump
BumpScope
&BumpScope
&mut BumpScope
dyn BumpAllocatorCore
&dyn BumpAllocatorCore
&mut dyn BumpAllocatorCore
dyn BumpAllocatorCoreScope
&dyn BumpAllocatorCoreScope
&mut dyn BumpAllocatorCoreScope
dyn MutBumpAllocatorCore
&dyn MutBumpAllocatorCore
&mut dyn MutBumpAllocatorCore
dyn MutBumpAllocatorCoreScope
&dyn MutBumpAllocatorCoreScope
&mut dyn MutBumpAllocatorCoreScope
}
macro_rules! impl_for_ref {
($($ty:ty)*) => {
$(
unsafe impl<B: BumpAllocatorCore + ?Sized> BumpAllocatorCore for $ty {
#[inline(always)]
fn any_stats(&self) -> AnyStats<'_> {
B::any_stats(self)
}
#[inline(always)]
fn checkpoint(&self) -> Checkpoint {
B::checkpoint(self)
}
#[inline(always)]
unsafe fn reset_to(&self, checkpoint: Checkpoint) {
unsafe { B::reset_to(self, checkpoint) };
}
#[inline(always)]
fn is_claimed(&self) -> bool {
B::is_claimed(self)
}
#[inline(always)]
fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
B::prepare_allocation(self, layout)
}
#[inline(always)]
unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { B::allocate_prepared(self, layout, range) }
}
#[inline(always)]
fn prepare_allocation_rev(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
B::prepare_allocation_rev(self, layout)
}
#[inline(always)]
unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { B::allocate_prepared_rev(self, layout, range) }
}
}
)*
};
}
impl_for_ref! {
&B
&mut B
}
unsafe impl<B: BumpAllocatorCore> BumpAllocatorCore for WithoutDealloc<B> {
#[inline(always)]
fn any_stats(&self) -> AnyStats<'_> {
B::any_stats(&self.0)
}
#[inline(always)]
fn checkpoint(&self) -> Checkpoint {
B::checkpoint(&self.0)
}
#[inline(always)]
unsafe fn reset_to(&self, checkpoint: Checkpoint) {
unsafe { B::reset_to(&self.0, checkpoint) };
}
#[inline(always)]
fn is_claimed(&self) -> bool {
B::is_claimed(&self.0)
}
#[inline(always)]
fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
B::prepare_allocation(&self.0, layout)
}
#[inline(always)]
unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { B::allocate_prepared(&self.0, layout, range) }
}
#[inline(always)]
fn prepare_allocation_rev(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
B::prepare_allocation_rev(&self.0, layout)
}
#[inline(always)]
unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { B::allocate_prepared_rev(&self.0, layout, range) }
}
}
unsafe impl<B: BumpAllocatorCore> BumpAllocatorCore for WithoutShrink<B> {
#[inline(always)]
fn any_stats(&self) -> AnyStats<'_> {
B::any_stats(&self.0)
}
#[inline(always)]
fn checkpoint(&self) -> Checkpoint {
B::checkpoint(&self.0)
}
#[inline(always)]
unsafe fn reset_to(&self, checkpoint: Checkpoint) {
unsafe { B::reset_to(&self.0, checkpoint) };
}
#[inline(always)]
fn is_claimed(&self) -> bool {
B::is_claimed(&self.0)
}
#[inline(always)]
fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
B::prepare_allocation(&self.0, layout)
}
#[inline(always)]
unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { B::allocate_prepared(&self.0, layout, range) }
}
#[inline(always)]
fn prepare_allocation_rev(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
B::prepare_allocation_rev(&self.0, layout)
}
#[inline(always)]
unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { B::allocate_prepared_rev(&self.0, layout, range) }
}
}
unsafe impl<A, S> BumpAllocatorCore for Bump<A, S>
where
A: BaseAllocator<S::GuaranteedAllocated>,
S: BumpAllocatorSettings,
{
#[inline(always)]
fn any_stats(&self) -> AnyStats<'_> {
self.as_scope().any_stats()
}
#[inline(always)]
fn checkpoint(&self) -> Checkpoint {
self.as_scope().checkpoint()
}
#[inline(always)]
unsafe fn reset_to(&self, checkpoint: Checkpoint) {
unsafe { self.as_scope().reset_to(checkpoint) };
}
#[inline(always)]
fn is_claimed(&self) -> bool {
self.as_scope().is_claimed()
}
#[inline(always)]
fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
self.as_scope().prepare_allocation(layout)
}
#[inline(always)]
unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { self.as_scope().allocate_prepared(layout, range) }
}
#[inline(always)]
fn prepare_allocation_rev(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
self.as_scope().prepare_allocation_rev(layout)
}
#[inline(always)]
unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
unsafe { self.as_scope().allocate_prepared_rev(layout, range) }
}
}
unsafe impl<A, S> BumpAllocatorCore for BumpScope<'_, A, S>
where
A: BaseAllocator<S::GuaranteedAllocated>,
S: BumpAllocatorSettings,
{
#[inline(always)]
fn any_stats(&self) -> AnyStats<'_> {
self.stats().into()
}
#[inline(always)]
fn checkpoint(&self) -> Checkpoint {
self.raw.checkpoint()
}
#[inline]
unsafe fn reset_to(&self, checkpoint: Checkpoint) {
unsafe { self.raw.reset_to(checkpoint) }
}
#[inline(always)]
fn is_claimed(&self) -> bool {
self.raw.is_claimed()
}
#[inline(always)]
fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
#[cold]
#[inline(never)]
unsafe fn prepare_allocation_in_another_chunk<A, S>(
this: &BumpScope<'_, A, S>,
layout: Layout,
) -> Result<Range<NonNull<u8>>, AllocError>
where
A: BaseAllocator<S::GuaranteedAllocated>,
S: BumpAllocatorSettings,
{
unsafe {
this.raw
.in_another_chunk(CustomLayout(layout), RawChunk::prepare_allocation_range)
}
}
match self.raw.chunk.get().prepare_allocation_range(CustomLayout(layout)) {
Some(ptr) => Ok(ptr),
None => unsafe { prepare_allocation_in_another_chunk(self, layout) },
}
}
#[inline(always)]
unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
debug_assert_eq!(range.start.addr().get() % layout.align(), 0);
debug_assert_eq!(range.end.addr().get() % layout.align(), 0);
debug_assert_eq!(layout.size() % layout.align(), 0);
unsafe {
let chunk = self.raw.chunk.get().as_non_dummy_unchecked();
if S::UP {
let end = range.start.add(layout.size());
chunk.set_pos_addr_and_align(end.addr().get());
range.start
} else {
let src = range.start;
let dst_end = range.end;
let dst = dst_end.sub(layout.size());
src.copy_to(dst, layout.size());
chunk.set_pos_addr_and_align(dst.addr().get());
dst
}
}
}
#[inline(always)]
fn prepare_allocation_rev(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
self.prepare_allocation(layout)
}
#[inline(always)]
unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
debug_assert_eq!(range.start.addr().get() % layout.align(), 0);
debug_assert_eq!(range.end.addr().get() % layout.align(), 0);
debug_assert_eq!(layout.size() % layout.align(), 0);
unsafe {
let chunk = self.raw.chunk.get().as_non_dummy_unchecked();
if S::UP {
let dst = range.start;
let dst_end = dst.add(layout.size());
let src_end = range.end;
let src = src_end.sub(layout.size());
src.copy_to(dst, layout.size());
chunk.set_pos_addr_and_align(dst_end.addr().get());
dst
} else {
let dst_end = range.end;
let dst = dst_end.sub(layout.size());
chunk.set_pos_addr_and_align(dst.addr().get());
dst
}
}
}
}