use std::{mem, num, ptr};
use storage::alloc;
use crate::buffer::{raw::Policy as _, resident, storage};
#[repr(transparent)]
#[derive(Debug)]
pub(in crate::buffer) struct Heap<P: crate::buffer::raw::Policy = ()> {
pointer: ptr::NonNull<crate::buffer::raw::Header<P>>,
}
impl<P: crate::buffer::raw::Policy> Heap<P> {
#[inline]
pub(in crate::buffer) fn try_new(
seed: P,
capacity: num::NonZero<u32>,
) -> Result<Self, alloc::Error<P::Failure>> {
let debit =
crate::buffer::raw::Debit::try_new(seed, capacity).map_err(alloc::Error::Budget)?;
Self::try_charged(debit).map_err(alloc::Error::Allocation)
}
#[inline]
pub(in crate::buffer) fn try_charged(
debit: crate::buffer::raw::Debit<P>,
) -> Result<Self, crate::alloc::Exhausted> {
let capacity = *debit.item();
let layout = crate::buffer::raw::Header::<P>::layout(capacity);
let pointer = unsafe { crate::alloc::raw::try_new(layout, false)? };
unsafe {
crate::buffer::raw::Header::write(pointer, capacity, debit);
}
Ok(Self { pointer })
}
pub(in crate::buffer) fn capacity(&self) -> num::NonZero<u32> {
unsafe { self.pointer.as_ref() }.capacity()
}
pub(in crate::buffer) fn try_reserve(
&mut self,
target: num::NonZero<u32>,
) -> Result<(), alloc::Error<P::Failure>> {
let header = unsafe { self.pointer.as_ref() };
let Some(amount) = num::NonZero::new(target.get().saturating_sub(header.capacity().get()))
else {
return Ok(());
};
let debit = crate::buffer::raw::Debit::try_new(header.policy, amount)
.map_err(alloc::Error::Budget)?;
self.pointer =
unsafe { crate::buffer::raw::Header::try_resize(self.pointer, target, debit) }
.map_err(alloc::Error::Allocation)?;
Ok(())
}
}
impl<P: crate::buffer::raw::Policy> Heap<P> {
pub(in crate::buffer) fn as_ptr(&self) -> *const mem::MaybeUninit<u8> {
crate::buffer::raw::Header::data(self.pointer)
.cast()
.as_ptr()
}
pub(in crate::buffer) fn as_mut_ptr(&mut self) -> *mut mem::MaybeUninit<u8> {
crate::buffer::raw::Header::data(self.pointer)
.cast()
.as_ptr()
}
pub(in crate::buffer) fn into_shared(self) -> crate::buffer::raw::Rc<P> {
let this = mem::ManuallyDrop::new(self);
unsafe { crate::buffer::raw::Rc::from_raw(this.pointer) }
}
}
impl<P: crate::buffer::raw::Policy> Drop for Heap<P> {
fn drop(&mut self) {
unsafe { crate::buffer::raw::Header::destroy(self.pointer) };
}
}
const _: () = assert!(size_of::<Heap<()>>() == size_of::<ptr::NonNull<()>>());
const _: () = assert!(size_of::<Option<Heap<()>>>() == size_of::<ptr::NonNull<()>>());
impl<'d> Heap<resident::Budget<'d>> {
pub(in crate::buffer) fn try_compact(
&mut self,
capacity: num::NonZero<u32>,
) -> Result<(), crate::alloc::Exhausted> {
let current = self.capacity();
if capacity == current {
return Ok(());
}
let old = crate::buffer::raw::Header::<resident::Budget<'d>>::layout(current);
let new = crate::buffer::raw::Header::<resident::Budget<'d>>::layout(capacity);
self.pointer = unsafe { crate::alloc::raw::resize(old, self.pointer.cast(), new)? }.cast();
let header = unsafe { self.pointer.as_mut() };
unsafe {
header.set_capacity(capacity);
header.policy.refund(crate::mem::raw::Counter::nonzero(
current.get() - capacity.get(),
));
}
Ok(())
}
}