use core::{alloc::Layout, marker::PhantomData, num::NonZeroUsize};
use crate::{
chunk::{ChunkHeader, ChunkSizeConfig, MIN_CHUNK_ALIGN},
settings::BumpAllocatorSettings,
};
const _: () = assert!(MIN_CHUNK_ALIGN == crate::bumping::MIN_CHUNK_ALIGN);
pub(crate) type AssumedMallocOverhead = [usize; 2];
pub const fn config<A, S>() -> ChunkSizeConfig
where
S: BumpAllocatorSettings,
{
ChunkSizeConfig {
up: S::UP,
assumed_malloc_overhead_layout: Layout::new::<AssumedMallocOverhead>(),
chunk_header_layout: Layout::new::<ChunkHeader<A>>(),
}
}
macro_rules! attempt {
($expr:expr) => {
match $expr {
Some(some) => some,
None => return None,
}
};
}
pub struct ChunkSize<A, S> {
size: NonZeroUsize,
marker: PhantomData<fn() -> (A, S)>,
}
impl<A, S> Clone for ChunkSize<A, S> {
fn clone(&self) -> Self {
*self
}
}
impl<A, S> Copy for ChunkSize<A, S> {}
impl<A, S> ChunkSize<A, S>
where
S: BumpAllocatorSettings,
{
pub const MINIMUM: Self = match Self::from_hint(S::MINIMUM_CHUNK_SIZE) {
Some(some) => some,
None => panic!("failed to calculate minimum chunk size"),
};
pub const fn from_hint(size_hint: usize) -> Option<Self> {
ChunkSizeHint::new(size_hint).calc_size()
}
pub const fn from_capacity(layout: Layout) -> Option<Self> {
attempt!(ChunkSizeHint::for_capacity(layout)).calc_size()
}
pub const fn align_allocation_size(size: usize) -> usize {
config::<A, S>().align_size(size)
}
pub const fn layout(self) -> Option<Layout> {
let size = self.size.get();
let align = core::mem::align_of::<ChunkHeader<A>>();
match Layout::from_size_align(size, align) {
Ok(ok) => Some(ok),
Err(_) => None,
}
}
}
pub struct ChunkSizeHint<A, S>(usize, PhantomData<fn() -> (A, S)>);
impl<A, S> Clone for ChunkSizeHint<A, S> {
fn clone(&self) -> Self {
*self
}
}
impl<A, S> Copy for ChunkSizeHint<A, S> {}
impl<A, S> ChunkSizeHint<A, S>
where
S: BumpAllocatorSettings,
{
pub const fn new(size_hint: usize) -> Self {
Self(size_hint, PhantomData)
}
pub const fn for_capacity(layout: Layout) -> Option<Self> {
Some(Self(attempt!(config::<A, S>().calc_hint_from_capacity(layout)), PhantomData))
}
pub const fn calc_size(self) -> Option<ChunkSize<A, S>> {
let size_hint = max(self.0, S::MINIMUM_CHUNK_SIZE);
Some(ChunkSize {
size: attempt!(config::<A, S>().calc_size_from_hint(size_hint)),
marker: PhantomData,
})
}
pub const fn max(self, other: Self) -> Self {
if self.0 > other.0 { self } else { other }
}
}
const fn max(a: usize, b: usize) -> usize {
if a > b { a } else { b }
}