mod abort;
mod layout;
pub use self::{
abort::AbortAlloc,
layout::{LayoutErr, NonZeroLayout},
};
pub use core::alloc::GlobalAlloc;
use core::{
cmp,
fmt,
num::NonZeroUsize,
ptr::{self, NonNull},
};
pub use liballoc::alloc::{alloc, alloc_zeroed, dealloc, realloc};
#[cfg(feature = "std")]
use std::alloc::System;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct CapacityOverflow;
impl From<core::alloc::LayoutErr> for CapacityOverflow {
#[inline]
#[must_use]
fn from(_: core::alloc::LayoutErr) -> Self {
Self
}
}
impl From<LayoutErr> for CapacityOverflow {
#[inline]
#[must_use]
fn from(_: LayoutErr) -> Self {
Self
}
}
pub trait BuildAllocRef: Sized {
type Ref: DeallocRef<BuildAlloc = Self>;
#[must_use]
unsafe fn build_alloc_ref(
&mut self,
ptr: NonNull<u8>,
layout: Option<NonZeroLayout>,
) -> Self::Ref;
}
pub trait DeallocRef: Sized {
type BuildAlloc: BuildAllocRef<Ref = Self>;
fn get_build_alloc(&mut self) -> Self::BuildAlloc;
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: NonZeroLayout);
}
pub trait AllocRef: DeallocRef {
type Error;
fn alloc(&mut self, layout: NonZeroLayout) -> Result<NonNull<u8>, Self::Error>;
fn alloc_zeroed(&mut self, layout: NonZeroLayout) -> Result<NonNull<u8>, Self::Error> {
let size = layout.size();
let p = self.alloc(layout)?;
unsafe {
ptr::write_bytes(p.as_ptr(), 0, size.get());
}
Ok(p)
}
fn usable_size(&self, layout: NonZeroLayout) -> (usize, usize) {
(layout.size().get(), layout.size().get())
}
unsafe fn grow_in_place(
&mut self,
ptr: NonNull<u8>,
layout: NonZeroLayout,
new_size: NonZeroUsize,
) -> bool {
let _ = ptr;
debug_assert!(new_size.get() >= layout.size().get());
let (_l, u) = self.usable_size(layout);
new_size.get() <= u
}
unsafe fn shrink_in_place(
&mut self,
ptr: NonNull<u8>,
layout: NonZeroLayout,
new_size: NonZeroUsize,
) -> bool {
let _ = ptr;
debug_assert!(new_size.get() <= layout.size().get());
let (l, _u) = self.usable_size(layout);
l <= new_size.get()
}
}
pub trait ReallocRef: AllocRef {
unsafe fn realloc(
&mut self,
ptr: NonNull<u8>,
old_layout: NonZeroLayout,
new_layout: NonZeroLayout,
) -> Result<NonNull<u8>, Self::Error> {
let old_size = old_layout.size();
let new_size = new_layout.size();
if old_layout.align() == new_layout.align()
&& ((new_size > old_size && self.grow_in_place(ptr, old_layout, new_size))
|| (new_size < old_size && self.shrink_in_place(ptr, old_layout, new_size)))
{
return Ok(ptr);
}
alloc_copy_dealloc(self, ptr, old_layout, new_layout)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AllocErr;
impl fmt::Display for AllocErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("memory allocation failed")
}
}
#[derive(Copy, Clone, Default, Debug)]
pub struct Global;
macro_rules! impl_buildalloc_alloc_zst {
($ty:tt) => {
impl BuildAllocRef for $ty {
type Ref = Self;
unsafe fn build_alloc_ref(
&mut self,
_ptr: NonNull<u8>,
_layout: Option<NonZeroLayout>,
) -> Self::Ref {
Self
}
}
};
}
impl_buildalloc_alloc_zst!(Global);
#[cfg(feature = "std")]
impl_buildalloc_alloc_zst!(System);
impl DeallocRef for Global {
type BuildAlloc = Self;
fn get_build_alloc(&mut self) -> Self::BuildAlloc {
Self
}
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: NonZeroLayout) {
dealloc(ptr.as_ptr(), layout.into())
}
}
impl AllocRef for Global {
type Error = AllocErr;
fn alloc(&mut self, layout: NonZeroLayout) -> Result<NonNull<u8>, Self::Error> {
unsafe { NonNull::new(alloc(layout.into())).ok_or(AllocErr) }
}
fn alloc_zeroed(&mut self, layout: NonZeroLayout) -> Result<NonNull<u8>, Self::Error> {
unsafe { NonNull::new(alloc_zeroed(layout.into())).ok_or(AllocErr) }
}
}
impl ReallocRef for Global {
unsafe fn realloc(
&mut self,
ptr: NonNull<u8>,
old_layout: NonZeroLayout,
new_layout: NonZeroLayout,
) -> Result<NonNull<u8>, Self::Error> {
if old_layout.align() == new_layout.align() {
NonNull::new(realloc(
ptr.as_ptr(),
old_layout.into(),
new_layout.size().get(),
))
.ok_or(AllocErr)
} else {
alloc_copy_dealloc(self, ptr, old_layout, new_layout)
}
}
}
#[cfg(feature = "std")]
impl DeallocRef for System {
type BuildAlloc = Self;
fn get_build_alloc(&mut self) -> Self::BuildAlloc {
Self
}
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: NonZeroLayout) {
GlobalAlloc::dealloc(self, ptr.as_ptr(), layout.into())
}
}
#[cfg(feature = "std")]
impl AllocRef for System {
type Error = AllocErr;
fn alloc(&mut self, layout: NonZeroLayout) -> Result<NonNull<u8>, Self::Error> {
unsafe { NonNull::new(GlobalAlloc::alloc(self, layout.into())).ok_or(AllocErr) }
}
fn alloc_zeroed(&mut self, layout: NonZeroLayout) -> Result<NonNull<u8>, Self::Error> {
unsafe { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout.into())).ok_or(AllocErr) }
}
}
#[cfg(feature = "std")]
impl ReallocRef for System {
unsafe fn realloc(
&mut self,
ptr: NonNull<u8>,
old_layout: NonZeroLayout,
new_layout: NonZeroLayout,
) -> Result<NonNull<u8>, Self::Error> {
if old_layout.align() == new_layout.align() {
NonNull::new(GlobalAlloc::realloc(
self,
ptr.as_ptr(),
old_layout.into(),
new_layout.size().get(),
))
.ok_or(AllocErr)
} else {
alloc_copy_dealloc(self, ptr, old_layout, new_layout)
}
}
}
#[inline]
unsafe fn alloc_copy_dealloc<A: ReallocRef>(
alloc: &mut A,
ptr: NonNull<u8>,
old_layout: NonZeroLayout,
new_layout: NonZeroLayout,
) -> Result<NonNull<u8>, A::Error> {
let result = alloc.alloc(new_layout);
if let Ok(new_ptr) = result {
ptr::copy_nonoverlapping(
ptr.as_ptr(),
new_ptr.as_ptr(),
cmp::min(old_layout.size().get(), new_layout.size().get()),
);
alloc.dealloc(ptr, old_layout);
}
result
}