use core::{
alloc::{Layout, LayoutError},
fmt, hint,
marker::PhantomData,
ptr::NonNull,
};
use std::alloc::{alloc_zeroed, realloc};
#[repr(transparent)]
struct Unique<T: ?Sized> {
pointer: NonNull<T>,
_marker: PhantomData<T>,
}
impl<T: ?Sized> Unique<T> {
#[inline]
pub const unsafe fn _new_unchecked(ptr: *mut T) -> Self {
unsafe {
Unique {
pointer: NonNull::new_unchecked(ptr),
_marker: PhantomData,
}
}
}
#[inline]
pub fn _new(ptr: *mut T) -> Option<Self> {
if let Some(pointer) = NonNull::new(ptr) {
Some(Unique {
pointer,
_marker: PhantomData,
})
} else {
None
}
}
#[must_use = "`self` will be dropped if the result is not used"]
#[inline]
pub const fn as_ptr(self) -> *mut T {
self.pointer.as_ptr()
}
#[must_use]
#[inline]
pub const unsafe fn _as_ref(&self) -> &T {
unsafe { self.pointer.as_ref() }
}
#[must_use]
#[inline]
pub unsafe fn _as_mut(&mut self) -> &mut T {
unsafe { self.pointer.as_mut() }
}
#[must_use = "`self` will be dropped if the result is not used"]
#[inline]
pub const fn _cast<U>(self) -> Unique<U> {
unsafe { Unique::_new_unchecked(self.pointer.cast().as_ptr()) }
}
}
impl<T: ?Sized> Clone for Unique<T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for Unique<T> {}
impl<T: ?Sized> fmt::Debug for Unique<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_ptr(), f)
}
}
impl<T: ?Sized> fmt::Pointer for Unique<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_ptr(), f)
}
}
impl<T: ?Sized> From<&mut T> for Unique<T> {
#[inline]
fn from(reference: &mut T) -> Self {
Self::from(NonNull::from(reference))
}
}
impl<T: ?Sized> From<NonNull<T>> for Unique<T> {
#[inline]
fn from(pointer: NonNull<T>) -> Self {
Unique {
pointer,
_marker: PhantomData,
}
}
}
#[repr(transparent)]
#[derive(Debug)]
pub(crate) struct RawSoAVecInner {
ptr: Unique<u8>,
}
#[derive(Debug, Clone)]
pub enum AllocError {
CapacityOverflow,
AllocationFailure,
LayoutError(LayoutError),
}
impl RawSoAVecInner {
#[inline]
const fn new_in(layout: Layout) -> Self {
let ptr = unsafe { core::mem::transmute::<usize, Unique<u8>>(layout.align()) };
Self { ptr }
}
#[inline]
pub(crate) fn with_layout(layout: Layout) -> Result<Self, AllocError> {
if layout.size() == 0 {
return Ok(Self::new_in(layout));
}
if alloc_guard(layout.size()).is_err() {
return Err(AllocError::AllocationFailure);
}
let result = unsafe { alloc_zeroed(layout) };
let ptr = match NonNull::new(result) {
Some(ptr) => ptr,
None => return Err(AllocError::AllocationFailure),
};
Ok(Self {
ptr: Unique::from(ptr),
})
}
#[cold]
pub(crate) fn grow_amortized_inner(
&mut self,
new_layout: Layout,
old_layout: Option<Layout>,
) -> Result<(), AllocError> {
if new_layout.size() == 0 {
return Err(AllocError::CapacityOverflow);
}
let Some(ptr) = finish_grow(new_layout, old_layout.map(|l| (self.ptr.pointer, l))) else {
return Err(AllocError::AllocationFailure);
};
unsafe { self.set_ptr(ptr) };
Ok(())
}
#[inline]
unsafe fn set_ptr(&mut self, ptr: NonNull<u8>) {
self.ptr = Unique::from(ptr.cast());
}
pub(crate) unsafe fn deallocate(&mut self, layout: Layout) {
unsafe {
std::alloc::dealloc(self.ptr.as_ptr(), layout);
}
}
#[inline]
pub(crate) const fn ptr(&self) -> NonNull<u8> {
self.ptr.pointer
}
}
#[inline]
fn alloc_guard(alloc_size: usize) -> Result<(), ()> {
if usize::BITS < 64 && alloc_size > isize::MAX as usize {
Err(())
} else {
Ok(())
}
}
#[inline(never)]
fn finish_grow(
new_layout: Layout,
current_memory: Option<(NonNull<u8>, Layout)>,
) -> Option<NonNull<u8>> {
if alloc_guard(new_layout.size()).is_err() {
return None;
}
let memory = if let Some((ptr, old_layout)) = current_memory {
debug_assert_eq!(old_layout.align(), new_layout.align());
unsafe {
hint::assert_unchecked(old_layout.align() == new_layout.align());
realloc(ptr.as_ptr(), old_layout, new_layout.size())
}
} else {
unsafe { std::alloc::alloc(new_layout) }
};
NonNull::new(memory)
}