use core::alloc::Layout;
#[cfg(not(feature = "allocator-api2"))]
use core::fmt;
use core::marker::PhantomData;
use core::ptr::{self, NonNull};
#[cfg(feature = "zeroize")]
use core::slice;
#[cfg(all(feature = "alloc", not(feature = "allocator-api2")))]
use core::mem::transmute;
#[cfg(all(feature = "alloc", not(feature = "allocator-api2")))]
use alloc_crate::alloc::{alloc as raw_alloc, dealloc as raw_dealloc};
#[cfg(all(feature = "alloc", feature = "allocator-api2"))]
pub use allocator_api2::alloc::Global;
#[cfg(feature = "allocator-api2")]
pub use allocator_api2::alloc::{AllocError, Allocator};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
#[cfg(all(not(test), feature = "alloc"))]
pub use alloc_crate::alloc::handle_alloc_error;
#[cfg(any(test, not(feature = "alloc")))]
pub fn handle_alloc_error(layout: Layout) -> ! {
panic!("memory allocation of {} bytes failed", layout.size());
}
#[cfg(not(feature = "allocator-api2"))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AllocError;
#[cfg(not(feature = "allocator-api2"))]
impl fmt::Display for AllocError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("memory allocation failed")
}
}
#[cfg(all(feature = "std", not(feature = "allocator-api2")))]
impl std::error::Error for AllocError {}
#[cfg(not(feature = "allocator-api2"))]
pub unsafe trait Allocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
#[inline]
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr = self.allocate(layout)?;
unsafe { ptr::write_bytes(ptr.cast::<u8>().as_ptr(), 0, ptr.len()) };
Ok(ptr)
}
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(
new_layout.size() >= old_layout.size(),
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
);
let new_ptr = self.allocate(new_layout)?;
let cp_len = old_layout.size().min(new_ptr.len());
if cp_len > 0 {
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), cp_len);
}
self.deallocate(ptr, old_layout);
Ok(new_ptr)
}
unsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(
new_layout.size() >= old_layout.size(),
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
);
let new_ptr = self.allocate_zeroed(new_layout)?;
let cp_len = old_layout.size().min(new_ptr.len());
if cp_len > 0 {
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), cp_len);
}
self.deallocate(ptr, old_layout);
Ok(new_ptr)
}
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(
new_layout.size() <= old_layout.size(),
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
);
let new_ptr = self.allocate(new_layout)?;
let cp_len = old_layout.size().min(new_ptr.len());
if cp_len > 0 {
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), cp_len);
}
self.deallocate(ptr, old_layout);
Ok(new_ptr)
}
#[inline(always)]
fn by_ref(&self) -> &Self
where
Self: Sized,
{
self
}
}
pub trait AllocateIn: Sized {
type Alloc: Allocator;
fn allocate_in(self, layout: Layout) -> Result<(NonNull<[u8]>, Self::Alloc), AllocError>;
#[inline]
fn allocate_zeroed_in(
self,
layout: Layout,
) -> Result<(NonNull<[u8]>, Self::Alloc), AllocError> {
let (ptr, alloc) = self.allocate_in(layout)?;
unsafe { ptr::write_bytes(ptr.cast::<u8>().as_ptr(), 0, ptr.len()) };
Ok((ptr, alloc))
}
}
impl<A: Allocator> AllocateIn for A {
type Alloc = A;
#[inline]
fn allocate_in(self, layout: Layout) -> Result<(NonNull<[u8]>, Self::Alloc), AllocError> {
let data = self.allocate(layout)?;
Ok((data, self))
}
#[inline]
fn allocate_zeroed_in(
self,
layout: Layout,
) -> Result<(NonNull<[u8]>, Self::Alloc), AllocError> {
let data = self.allocate_zeroed(layout)?;
Ok((data, self))
}
}
pub trait AllocatorDefault: Allocator + Clone + Default {
const DEFAULT: Self;
}
pub trait AllocatorZeroizes: Allocator {}
pub trait SpillAlloc<'a>: Sized {
type NewIn<A: 'a>;
#[inline]
fn spill_alloc(self) -> Self::NewIn<Global> {
Self::spill_alloc_in(self, Global)
}
fn spill_alloc_in<A: Allocator + 'a>(self, alloc: A) -> Self::NewIn<A>;
}
#[cfg(any(not(feature = "alloc"), not(feature = "allocator-api2")))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "alloc", derive(Default, Copy))]
pub struct Global;
#[cfg(all(feature = "alloc", not(feature = "allocator-api2")))]
unsafe impl Allocator for Global {
#[inline]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr = if layout.size() == 0 {
#[allow(clippy::useless_transmute)]
unsafe {
NonNull::new_unchecked(transmute(layout.align()))
}
} else {
let Some(ptr) = NonNull::new(unsafe { raw_alloc(layout) }) else {
return Err(AllocError);
};
ptr
};
Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
}
#[inline]
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() > 0 {
raw_dealloc(ptr.as_ptr(), layout);
}
}
}
#[cfg(not(feature = "alloc"))]
unsafe impl Allocator for Global {
fn allocate(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
unimplemented!();
}
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
unimplemented!();
}
}
#[cfg(feature = "alloc")]
impl AllocatorDefault for Global {
const DEFAULT: Self = Global;
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Fixed<'a>(PhantomData<&'a mut ()>);
unsafe impl Allocator for Fixed<'_> {
#[inline(always)]
fn allocate(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
Err(AllocError)
}
#[inline(always)]
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
if old_layout.align() != new_layout.align() || new_layout.size() > old_layout.size() {
Err(AllocError)
} else {
Ok(NonNull::slice_from_raw_parts(ptr, old_layout.size()))
}
}
#[inline(always)]
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
if old_layout.align() != new_layout.align() || new_layout.size() > old_layout.size() {
Err(AllocError)
} else {
Ok(NonNull::slice_from_raw_parts(ptr, old_layout.size()))
}
}
#[inline]
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {}
}
impl<'a> AllocatorDefault for Fixed<'a> {
const DEFAULT: Self = Self(PhantomData);
}
impl Clone for Fixed<'_> {
fn clone(&self) -> Self {
Fixed::DEFAULT
}
}
#[derive(Debug)]
pub struct Spill<'a, A> {
alloc: A,
initial: *const u8,
_fixed: Fixed<'a>,
}
impl<'a, A> Spill<'a, A> {
pub(crate) const fn new(alloc: A, initial: *const u8, fixed: Fixed<'a>) -> Self {
Self {
alloc,
initial,
_fixed: fixed,
}
}
}
impl<A: Default + Allocator> Default for Spill<'_, A> {
#[inline]
fn default() -> Self {
Self::new(A::default(), ptr::null(), Fixed::DEFAULT)
}
}
unsafe impl<A: Allocator> Allocator for Spill<'_, A> {
#[inline]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.alloc.allocate(layout)
}
#[inline]
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
if !ptr::eq(self.initial, ptr.as_ptr()) {
self.alloc.deallocate(ptr, layout)
}
}
}
impl<'a, A: Default + Allocator> Clone for Spill<'a, A> {
fn clone(&self) -> Self {
Self::default()
}
}
impl<'a, A: AllocatorDefault> AllocatorDefault for Spill<'a, A> {
const DEFAULT: Self = Self::new(A::DEFAULT, ptr::null(), Fixed::DEFAULT);
}
#[cfg(feature = "zeroize")]
#[derive(Debug, Default, Clone, Copy)]
pub struct ZeroizingAlloc<A>(pub A);
#[cfg(feature = "zeroize")]
unsafe impl<A: Allocator> Allocator for ZeroizingAlloc<A> {
#[inline]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.0.allocate(layout)
}
#[inline]
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() > 0 {
let mem = slice::from_raw_parts_mut(ptr.as_ptr(), layout.size());
mem.zeroize();
}
self.0.deallocate(ptr, layout)
}
}
#[cfg(feature = "zeroize")]
impl<A: AllocatorDefault> AllocatorDefault for ZeroizingAlloc<A> {
const DEFAULT: Self = ZeroizingAlloc(A::DEFAULT);
}
#[cfg(feature = "zeroize")]
impl<'a, Z> SpillAlloc<'a> for &'a mut zeroize::Zeroizing<Z>
where
Z: Zeroize + 'a,
&'a mut Z: SpillAlloc<'a>,
{
type NewIn<A: 'a> = <&'a mut Z as SpillAlloc<'a>>::NewIn<ZeroizingAlloc<A>>;
#[inline]
fn spill_alloc_in<A: Allocator + 'a>(self, alloc: A) -> Self::NewIn<A> {
(&mut **self).spill_alloc_in(ZeroizingAlloc(alloc))
}
}
#[cfg(feature = "zeroize")]
impl<A: Allocator> AllocatorZeroizes for ZeroizingAlloc<A> {}
pub trait ConvertAlloc<Target> {
fn convert(self) -> Target;
}