use core::{
alloc::{Layout, LayoutErr},
cell::Cell,
mem::MaybeUninit,
ptr::{self, NonNull},
};
use alloc::{
alloc::alloc_zeroed,
boxed::Box,
};
use crate::bump::Failure;
use crate::unsync::bump::MemBump;
use crate::leaked::LeakBox;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct TryNewError {
inner: RawAllocError,
}
impl TryNewError {
pub const fn allocation_size(&self) -> usize {
self.inner.allocation_size()
}
}
type LinkPtr = Option<NonNull<Link>>;
struct Link {
next: Cell<LinkPtr>,
bump: MemBump,
}
pub struct Chain {
root: Cell<LinkPtr>,
}
impl Chain {
pub fn new(size: usize) -> Result<Self, TryNewError> {
let link = Link::alloc(size).map_err(|e| TryNewError { inner: e })?;
Ok(Chain {
root: Cell::new(Some(link)),
})
}
pub fn bump_box<'bump, T: 'bump>(&'bump self)
-> Result<LeakBox<'bump, MaybeUninit<T>>, Failure>
{
let root = self.root().ok_or(Failure::Exhausted)?;
root.as_bump().bump_box()
}
pub fn chain(&self, new: Chain) {
let self_bump = self.root.take();
match new.root() {
None => {
self.root.set(self_bump)
}
Some(root) => {
unsafe { root.set_next(self_bump) };
self.root.set(new.root.take())
}
}
}
pub fn capacity(&self) -> usize {
match self.root() {
None => 0,
Some(root) => root.as_bump().capacity(),
}
}
pub fn remaining_capacity(&self) -> usize {
match self.root() {
None => 0,
Some(root) => self.capacity() - root.as_bump().level().0,
}
}
fn root(&self) -> Option<&Link> {
unsafe {
let bump_ptr = self.root.get()?.as_ptr();
Some(&*bump_ptr)
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub(crate) struct RawAllocError {
allocation_size: usize,
kind: RawAllocFailure,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
enum RawAllocFailure {
Exhausted,
Layout,
}
impl Link {
pub(crate) unsafe fn set_next(&self, next: LinkPtr) {
if let Some(next) = self.next.replace(next) {
let _ = Box::from_raw(next.as_ptr());
}
}
pub(crate) fn take_next(&self) -> Option<Box<Link>> {
let ptr = self.next.take()?;
Some(unsafe {
Box::from_raw(ptr.as_ptr())
})
}
pub(crate) fn as_bump(&self) -> &MemBump {
&self.bump
}
pub(crate) fn layout_from_size(size: usize) -> Result<Layout, LayoutErr> {
Layout::new::<Cell<LinkPtr>>()
.extend(MemBump::layout_from_size(size)?)
.map(|layout| layout.0)
}
unsafe fn alloc_raw(layout: Layout) -> Result<NonNull<u8>, RawAllocError> {
let ptr = alloc_zeroed(layout);
NonNull::new(ptr).ok_or_else(|| {
RawAllocError::new(layout.size(), RawAllocFailure::Exhausted)
})
}
pub(crate) fn alloc(capacity: usize) -> Result<NonNull<Self>, RawAllocError> {
let layout = Self::layout_from_size(capacity)
.map_err(|_| {
RawAllocError::new(capacity, RawAllocFailure::Layout)
})?;
unsafe {
let raw = Link::alloc_raw(layout)?;
let raw_mut: *mut [Cell<MaybeUninit<u8>>] =
ptr::slice_from_raw_parts_mut(raw.cast().as_ptr(), capacity);
Ok(NonNull::new_unchecked(raw_mut as *mut Link))
}
}
}
impl RawAllocError {
const fn new(allocation_size: usize, kind: RawAllocFailure) -> Self {
Self {
allocation_size,
kind,
}
}
pub(crate) const fn allocation_size(&self) -> usize {
self.allocation_size
}
}
impl Drop for Chain {
fn drop(&mut self) {
let mut current = self.root.take();
while let Some(non_null) = current {
let link = unsafe { Box::from_raw(non_null.as_ptr()) };
current = link.next.take();
}
}
}
impl Drop for Link {
fn drop(&mut self) {
let mut current = self.take_next();
while let Some(link) = current {
current = link.take_next();
}
}
}