#![allow(unknown_lints)]
#![warn(clippy::all, clippy::pedantic, clippy::undocumented_unsafe_blocks)]
#![warn(unknown_lints)]
#![allow(
unsafe_op_in_unsafe_fn,
rustdoc::broken_intra_doc_links,
// :) because patch doesn't work for overriding crates.io packages and i have to include my
// dependencies' dependencies versions to make this compile on msrv
unused_crate_dependencies
)]
#![deny(missing_docs, unused_unsafe)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(allocator_api))]
#![cfg_attr(feature = "metadata", feature(ptr_metadata))]
#![cfg_attr(feature = "clone_to_uninit", feature(clone_to_uninit))]
#![cfg_attr(feature = "specialization", feature(min_specialization))]
#![cfg_attr(feature = "sized_hierarchy", feature(sized_hierarchy))]
macro_rules! const_if {
(
$feature:literal,
$docs:literal,
$(#[$attr:meta])*
// this is also pretty poorly done, but it makes type param and optional req work
pub const fn $name:ident $(<$generic_ty:ident $(: $req:ident)?>)? ( $($args:tt)* )
$(-> $ret:ty)?
$(where $where_ty:ident : $where_req:ident)?
$body:block
) => {
#[cfg(feature = $feature)]
#[doc = $docs]
#[allow(clippy::incompatible_msrv)]
$(#[$attr])*
pub const fn $name $(<$generic_ty $(: $req)?>)? ($($args)*)
$(-> $ret)? $(where $where_ty: $where_req)? $body
#[cfg(not(feature = $feature))]
#[doc = $docs]
$(#[$attr])*
pub fn $name $(<$generic_ty $(: $req)?>)? ($($args)*)
$(-> $ret)? $(where $where_ty: $where_req)? $body
};
(
$feature:literal,
$docs:literal,
$(#[$attr:meta])*
pub const fn $name:ident $(<$lt:lifetime>)? ( $($args:tt)* )
$(-> $ret:ty)?
$body:block
) => {
#[cfg(feature = $feature)]
#[doc = $docs]
#[allow(clippy::incompatible_msrv)]
$(#[$attr])*
pub const fn $name $(<$lt>)? ($($args)*)
$(-> $ret)? $body
#[cfg(not(feature = $feature))]
#[doc = $docs]
$(#[$attr])*
pub fn $name $(<$lt>)? ($($args)*) $(-> $ret)? $body
};
(
$feature:literal,
$docs:literal,
$(#[$attr:meta])*
pub const unsafe fn $name:ident $(<$generic_ty:ident $(: $req:ident)?>)? ( $($args:tt)* )
$(-> $ret:ty)?
$body:block
) => {
#[cfg(feature = $feature)]
#[doc = $docs]
#[allow(clippy::incompatible_msrv)]
$(#[$attr])*
pub const unsafe fn $name$(<$generic_ty $(: $req)?>)?($($args)*) $(-> $ret)? $body
#[cfg(not(feature = $feature))]
#[doc = $docs]
$(#[$attr])*
pub unsafe fn $name$(<$generic_ty $(: $req)?>)?($($args)*) $(-> $ret)? $body
};
}
macro_rules! tri {
($($fallible:expr)+) => {
match $($fallible)+ {
Ok(s) => s,
Err(e) => return Err(e),
}
};
(il, $($fallible:expr)+) => {
match $($fallible)+ {
Ok(s) => s,
Err(e) => return Err(AllocError::InvalidLayout(e)),
}
};
}
extern crate alloc;
extern crate core;
#[cfg(feature = "extern_alloc")]
pub(crate) mod external_alloc;
#[cfg(any(
feature = "fallible_dealloc",
feature = "alloc_ext",
feature = "alloc_slice",
feature = "resize_in_place",
feature = "stats"
))]
mod features;
#[cfg(feature = "alloc_ext")]
pub use features::alloc_ext::*;
#[cfg(feature = "alloc_slice")]
pub use features::alloc_slice::*;
#[cfg(feature = "resize_in_place")]
pub use features::resize_in_place::*;
#[cfg(any(feature = "stats", feature = "fallible_dealloc"))]
pub use features::*;
#[cfg(feature = "extern_alloc")]
pub use external_alloc::*;
pub mod marker;
pub mod type_props;
pub mod unstable_util;
pub mod error;
use alloc::alloc::{
alloc as raw_all, alloc_zeroed as raw_allz, dealloc as de, realloc as re, GlobalAlloc, Layout,
};
use core::{
cmp::Ordering,
ptr::{self, NonNull},
};
use {error::AllocError, helpers::alloc_then};
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DefaultAlloc;
macro_rules! default_alloc_impl {
($ty:ty) => {
impl Alloc for $ty {
#[cfg_attr(miri, track_caller)]
#[inline]
fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
$crate::helpers::null_q_zsl_check(
layout,
|layout| unsafe { raw_all(layout) },
$crate::helpers::null_q_dyn,
)
}
#[cfg_attr(miri, track_caller)]
#[inline]
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
$crate::helpers::null_q_zsl_check(
layout,
|layout| unsafe { raw_allz(layout) },
$crate::helpers::null_q_dyn,
)
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() != 0 {
de(ptr.as_ptr(), layout);
}
}
}
};
}
unsafe impl GlobalAlloc for DefaultAlloc {
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
raw_all(layout)
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
de(ptr, layout);
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
raw_allz(layout)
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
re(ptr, layout, new_size)
}
}
default_alloc_impl!(DefaultAlloc);
pub trait Alloc {
fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError>;
#[cfg_attr(miri, track_caller)]
#[inline]
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
alloc_then(self, layout, (), |p, ()| unsafe {
ptr::write_bytes(p.as_ptr(), 0, layout.size());
p
})
}
unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout);
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<u8>, AllocError> {
grow(
self,
ptr,
old_layout,
new_layout,
AllocPattern::<fn(usize) -> u8>::None,
)
}
#[cfg_attr(miri, track_caller)]
unsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<u8>, AllocError> {
grow(
self,
ptr,
old_layout,
new_layout,
AllocPattern::<fn(usize) -> u8>::Zero,
)
}
#[cfg_attr(miri, track_caller)]
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<u8>, AllocError> {
shrink(self, ptr, old_layout, new_layout)
}
#[cfg_attr(miri, track_caller)]
unsafe fn realloc(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<u8>, AllocError> {
ralloc(
self,
ptr,
old_layout,
new_layout,
AllocPattern::<fn(usize) -> u8>::None,
)
}
#[cfg_attr(miri, track_caller)]
unsafe fn realloc_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<u8>, AllocError> {
ralloc(
self,
ptr,
old_layout,
new_layout,
AllocPattern::<fn(usize) -> u8>::Zero,
)
}
}
#[cfg(feature = "nightly")]
pub(crate) mod nightly {
use crate::{error::AllocError, Alloc, DefaultAlloc};
use alloc::alloc::{
alloc as raw_all, alloc_zeroed as raw_allz, dealloc as de, Allocator, Global, Layout,
};
use core::{alloc::AllocError as AllocatorError, ptr::NonNull};
unsafe impl Allocator for DefaultAlloc {
#[cfg_attr(miri, track_caller)]
#[inline]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocatorError> {
Allocator::allocate(&Global, layout)
}
#[cfg_attr(miri, track_caller)]
#[inline]
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocatorError> {
Allocator::allocate_zeroed(&Global, layout)
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
Allocator::deallocate(&Global, ptr.cast(), layout);
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocatorError> {
Allocator::grow(&Global, ptr.cast(), old_layout, new_layout)
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocatorError> {
Allocator::grow_zeroed(&Global, ptr.cast(), old_layout, new_layout)
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocatorError> {
Allocator::shrink(&Global, ptr.cast(), old_layout, new_layout)
}
}
default_alloc_impl!(Global);
}
#[allow(clippy::inline_always)]
impl<A: Alloc + ?Sized> Alloc for &A {
#[cfg_attr(miri, track_caller)]
#[inline(always)]
fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
(**self).alloc(layout)
}
#[cfg_attr(miri, track_caller)]
#[inline(always)]
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
(**self).alloc_zeroed(layout)
}
#[cfg_attr(miri, track_caller)]
#[inline(always)]
unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
(**self).dealloc(ptr, layout);
}
}
#[cfg(feature = "std")]
#[cfg_attr(miri, track_caller)]
fn zsl_check_alloc<A: GlobalAlloc>(
a: &A,
layout: Layout,
alloc: unsafe fn(&A, Layout) -> *mut u8,
) -> Result<NonNull<u8>, AllocError> {
helpers::zsl_check(layout, |layout: Layout| {
helpers::null_q(unsafe { alloc(a, layout) }, layout)
})
}
#[cfg(feature = "std")]
impl Alloc for std::alloc::System {
#[cfg_attr(miri, track_caller)]
#[inline]
fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
zsl_check_alloc(self, layout, GlobalAlloc::alloc)
}
#[cfg_attr(miri, track_caller)]
#[inline]
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
zsl_check_alloc(self, layout, GlobalAlloc::alloc_zeroed)
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
GlobalAlloc::dealloc(self, ptr.as_ptr(), layout);
}
}
#[cfg_attr(miri, track_caller)]
pub(crate) unsafe fn grow<A: Alloc + ?Sized, F: Fn(usize) -> u8 + Clone>(
a: &A,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
pattern: AllocPattern<F>,
) -> Result<NonNull<u8>, AllocError> {
match old_layout.size().cmp(&new_layout.size()) {
Ordering::Less => grow_unchecked(a, ptr, old_layout, new_layout, pattern),
Ordering::Equal => {
if new_layout.align() == old_layout.align() {
Ok(ptr)
} else {
grow_unchecked(&a, ptr, old_layout, new_layout, pattern)
}
}
Ordering::Greater => Err(AllocError::GrowSmallerNewLayout(
old_layout.size(),
new_layout.size(),
)),
}
}
#[cfg_attr(miri, track_caller)]
pub(crate) unsafe fn shrink<A: Alloc + ?Sized>(
a: &A,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<u8>, AllocError> {
match old_layout.size().cmp(&new_layout.size()) {
Ordering::Less => Err(AllocError::ShrinkBiggerNewLayout(
old_layout.size(),
new_layout.size(),
)),
Ordering::Equal => {
if new_layout.align() == old_layout.align() {
Ok(ptr)
} else {
shrink_unchecked(&a, ptr, old_layout, new_layout)
}
}
Ordering::Greater => shrink_unchecked(a, ptr, old_layout, new_layout),
}
}
#[allow(clippy::needless_pass_by_value)]
#[cfg_attr(miri, track_caller)]
unsafe fn grow_unchecked<A: Alloc + ?Sized, F: Fn(usize) -> u8 + Clone>(
a: &A,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
pattern: AllocPattern<F>,
) -> Result<NonNull<u8>, AllocError> {
let new_ptr = match pattern {
AllocPattern::None => tri!(a.alloc(new_layout)).cast::<u8>(),
#[cfg(feature = "alloc_ext")]
AllocPattern::Fn(f) => tri!(a.alloc_patterned(new_layout, f)),
AllocPattern::Zero => tri!(a.alloc_zeroed(new_layout)).cast::<u8>(),
#[cfg(feature = "alloc_ext")]
AllocPattern::All(n) => tri!(a.alloc_filled(new_layout, n)).cast::<u8>(),
#[cfg(not(feature = "alloc_ext"))]
AllocPattern::PhantomFn(_) => core::hint::unreachable_unchecked(),
};
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), old_layout.size());
a.dealloc(ptr, old_layout);
Ok(new_ptr)
}
#[cfg_attr(miri, track_caller)]
unsafe fn shrink_unchecked<A: Alloc + ?Sized>(
a: &A,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<u8>, AllocError> {
let new_ptr = tri!(a.alloc(new_layout)).cast::<u8>();
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), new_layout.size());
a.dealloc(ptr, old_layout);
Ok(new_ptr)
}
#[cfg_attr(miri, track_caller)]
pub(crate) unsafe fn ralloc<A: Alloc + ?Sized, F: Fn(usize) -> u8 + Clone>(
a: &A,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
pat: AllocPattern<F>,
) -> Result<NonNull<u8>, AllocError> {
match old_layout.size().cmp(&new_layout.size()) {
Ordering::Less => grow_unchecked(&a, ptr, old_layout, new_layout, pat),
Ordering::Greater => shrink_unchecked(&a, ptr, old_layout, new_layout),
Ordering::Equal => {
if new_layout.align() == old_layout.align() {
Ok(ptr)
} else {
grow_unchecked(&a, ptr, old_layout, new_layout, pat)
}
}
}
}
pub(crate) enum AllocPattern<F: Fn(usize) -> u8 + Clone> {
None,
Zero,
#[cfg(feature = "alloc_ext")]
All(u8),
#[cfg(feature = "alloc_ext")]
Fn(F),
#[cfg(not(feature = "alloc_ext"))]
#[allow(dead_code)]
PhantomFn(core::marker::PhantomData<F>),
}
pub mod helpers;