1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
#![doc = include_str!("../README.md")]
#![no_std]
#![warn(missing_docs)]
use core::{
cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, num::NonZeroUsize, ptr::NonNull,
};
/// Type-erased borrow of some unknown type chosen when constructing this type.
///
/// This type tries to act "borrow-like" which means that:
/// - It should be considered immutable: its target must not be changed while this pointer is alive.
/// - It must always points to a valid value of whatever the pointee type is.
/// - The lifetime `'a` accurately represents how long the pointer is valid for.
///
/// It may be helpful to think of this type as similar to `&'a dyn Any` but without
/// the metadata and able to point to data that does not correspond to a Rust type.
#[derive(Copy, Clone)]
pub struct Ptr<'a>(NonNull<u8>, PhantomData<&'a u8>);
/// Type-erased mutable borrow of some unknown type chosen when constructing this type.
///
/// This type tries to act "borrow-like" which means that:
/// - Pointer is considered exclusive and mutable. It cannot be cloned as this would lead to
/// aliased mutability.
/// - It must always points to a valid value of whatever the pointee type is.
/// - The lifetime `'a` accurately represents how long the pointer is valid for.
///
/// It may be helpful to think of this type as similar to `&'a mut dyn Any` but without
/// the metadata and able to point to data that does not correspond to a Rust type.
pub struct PtrMut<'a>(NonNull<u8>, PhantomData<&'a mut u8>);
/// Type-erased Box-like pointer to some unknown type chosen when constructing this type.
/// Conceptually represents ownership of whatever data is being pointed to and so is
/// responsible for calling its `Drop` impl. This pointer is _not_ responsible for freeing
/// the memory pointed to by this pointer as it may be pointing to an element in a `Vec` or
/// to a local in a function etc.
///
/// This type tries to act "borrow-like" like which means that:
/// - Pointer should be considered exclusive and mutable. It cannot be cloned as this would lead
/// to aliased mutability and potentially use after free bugs.
/// - It must always points to a valid value of whatever the pointee type is.
/// - The lifetime `'a` accurately represents how long the pointer is valid for.
///
/// It may be helpful to think of this type as similar to `&'a mut ManuallyDrop<dyn Any>` but
/// without the metadata and able to point to data that does not correspond to a Rust type.
pub struct OwningPtr<'a>(NonNull<u8>, PhantomData<&'a mut u8>);
macro_rules! impl_ptr {
($ptr:ident) => {
impl $ptr<'_> {
/// Calculates the offset from a pointer.
/// As the pointer is type-erased, there is no size information available. The provided
/// `count` parameter is in raw bytes.
///
/// *See also: [`ptr::offset`][ptr_offset]*
///
/// # Safety
/// the offset cannot make the existing ptr null, or take it out of bounds for its allocation.
///
/// [ptr_offset]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
#[inline]
pub unsafe fn byte_offset(self, count: isize) -> Self {
Self(
NonNull::new_unchecked(self.as_ptr().offset(count)),
PhantomData,
)
}
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
/// As the pointer is type-erased, there is no size information available. The provided
/// `count` parameter is in raw bytes.
///
/// *See also: [`ptr::add`][ptr_add]*
///
/// # Safety
/// the offset cannot make the existing ptr null, or take it out of bounds for its allocation.
///
/// [ptr_add]: https://doc.rust-lang.org/std/primitive.pointer.html#method.add
#[inline]
pub unsafe fn byte_add(self, count: usize) -> Self {
Self(
NonNull::new_unchecked(self.as_ptr().add(count)),
PhantomData,
)
}
/// Creates a new instance from a raw pointer.
///
/// # Safety
/// The lifetime for the returned item must not exceed the lifetime `inner` is valid for
#[inline]
pub unsafe fn new(inner: NonNull<u8>) -> Self {
Self(inner, PhantomData)
}
}
};
}
impl_ptr!(Ptr);
impl<'a> Ptr<'a> {
/// Transforms this [`Ptr`] into an [`PtrMut`]
///
/// # Safety
/// Another [`PtrMut`] for the same [`Ptr`] must not be created until the first is dropped.
#[inline]
pub unsafe fn assert_unique(self) -> PtrMut<'a> {
PtrMut(self.0, PhantomData)
}
/// Transforms this [`Ptr<T>`] into a `&T` with the same lifetime
///
/// # Safety
/// Must point to a valid `T`
#[inline]
pub unsafe fn deref<T>(self) -> &'a T {
&*self.as_ptr().cast()
}
/// Gets the underlying pointer, erasing the associated lifetime.
///
/// If possible, it is strongly encouraged to use [`deref`](Self::deref) over this function,
/// as it retains the lifetime.
#[inline]
#[allow(clippy::wrong_self_convention)]
pub fn as_ptr(self) -> *mut u8 {
self.0.as_ptr()
}
}
impl_ptr!(PtrMut);
impl<'a> PtrMut<'a> {
/// Transforms this [`PtrMut`] into an [`OwningPtr`]
///
/// # Safety
/// Must have right to drop or move out of [`PtrMut`].
#[inline]
pub unsafe fn promote(self) -> OwningPtr<'a> {
OwningPtr(self.0, PhantomData)
}
/// Transforms this [`PtrMut<T>`] into a `&mut T` with the same lifetime
///
/// # Safety
/// Must point to a valid `T`
#[inline]
pub unsafe fn deref_mut<T>(self) -> &'a mut T {
&mut *self.as_ptr().cast()
}
/// Gets the underlying pointer, erasing the associated lifetime.
///
/// If possible, it is strongly encouraged to use [`deref_mut`](Self::deref_mut) over
/// this function, as it retains the lifetime.
#[inline]
#[allow(clippy::wrong_self_convention)]
pub fn as_ptr(&self) -> *mut u8 {
self.0.as_ptr()
}
}
impl_ptr!(OwningPtr);
impl<'a> OwningPtr<'a> {
/// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen.
#[inline]
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
let mut temp = MaybeUninit::new(val);
// SAFETY: `temp.as_mut_ptr()` is a reference to a local value on the stack, so it cannot be null
let ptr = unsafe { NonNull::new_unchecked(temp.as_mut_ptr().cast::<u8>()) };
f(Self(ptr, PhantomData))
}
/// Consumes the [`OwningPtr`] to obtain ownership of the underlying data of type `T`.
///
/// # Safety
/// Must point to a valid `T`.
#[inline]
pub unsafe fn read<T>(self) -> T {
self.as_ptr().cast::<T>().read()
}
/// Consumes the [`OwningPtr`] to drop the underlying data of type `T`.
///
/// # Safety
/// Must point to a valid `T`.
#[inline]
pub unsafe fn drop_as<T>(self) {
self.as_ptr().cast::<T>().drop_in_place();
}
/// Gets the underlying pointer, erasing the associated lifetime.
///
/// If possible, it is strongly encouraged to use the other more type-safe functions
/// over this function.
#[inline]
#[allow(clippy::wrong_self_convention)]
pub fn as_ptr(&self) -> *mut u8 {
self.0.as_ptr()
}
}
/// Conceptually equivalent to `&'a [T]` but with length information cut out for performance reasons
pub struct ThinSlicePtr<'a, T> {
ptr: NonNull<T>,
#[cfg(debug_assertions)]
len: usize,
_marker: PhantomData<&'a [T]>,
}
impl<'a, T> ThinSlicePtr<'a, T> {
#[inline]
/// Indexes the slice without doing bounds checks
///
/// # Safety
/// `index` must be in-bounds.
pub unsafe fn get(self, index: usize) -> &'a T {
#[cfg(debug_assertions)]
debug_assert!(index < self.len);
&*self.ptr.as_ptr().add(index)
}
}
impl<'a, T> Clone for ThinSlicePtr<'a, T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
#[cfg(debug_assertions)]
len: self.len,
_marker: PhantomData,
}
}
}
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 {
Self {
// SAFETY: a reference can never be null
ptr: unsafe { NonNull::new_unchecked(slice.as_ptr() as *mut T) },
#[cfg(debug_assertions)]
len: slice.len(),
_marker: PhantomData,
}
}
}
/// Creates a dangling pointer with specified alignment.
/// See [`NonNull::dangling`].
pub fn dangling_with_align(align: NonZeroUsize) -> NonNull<u8> {
// SAFETY: The pointer will not be null, since it was created
// from the address of a `NonZeroUsize`.
unsafe { NonNull::new_unchecked(align.get() as *mut u8) }
}
mod private {
use core::cell::UnsafeCell;
pub trait SealedUnsafeCell {}
impl<'a, T> SealedUnsafeCell for &'a UnsafeCell<T> {}
}
/// Extension trait for helper methods on [`UnsafeCell`]
pub trait UnsafeCellDeref<'a, T>: private::SealedUnsafeCell {
/// # Safety
/// - The returned value must be unique and not alias any mutable or immutable references to the contents of the [`UnsafeCell`].
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
unsafe fn deref_mut(self) -> &'a mut T;
/// # Safety
/// - For the lifetime `'a` of the returned value you must not construct a mutable reference to the contents of the [`UnsafeCell`].
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
unsafe fn deref(self) -> &'a T;
/// Returns a copy of the contained value.
///
/// # Safety
/// - The [`UnsafeCell`] must not currently have a mutable reference to its content.
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
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 {
&mut *self.get()
}
#[inline]
unsafe fn deref(self) -> &'a T {
&*self.get()
}
#[inline]
unsafe fn read(self) -> T
where
T: Copy,
{
self.get().read()
}
}