#![doc = include_str!("../README.md")]
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![expect(unsafe_code, reason = "Raw pointers are inherently unsafe.")]
#![doc(
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
use core::{
cell::UnsafeCell,
fmt::{self, Debug, Formatter, Pointer},
marker::PhantomData,
mem::{self, ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut},
ptr::{self, NonNull},
};
#[derive(Debug, Copy, Clone)]
pub struct Aligned;
#[derive(Debug, Copy, Clone)]
pub struct Unaligned;
pub trait IsAligned: sealed::Sealed {
#[doc(hidden)]
unsafe fn read_ptr<T>(ptr: *const T) -> T;
#[doc(hidden)]
unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
#[doc(hidden)]
unsafe fn drop_in_place<T>(ptr: *mut T);
}
impl IsAligned for Aligned {
#[inline]
unsafe fn read_ptr<T>(ptr: *const T) -> T {
unsafe { ptr.read() }
}
#[inline]
unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
unsafe {
ptr::copy_nonoverlapping(src, dst, count);
}
}
#[inline]
unsafe fn drop_in_place<T>(ptr: *mut T) {
unsafe {
ptr::drop_in_place(ptr);
}
}
}
impl IsAligned for Unaligned {
#[inline]
unsafe fn read_ptr<T>(ptr: *const T) -> T {
unsafe { ptr.read_unaligned() }
}
#[inline]
unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
unsafe {
ptr::copy_nonoverlapping::<u8>(
src.cast::<u8>(),
dst.cast::<u8>(),
count * size_of::<T>(),
);
}
}
#[inline]
unsafe fn drop_in_place<T>(ptr: *mut T) {
unsafe {
drop(ptr.read_unaligned());
}
}
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::Aligned {}
impl Sealed for super::Unaligned {}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct ConstNonNull<T: ?Sized>(NonNull<T>);
impl<T: ?Sized> ConstNonNull<T> {
pub fn new(ptr: *const T) -> Option<Self> {
NonNull::new(ptr.cast_mut()).map(Self)
}
pub const unsafe fn new_unchecked(ptr: *const T) -> Self {
unsafe { Self(NonNull::new_unchecked(ptr.cast_mut())) }
}
#[inline]
pub unsafe fn as_ref<'a>(&self) -> &'a T {
unsafe { self.0.as_ref() }
}
}
impl<T: ?Sized> From<NonNull<T>> for ConstNonNull<T> {
fn from(value: NonNull<T>) -> ConstNonNull<T> {
ConstNonNull(value)
}
}
impl<'a, T: ?Sized> From<&'a T> for ConstNonNull<T> {
fn from(value: &'a T) -> ConstNonNull<T> {
ConstNonNull(NonNull::from(value))
}
}
impl<'a, T: ?Sized> From<&'a mut T> for ConstNonNull<T> {
fn from(value: &'a mut T) -> ConstNonNull<T> {
ConstNonNull(NonNull::from(value))
}
}
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Ptr<'a, A: IsAligned = Aligned>(NonNull<u8>, PhantomData<(&'a u8, A)>);
#[repr(transparent)]
pub struct PtrMut<'a, A: IsAligned = Aligned>(NonNull<u8>, PhantomData<(&'a mut u8, A)>);
#[repr(transparent)]
pub struct OwningPtr<'a, A: IsAligned = Aligned>(NonNull<u8>, PhantomData<(&'a mut u8, A)>);
#[repr(transparent)]
pub struct MovingPtr<'a, T, A: IsAligned = Aligned>(NonNull<T>, PhantomData<(&'a mut T, A)>);
macro_rules! impl_ptr {
($ptr:ident) => {
impl<'a> $ptr<'a, Aligned> {
pub fn to_unaligned(self) -> $ptr<'a, Unaligned> {
$ptr(self.0, PhantomData)
}
}
impl<'a, A: IsAligned> From<$ptr<'a, A>> for NonNull<u8> {
fn from(ptr: $ptr<'a, A>) -> Self {
ptr.0
}
}
impl<A: IsAligned> $ptr<'_, A> {
#[inline]
pub unsafe fn byte_offset(self, count: isize) -> Self {
Self(
unsafe { NonNull::new_unchecked(self.as_ptr().offset(count)) },
PhantomData,
)
}
#[inline]
pub unsafe fn byte_add(self, count: usize) -> Self {
Self(
unsafe { NonNull::new_unchecked(self.as_ptr().add(count)) },
PhantomData,
)
}
}
impl<A: IsAligned> Pointer for $ptr<'_, A> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Pointer::fmt(&self.0, f)
}
}
impl Debug for $ptr<'_, Aligned> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}<Aligned>({:?})", stringify!($ptr), self.0)
}
}
impl Debug for $ptr<'_, Unaligned> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}<Unaligned>({:?})", stringify!($ptr), self.0)
}
}
};
}
impl_ptr!(Ptr);
impl_ptr!(PtrMut);
impl_ptr!(OwningPtr);
impl<'a, T> MovingPtr<'a, T, Aligned> {
#[inline]
pub fn to_unaligned(self) -> MovingPtr<'a, T, Unaligned> {
let value = MovingPtr(self.0, PhantomData);
mem::forget(self);
value
}
#[inline]
pub unsafe fn from_value(value: &'a mut MaybeUninit<T>) -> Self {
MovingPtr(NonNull::from(value).cast::<T>(), PhantomData)
}
}
impl<'a, T, A: IsAligned> MovingPtr<'a, T, A> {
#[inline]
pub unsafe fn new(inner: NonNull<T>) -> Self {
Self(inner, PhantomData)
}
#[inline]
pub fn partial_move<R>(
self,
f: impl FnOnce(MovingPtr<'_, T, A>) -> R,
) -> (MovingPtr<'a, MaybeUninit<T>, A>, R) {
let partial_ptr = self.0;
let ret = f(self);
(
MovingPtr(partial_ptr.cast::<MaybeUninit<T>>(), PhantomData),
ret,
)
}
#[inline]
pub fn read(self) -> T {
let value = unsafe { A::read_ptr(self.0.as_ptr()) };
mem::forget(self);
value
}
#[inline]
pub unsafe fn write_to(self, dst: *mut T) {
let src = self.0.as_ptr();
mem::forget(self);
unsafe { A::copy_nonoverlapping(src, dst, 1) };
}
#[inline]
pub fn assign_to(self, dst: &mut T) {
unsafe {
ptr::drop_in_place(dst);
}
unsafe {
self.write_to(dst);
}
}
#[inline(always)]
pub unsafe fn move_field<U>(&self, f: impl Fn(*mut T) -> *mut U) -> MovingPtr<'a, U, A> {
MovingPtr(
unsafe { NonNull::new_unchecked(f(self.0.as_ptr())) },
PhantomData,
)
}
}
impl<'a, T, A: IsAligned> MovingPtr<'a, MaybeUninit<T>, A> {
#[inline(always)]
pub unsafe fn move_maybe_uninit_field<U>(
&self,
f: impl Fn(*mut T) -> *mut U,
) -> MovingPtr<'a, MaybeUninit<U>, A> {
let self_ptr = self.0.as_ptr().cast::<T>();
let field_ptr = unsafe { NonNull::new_unchecked(f(self_ptr)) };
MovingPtr(field_ptr.cast::<MaybeUninit<U>>(), PhantomData)
}
}
impl<'a, T, A: IsAligned> MovingPtr<'a, MaybeUninit<T>, A> {
#[inline]
pub unsafe fn assume_init(self) -> MovingPtr<'a, T, A> {
let value = MovingPtr(self.0.cast::<T>(), PhantomData);
mem::forget(self);
value
}
}
impl<T, A: IsAligned> Pointer for MovingPtr<'_, T, A> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Pointer::fmt(&self.0, f)
}
}
impl<T> Debug for MovingPtr<'_, T, Aligned> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "MovingPtr<Aligned>({:?})", self.0)
}
}
impl<T> Debug for MovingPtr<'_, T, Unaligned> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "MovingPtr<Unaligned>({:?})", self.0)
}
}
impl<'a, T, A: IsAligned> From<MovingPtr<'a, T, A>> for OwningPtr<'a, A> {
#[inline]
fn from(value: MovingPtr<'a, T, A>) -> Self {
let ptr = unsafe { OwningPtr::new(value.0.cast::<u8>()) };
mem::forget(value);
ptr
}
}
impl<'a, T> TryFrom<MovingPtr<'a, T, Unaligned>> for MovingPtr<'a, T, Aligned> {
type Error = MovingPtr<'a, T, Unaligned>;
#[inline]
fn try_from(value: MovingPtr<'a, T, Unaligned>) -> Result<Self, Self::Error> {
let ptr = value.0;
if ptr.as_ptr().is_aligned() {
mem::forget(value);
Ok(MovingPtr(ptr, PhantomData))
} else {
Err(value)
}
}
}
impl<T> Deref for MovingPtr<'_, T, Aligned> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
let ptr = self.0.as_ptr().debug_ensure_aligned();
unsafe { &*ptr }
}
}
impl<T> DerefMut for MovingPtr<'_, T, Aligned> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
let ptr = self.0.as_ptr().debug_ensure_aligned();
unsafe { &mut *ptr }
}
}
impl<T, A: IsAligned> Drop for MovingPtr<'_, T, A> {
fn drop(&mut self) {
unsafe { A::drop_in_place(self.0.as_ptr()) };
}
}
impl<'a, A: IsAligned> Ptr<'a, A> {
#[inline]
pub unsafe fn new(inner: NonNull<u8>) -> Self {
Self(inner, PhantomData)
}
#[inline]
pub unsafe fn assert_unique(self) -> PtrMut<'a, A> {
PtrMut(self.0, PhantomData)
}
#[inline]
pub unsafe fn deref<T>(self) -> &'a T {
let ptr = self.as_ptr().cast::<T>().debug_ensure_aligned();
unsafe { &*ptr }
}
#[inline]
pub fn as_ptr(self) -> *mut u8 {
self.0.as_ptr()
}
}
impl<'a, T: ?Sized> From<&'a T> for Ptr<'a> {
#[inline]
fn from(val: &'a T) -> Self {
unsafe { Self::new(NonNull::from(val).cast()) }
}
}
impl<'a, A: IsAligned> PtrMut<'a, A> {
#[inline]
pub unsafe fn new(inner: NonNull<u8>) -> Self {
Self(inner, PhantomData)
}
#[inline]
pub unsafe fn promote(self) -> OwningPtr<'a, A> {
OwningPtr(self.0, PhantomData)
}
#[inline]
pub unsafe fn deref_mut<T>(self) -> &'a mut T {
let ptr = self.as_ptr().cast::<T>().debug_ensure_aligned();
unsafe { &mut *ptr }
}
#[inline]
pub fn as_ptr(&self) -> *mut u8 {
self.0.as_ptr()
}
#[inline]
pub fn reborrow(&mut self) -> PtrMut<'_, A> {
unsafe { PtrMut::new(self.0) }
}
#[inline]
pub fn as_ref(&self) -> Ptr<'_, A> {
unsafe { Ptr::new(self.0) }
}
}
impl<'a, T: ?Sized> From<&'a mut T> for PtrMut<'a> {
#[inline]
fn from(val: &'a mut T) -> Self {
unsafe { Self::new(NonNull::from(val).cast()) }
}
}
impl<'a> OwningPtr<'a> {
unsafe fn make_internal<T>(temp: &mut ManuallyDrop<T>) -> OwningPtr<'_> {
unsafe { PtrMut::from(&mut *temp).promote() }
}
#[inline]
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
let mut val = ManuallyDrop::new(val);
f(unsafe { Self::make_internal(&mut val) })
}
}
impl<'a, A: IsAligned> OwningPtr<'a, A> {
#[inline]
pub unsafe fn new(inner: NonNull<u8>) -> Self {
Self(inner, PhantomData)
}
#[inline]
pub unsafe fn read<T>(self) -> T {
let ptr = self.as_ptr().cast::<T>().debug_ensure_aligned();
unsafe { ptr.read() }
}
#[inline]
pub unsafe fn cast<T>(self) -> MovingPtr<'a, T, A> {
MovingPtr(self.0.cast::<T>(), PhantomData)
}
#[inline]
pub unsafe fn drop_as<T>(self) {
let ptr = self.as_ptr().cast::<T>().debug_ensure_aligned();
unsafe {
ptr.drop_in_place();
}
}
#[inline]
pub fn as_ptr(&self) -> *mut u8 {
self.0.as_ptr()
}
#[inline]
pub fn as_ref(&self) -> Ptr<'_, A> {
unsafe { Ptr::new(self.0) }
}
#[inline]
pub fn as_mut(&mut self) -> PtrMut<'_, A> {
unsafe { PtrMut::new(self.0) }
}
}
impl<'a> OwningPtr<'a, Unaligned> {
pub unsafe fn read_unaligned<T>(self) -> T {
let ptr = self.as_ptr().cast::<T>();
unsafe { ptr.read_unaligned() }
}
}
pub struct ThinSlicePtr<'a, T> {
ptr: NonNull<T>,
#[cfg(debug_assertions)]
len: usize,
_marker: PhantomData<&'a [T]>,
}
impl<'a, T> ThinSlicePtr<'a, T> {
#[inline]
pub unsafe fn get_unchecked(&self, index: usize) -> &'a T {
#[cfg(debug_assertions)]
assert!(index < self.len, "tried to index out-of-bounds of a slice");
unsafe { &*self.ptr.add(index).as_ptr() }
}
#[deprecated(since = "0.18.0", note = "use get_unchecked() instead")]
pub unsafe fn get(self, index: usize) -> &'a T {
unsafe { self.get_unchecked(index) }
}
}
impl<'a, T> Clone for ThinSlicePtr<'a, T> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, T> Copy for ThinSlicePtr<'a, T> {}
impl<'a, T> From<&'a [T]> for ThinSlicePtr<'a, T> {
#[inline]
fn from(slice: &'a [T]) -> Self {
let ptr = slice.as_ptr().cast_mut().debug_ensure_aligned();
Self {
ptr: unsafe { NonNull::new_unchecked(ptr) },
#[cfg(debug_assertions)]
len: slice.len(),
_marker: PhantomData,
}
}
}
mod private {
use core::cell::UnsafeCell;
pub trait SealedUnsafeCell {}
impl<'a, T> SealedUnsafeCell for &'a UnsafeCell<T> {}
}
pub trait UnsafeCellDeref<'a, T>: private::SealedUnsafeCell {
unsafe fn deref_mut(self) -> &'a mut T;
unsafe fn deref(self) -> &'a T;
unsafe fn read(self) -> T
where
T: Copy;
}
impl<'a, T> UnsafeCellDeref<'a, T> for &'a UnsafeCell<T> {
#[inline]
unsafe fn deref_mut(self) -> &'a mut T {
unsafe { &mut *self.get() }
}
#[inline]
unsafe fn deref(self) -> &'a T {
unsafe { &*self.get() }
}
#[inline]
unsafe fn read(self) -> T
where
T: Copy,
{
unsafe { self.get().read() }
}
}
trait DebugEnsureAligned {
fn debug_ensure_aligned(self) -> Self;
}
#[cfg(all(debug_assertions, not(miri)))]
impl<T: Sized> DebugEnsureAligned for *mut T {
#[track_caller]
fn debug_ensure_aligned(self) -> Self {
assert!(
self.is_aligned(),
"pointer is not aligned. Address {:p} does not have alignment {} for type {}",
self,
align_of::<T>(),
core::any::type_name::<T>()
);
self
}
}
#[cfg(any(not(debug_assertions), miri))]
impl<T: Sized> DebugEnsureAligned for *mut T {
#[inline(always)]
fn debug_ensure_aligned(self) -> Self {
self
}
}
#[macro_export]
macro_rules! move_as_ptr {
($value: ident) => {
let mut $value = core::mem::MaybeUninit::new($value);
let $value = unsafe { $crate::MovingPtr::from_value(&mut $value) };
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! get_pattern {
($field_index:tt) => {
$field_index
};
($field_index:tt: $pattern:pat) => {
$pattern
};
}
#[macro_export]
macro_rules! deconstruct_moving_ptr {
({ let tuple { $($field_index:tt: $pattern:pat),* $(,)? } = $ptr:expr ;}) => {
let mut ptr: $crate::MovingPtr<_, _> = $ptr;
let _ = || {
let value = &mut *ptr;
core::hint::black_box(($(&mut value.$field_index,)*));
fn unreachable<T>(_index: usize) -> T {
unreachable!()
}
*value = ($(unreachable($field_index),)*);
};
$(let $pattern = unsafe { ptr.move_field(|f| &raw mut (*f).$field_index) };)*
core::mem::forget(ptr);
};
({ let MaybeUninit::<tuple> { $($field_index:tt: $pattern:pat),* $(,)? } = $ptr:expr ;}) => {
let mut ptr: $crate::MovingPtr<core::mem::MaybeUninit<_>, _> = $ptr;
let _ = || {
let value = unsafe { ptr.assume_init_mut() };
core::hint::black_box(($(&mut value.$field_index,)*));
fn unreachable<T>(_index: usize) -> T {
unreachable!()
}
*value = ($(unreachable($field_index),)*);
};
$(let $pattern = unsafe { ptr.move_maybe_uninit_field(|f| &raw mut (*f).$field_index) };)*
core::mem::forget(ptr);
};
({ let $struct_name:ident { $($field_index:tt$(: $pattern:pat)?),* $(,)? } = $ptr:expr ;}) => {
let mut ptr: $crate::MovingPtr<_, _> = $ptr;
let _ = || {
let value = &mut *ptr;
let $struct_name { $($field_index: _),* } = value;
core::hint::black_box(($(&mut value.$field_index),*));
let value: *mut _ = value;
$struct_name { ..unsafe { value.read() } };
};
$(let $crate::get_pattern!($field_index$(: $pattern)?) = unsafe { ptr.move_field(|f| &raw mut (*f).$field_index) };)*
core::mem::forget(ptr);
};
({ let MaybeUninit::<$struct_name:ident> { $($field_index:tt$(: $pattern:pat)?),* $(,)? } = $ptr:expr ;}) => {
let mut ptr: $crate::MovingPtr<core::mem::MaybeUninit<_>, _> = $ptr;
let _ = || {
let value = unsafe { ptr.assume_init_mut() };
let $struct_name { $($field_index: _),* } = value;
core::hint::black_box(($(&mut value.$field_index),*));
let value: *mut _ = value;
$struct_name { ..unsafe { value.read() } };
};
$(let $crate::get_pattern!($field_index$(: $pattern)?) = unsafe { ptr.move_maybe_uninit_field(|f| &raw mut (*f).$field_index) };)*
core::mem::forget(ptr);
};
}