use core::fmt;
use crate::{
core::{
alloc::{AllocError, Layout},
num::NonZeroUsize,
ptr::NonNull,
},
AllocInitError, BackingAllocator, BasePtr, Raw,
};
#[cfg(feature = "unstable")]
use crate::core::alloc::Allocator;
#[cfg(not(feature = "unstable"))]
use crate::core::ptr::NonNullStrict;
#[cfg(any(feature = "alloc", test))]
use crate::Global;
pub struct Bump<A: BackingAllocator> {
base: BasePtr,
low_mark: NonZeroUsize,
outstanding: usize,
layout: Layout,
backing_allocator: A,
}
impl Bump<Raw> {
pub unsafe fn new_raw(
region: NonNull<u8>,
layout: Layout,
) -> Result<Bump<Raw>, AllocInitError> {
unsafe { RawBump::try_new(region, layout).map(|b| b.with_backing_allocator(Raw)) }
}
#[doc(hidden)]
pub fn split(self) -> (Bump<Raw>, Bump<Raw>) {
let base_addr = self.base.addr();
let lower_size = self.low_mark.get().checked_sub(base_addr.get()).unwrap();
let lower_limit = self.low_mark;
let lower = Bump {
base: BasePtr::new(self.base.ptr(), lower_size),
outstanding: 0,
low_mark: lower_limit,
layout: Layout::from_size_align(lower_size, 1).unwrap(),
backing_allocator: Raw,
};
let upper_size = self.layout.size().checked_sub(lower_size).unwrap();
let new_base = BasePtr::new(self.base.with_addr(self.low_mark), upper_size);
let upper = Bump {
base: new_base,
low_mark: self.low_mark,
outstanding: self.outstanding,
layout: Layout::from_size_align(upper_size, 1).unwrap(),
backing_allocator: Raw,
};
(lower, upper)
}
}
#[cfg(all(any(feature = "alloc", test), not(feature = "unstable")))]
#[cfg_attr(docs_rs, doc(cfg(all(feature = "alloc"))))]
impl Bump<Global> {
pub fn try_new(layout: Layout) -> Result<Bump<Global>, AllocInitError> {
if layout.size() == 0 {
return Err(AllocInitError::InvalidConfig);
}
unsafe {
let region_raw = alloc::alloc::alloc(layout);
let region_ptr = NonNull::new(region_raw).ok_or(AllocInitError::AllocFailed(layout))?;
match RawBump::try_new(region_ptr, layout) {
Ok(b) => Ok(b.with_backing_allocator(Global)),
Err(e) => {
alloc::alloc::dealloc(region_ptr.as_ptr(), layout);
Err(e)
}
}
}
}
}
#[cfg(all(any(feature = "alloc", test), feature = "unstable"))]
#[cfg_attr(docs_rs, doc(cfg(all(feature = "alloc"))))]
impl Bump<Global> {
pub fn try_new(layout: Layout) -> Result<Bump<Global>, AllocInitError> {
Self::try_new_in(layout, Global)
}
}
#[cfg(feature = "unstable")]
#[cfg_attr(docs_rs, doc(cfg(feature = "unstable")))]
impl<A> Bump<A>
where
A: Allocator,
{
pub fn try_new_in(layout: Layout, backing_allocator: A) -> Result<Bump<A>, AllocInitError> {
unsafe {
let region_ptr = backing_allocator
.allocate(layout)
.map_err(|_| AllocInitError::AllocFailed(layout))?;
match RawBump::try_new(region_ptr.cast(), layout) {
Ok(b) => Ok(b.with_backing_allocator(backing_allocator)),
Err(e) => {
backing_allocator.deallocate(region_ptr.cast(), layout);
Err(e)
}
}
}
}
}
impl<A> Bump<A>
where
A: BackingAllocator,
{
pub fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
if layout.size() == 0 {
return Err(AllocError);
}
let new_low_unaligned = self
.low_mark
.get()
.checked_sub(layout.size())
.ok_or(AllocError)?;
let new_low_mark = new_low_unaligned & !(layout.align() - 1);
if new_low_mark < self.base.addr().get() {
return Err(AllocError);
}
self.outstanding += 1;
self.low_mark = unsafe { NonZeroUsize::new_unchecked(new_low_mark) };
Ok(self.base.with_addr_and_size(self.low_mark, layout.size()))
}
pub unsafe fn deallocate(&mut self, ptr: NonNull<u8>) {
let _ = ptr;
self.outstanding = self.outstanding.checked_sub(1).unwrap();
if self.outstanding == 0 {
self.low_mark = self.base.limit();
}
}
pub unsafe fn reset(&mut self) {
self.low_mark = self.base.limit();
}
}
impl<A> Drop for Bump<A>
where
A: BackingAllocator,
{
fn drop(&mut self) {
unsafe {
self.backing_allocator
.deallocate(self.base.ptr(), self.layout)
};
}
}
impl<A> fmt::Debug for Bump<A>
where
A: BackingAllocator,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Bump")
.field("base", &self.base)
.field("low_mark", &self.low_mark)
.finish()
}
}
struct RawBump {
base: BasePtr,
limit: NonZeroUsize,
layout: Layout,
}
impl RawBump {
fn with_backing_allocator<A: BackingAllocator>(self, backing_allocator: A) -> Bump<A> {
Bump {
base: self.base,
low_mark: self.limit,
outstanding: 0,
layout: self.layout,
backing_allocator,
}
}
unsafe fn try_new(region: NonNull<u8>, layout: Layout) -> Result<RawBump, AllocInitError> {
let addr = region.addr().get();
if addr & !(layout.align() - 1) != addr {
return Err(AllocInitError::InvalidConfig);
}
let base = BasePtr::new(region, layout.size());
let limit = NonZeroUsize::new(
region
.addr()
.get()
.checked_add(layout.size())
.ok_or(AllocInitError::InvalidLocation)?,
)
.unwrap();
Ok(RawBump {
base,
limit,
layout,
})
}
}