#![allow(deprecated)]
use core::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull, sync::atomic::AtomicUsize};
use self::vec::ptr_diff;
pub mod allocators;
#[crate::stabby]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct AllocationError();
impl core::fmt::Display for AllocationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("AllocationError")
}
}
#[cfg(feature = "std")]
impl std::error::Error for AllocationError {}
pub mod boxed;
pub mod collections;
pub mod single_or_vec;
pub mod string;
pub mod sync;
pub mod vec;
pub type DefaultAllocator = allocators::DefaultAllocator;
#[crate::stabby]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Layout {
pub size: usize,
pub align: usize,
}
impl Layout {
pub const fn of<T: Sized>() -> Self {
Layout {
size: core::mem::size_of::<T>(),
align: core::mem::align_of::<T>(),
}
}
#[allow(clippy::arithmetic_side_effects)]
pub const fn array<T: Sized>(n: usize) -> Self {
let Self { size, align } = Self::of::<T>();
Layout {
size: size * n,
align,
}
}
#[allow(clippy::arithmetic_side_effects)]
pub const fn concat(mut self, other: Self) -> Self {
self.size += other.size;
self.realign(if self.align < other.align {
other.align
} else {
self.align
})
}
#[inline]
pub fn next_matching<T>(self, ptr: *mut T) -> *mut T {
fn next_matching(align: usize, ptr: *mut u8) -> *mut u8 {
unsafe { ptr.add(ptr.align_offset(align)) }
}
next_matching(self.align, ptr.cast()).cast()
}
#[allow(clippy::arithmetic_side_effects)]
pub const fn realign(mut self, new_align: usize) -> Self {
self.align = new_align;
self.size = self.size
+ (new_align - (self.size % new_align)) * (((self.size % new_align) != 0) as usize);
self
}
}
pub trait IAlloc: Unpin {
fn alloc(&mut self, layout: Layout) -> *mut ();
unsafe fn free(&mut self, ptr: *mut ());
unsafe fn realloc(&mut self, ptr: *mut (), prev_layout: Layout, new_size: usize) -> *mut () {
let ret = self.alloc(Layout {
size: new_size,
align: prev_layout.align,
});
if !ret.is_null() {
unsafe {
core::ptr::copy_nonoverlapping(ptr.cast::<u8>(), ret.cast(), prev_layout.size);
self.free(ptr);
}
}
ret
}
}
#[crate::stabby]
#[deprecated = "Stabby doesn't actually use this trait due to conflicts."]
pub trait IStableAlloc: Unpin {
extern "C" fn alloc(&mut self, layout: Layout) -> *mut ();
extern "C" fn free(&mut self, ptr: *mut ());
extern "C" fn realloc(
&mut self,
ptr: *mut (),
prev_layout: Layout,
new_size: usize,
) -> *mut () {
let ret = self.alloc(Layout {
size: new_size,
align: prev_layout.align,
});
if !ret.is_null() {
unsafe {
core::ptr::copy_nonoverlapping(ptr.cast::<u8>(), ret.cast(), prev_layout.size);
self.free(ptr);
}
}
ret
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
impl<T: IAlloc> IStableAlloc for T {
extern "C" fn alloc(&mut self, layout: Layout) -> *mut () {
IAlloc::alloc(self, layout)
}
extern "C" fn free(&mut self, ptr: *mut ()) {
unsafe { IAlloc::free(self, ptr) }
}
extern "C" fn realloc(
&mut self,
ptr: *mut (),
prev_layout: Layout,
new_size: usize,
) -> *mut () {
unsafe { IAlloc::realloc(self, ptr, prev_layout, new_size) }
}
}
impl<T: IStableAllocDynMut<crate::vtable::H> + Unpin> IAlloc for T {
fn alloc(&mut self, layout: Layout) -> *mut () {
IStableAllocDynMut::alloc(self, layout)
}
unsafe fn free(&mut self, ptr: *mut ()) {
IStableAllocDynMut::free(self, ptr)
}
unsafe fn realloc(&mut self, ptr: *mut (), prev_layout: Layout, new_size: usize) -> *mut () {
IStableAllocDynMut::realloc(self, ptr, prev_layout, new_size)
}
}
impl IAlloc for core::convert::Infallible {
fn alloc(&mut self, _layout: Layout) -> *mut () {
unreachable!()
}
unsafe fn free(&mut self, _ptr: *mut ()) {
unreachable!()
}
}
#[crate::stabby]
pub struct AllocPrefix<Alloc> {
pub strong: core::sync::atomic::AtomicUsize,
pub weak: core::sync::atomic::AtomicUsize,
pub capacity: core::sync::atomic::AtomicUsize,
pub origin: NonNull<()>,
pub alloc: core::mem::MaybeUninit<Alloc>,
}
impl<Alloc> AllocPrefix<Alloc> {
pub const fn skip_to<T>() -> usize {
let mut size = core::mem::size_of::<Self>();
let mut align = core::mem::align_of::<T>();
if align == 0 {
align = 1
}
let sizemodalign = size.rem_euclid(align);
if sizemodalign != 0 {
size = size.wrapping_add(align).wrapping_sub(sizemodalign);
}
size
}
}
#[crate::stabby]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AllocPtr<T, Alloc> {
pub ptr: NonNull<T>,
pub marker: PhantomData<Alloc>,
}
impl<T, Alloc> Copy for AllocPtr<T, Alloc> {}
impl<T, Alloc> Clone for AllocPtr<T, Alloc> {
fn clone(&self) -> Self {
*self
}
}
impl<T, Alloc> core::ops::Deref for AllocPtr<T, Alloc> {
type Target = NonNull<T>;
fn deref(&self) -> &Self::Target {
&self.ptr
}
}
impl<T, Alloc> core::ops::DerefMut for AllocPtr<T, Alloc> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ptr
}
}
impl<T, Alloc> AllocPtr<MaybeUninit<T>, Alloc> {
pub const unsafe fn assume_init(self) -> AllocPtr<T, Alloc> {
unsafe { core::mem::transmute::<Self, AllocPtr<T, Alloc>>(self) }
}
}
impl<T, Alloc> AllocPtr<T, Alloc> {
pub const fn dangling() -> Self {
Self {
ptr: NonNull::dangling(),
marker: PhantomData,
}
}
pub const fn cast<U>(self) -> AllocPtr<U, Alloc> {
AllocPtr {
ptr: self.ptr.cast(),
marker: PhantomData,
}
}
const fn prefix_ptr(&self) -> NonNull<AllocPrefix<Alloc>> {
unsafe { NonNull::new_unchecked(self.ptr.as_ptr().cast::<AllocPrefix<Alloc>>().sub(1)) }
}
#[rustversion::attr(since(1.73), const)]
pub unsafe fn prefix(&self) -> &AllocPrefix<Alloc> {
unsafe { self.prefix_ptr().as_ref() }
}
#[rustversion::attr(since(1.86), const)]
pub unsafe fn prefix_mut(&mut self) -> &mut AllocPrefix<Alloc> {
unsafe { self.prefix_ptr().as_mut() }
}
#[rustversion::attr(since(1.86), const)]
pub unsafe fn split_mut(&mut self) -> (&mut AllocPrefix<Alloc>, &mut T) {
let prefix = self.prefix_ptr().as_mut();
let data = self.ptr.as_mut();
(prefix, data)
}
pub unsafe fn init(ptr: NonNull<()>, capacity: usize) -> Self {
let shifted_for_prefix = ptr
.as_ptr()
.cast::<AllocPrefix<Alloc>>()
.add(1)
.cast::<u8>();
let inited = shifted_for_prefix
.cast::<u8>()
.add(shifted_for_prefix.align_offset(core::mem::align_of::<T>()))
.cast::<T>();
let this: Self = AllocPtr {
ptr: NonNull::new_unchecked(inited),
marker: core::marker::PhantomData,
};
this.prefix_ptr().as_ptr().write(AllocPrefix {
strong: AtomicUsize::new(1),
weak: AtomicUsize::new(1),
capacity: AtomicUsize::new(capacity),
origin: ptr,
alloc: core::mem::MaybeUninit::uninit(),
});
this
}
}
impl<T, Alloc: IAlloc> AllocPtr<T, Alloc> {
pub fn alloc(alloc: &mut Alloc) -> Option<Self> {
Self::alloc_array(alloc, 1)
}
pub fn alloc_array(alloc: &mut Alloc, capacity: usize) -> Option<Self> {
let mut layout = Layout::of::<AllocPrefix<Alloc>>().concat(Layout::array::<T>(capacity));
layout.align = core::mem::align_of::<AllocPrefix<Alloc>>();
let ptr = alloc.alloc(layout);
NonNull::new(ptr).map(|ptr| unsafe { Self::init(ptr, capacity) })
}
pub unsafe fn realloc(
self,
alloc: &mut Alloc,
prev_capacity: usize,
new_capacity: usize,
) -> Option<Self> {
let mut layout =
Layout::of::<AllocPrefix<Alloc>>().concat(Layout::array::<T>(prev_capacity));
layout.align = core::mem::align_of::<AllocPrefix<Alloc>>();
let ptr = alloc.realloc(
self.prefix_ptr().cast().as_ptr(),
layout,
Layout::of::<AllocPrefix<Alloc>>()
.concat(Layout::array::<T>(new_capacity))
.size,
);
NonNull::new(ptr).map(|ptr| unsafe { Self::init(ptr, new_capacity) })
}
pub unsafe fn free(self, alloc: &mut Alloc) {
alloc.free(self.prefix().origin.as_ptr())
}
}
#[crate::stabby]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AllocSlice<T, Alloc> {
pub start: AllocPtr<T, Alloc>,
pub end: NonNull<T>,
}
impl<T, Alloc> AllocSlice<T, Alloc> {
pub const fn len(&self) -> usize {
ptr_diff(self.end, self.start.ptr)
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub const unsafe fn as_slice(&self) -> &[T] {
core::slice::from_raw_parts(self.start.ptr.as_ptr(), ptr_diff(self.end, self.start.ptr))
}
}
impl<T, Alloc> Copy for AllocSlice<T, Alloc> {}
impl<T, Alloc> Clone for AllocSlice<T, Alloc> {
fn clone(&self) -> Self {
*self
}
}