use core::cell::Cell;
use core::ptr::NonNull;
use allocator_api2::alloc::Allocator;
use crate::internal::constants::LARGE;
use crate::internal::drop_list::DropEntry as InnerDropEntry;
use crate::internal::local_chunk::{LocalChunk, max_bump_extent as local_max_bump_extent};
use crate::internal::shared_chunk::{SharedChunk, max_bump_extent as shared_max_bump_extent};
pub(super) trait ChunkKind {
unsafe fn data_ptr_of(chunk: NonNull<Self>) -> NonNull<u8>;
fn max_bump_extent() -> usize;
unsafe fn capacity_of(chunk: NonNull<Self>) -> usize;
}
impl<A: Allocator + Clone> ChunkKind for LocalChunk<A> {
#[inline(always)]
unsafe fn data_ptr_of(chunk: NonNull<Self>) -> NonNull<u8> {
unsafe { Self::data_ptr(chunk) }
}
#[inline(always)]
fn max_bump_extent() -> usize {
local_max_bump_extent::<A>()
}
#[inline(always)]
unsafe fn capacity_of(chunk: NonNull<Self>) -> usize {
unsafe { chunk.as_ref() }.capacity
}
}
impl<A: Allocator + Clone> ChunkKind for SharedChunk<A> {
#[inline(always)]
unsafe fn data_ptr_of(chunk: NonNull<Self>) -> NonNull<u8> {
unsafe { Self::data_ptr(chunk) }
}
#[inline(always)]
fn max_bump_extent() -> usize {
shared_max_bump_extent::<A>()
}
#[inline(always)]
unsafe fn capacity_of(chunk: NonNull<Self>) -> usize {
unsafe { chunk.as_ref() }.capacity
}
}
pub(super) struct CurrentChunk<C: ChunkKind + ?Sized> {
pub(super) chunk: Cell<Option<NonNull<C>>>,
pub(super) data_ptr: Cell<NonNull<u8>>,
pub(super) drop_back: Cell<NonNull<u8>>,
pub(super) smart_pointers_issued: Cell<usize>,
}
impl<C: ChunkKind + ?Sized> Default for CurrentChunk<C> {
fn default() -> Self {
Self {
chunk: Cell::new(None),
data_ptr: Cell::new(NonNull::dangling()),
drop_back: Cell::new(NonNull::dangling()),
smart_pointers_issued: Cell::new(0),
}
}
}
impl<C: ChunkKind + ?Sized> CurrentChunk<C> {
#[inline(always)]
pub(super) unsafe fn chunk_assume_present(&self) -> NonNull<C> {
unsafe { self.chunk.get().unwrap_unchecked() }
}
#[inline]
pub(super) unsafe fn drop_count(&self, chunk: NonNull<C>) -> u16 {
let cap = unsafe { C::capacity_of(chunk) };
let payload_base = unsafe { C::data_ptr_of(chunk) }.as_ptr() as usize;
let bump_extent = cap.min(C::max_bump_extent());
let payload_end = payload_base + bump_extent;
let drop_back_addr = self.drop_back.get().as_ptr() as usize;
let bytes = payload_end - drop_back_addr;
#[expect(
clippy::cast_possible_truncation,
reason = "current-slot chunks are bounded by MAX_CHUNK_BYTES = 64 KiB, yielding count <= 8192"
)]
let count = (bytes / core::mem::size_of::<InnerDropEntry>()) as u16;
count
}
#[inline]
#[cfg_attr(test, mutants::skip)] pub(super) fn bump_smart_pointers_issued(&self) {
let n = self.smart_pointers_issued.get();
check_smart_pointers_issued_overflow(n);
unsafe { core::hint::assert_unchecked(n < LARGE - 1) };
self.smart_pointers_issued.set(n + 1);
}
}
pub(super) type CurrentLocalChunk<A> = CurrentChunk<LocalChunk<A>>;
pub(super) type CurrentSharedChunk<A> = CurrentChunk<SharedChunk<A>>;
pub(super) struct OversizedLocalGuard<A: Allocator + Clone> {
pub(super) chunk: NonNull<LocalChunk<A>>,
}
impl<A: Allocator + Clone> Drop for OversizedLocalGuard<A> {
fn drop(&mut self) {
unsafe { LocalChunk::reconcile_swap_out(self.chunk, 0, false) };
}
}
pub(super) struct OversizedSharedGuard<A: Allocator + Clone> {
pub(super) chunk: NonNull<SharedChunk<A>>,
}
impl<A: Allocator + Clone> Drop for OversizedSharedGuard<A> {
fn drop(&mut self) {
unsafe { SharedChunk::reconcile_swap_out(self.chunk, 0) };
}
}
#[inline(always)]
#[expect(
clippy::inline_always,
reason = "must inline at every alloc site to avoid a per-call function-call overhead; see PERF.md"
)]
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg_attr(test, mutants::skip)] pub(super) fn check_smart_pointers_issued_overflow(n: usize) {
if n >= LARGE - 1 {
crate::internal::constants::refcount_overflow_abort();
}
}