facet_core/
ptr.rs

1//! Opaque pointers
2//!
3//! Type-erased pointer helpers for working with reflected values
4
5use core::{marker::PhantomData, ptr::NonNull};
6
7/// A type-erased pointer to an uninitialized value
8#[derive(Debug, Clone, Copy)]
9pub struct PtrUninit<'mem>(*mut u8, PhantomData<&'mem mut ()>);
10
11impl<'mem> PtrUninit<'mem> {
12    /// Create a new opaque pointer from a mutable pointer
13    ///
14    /// This is safe because it's generic over T
15    pub fn new<T>(ptr: *mut T) -> Self {
16        Self(ptr as *mut u8, PhantomData)
17    }
18
19    /// Creates a new opaque pointer from a reference to a [`core::mem::MaybeUninit`]
20    ///
21    /// The pointer will point to the potentially uninitialized contents
22    ///
23    /// This is safe because it's generic over T
24    pub fn from_maybe_uninit<T>(borrow: &'mem mut core::mem::MaybeUninit<T>) -> Self {
25        Self(borrow.as_mut_ptr() as *mut u8, PhantomData)
26    }
27
28    /// Assumes the pointer is initialized and returns an `Opaque` pointer
29    ///
30    /// # Safety
31    ///
32    /// The pointer must actually be pointing to initialized memory of the correct type.
33    pub unsafe fn assume_init(self) -> PtrMut<'mem> {
34        let ptr = unsafe { NonNull::new_unchecked(self.0) };
35        PtrMut(ptr, PhantomData)
36    }
37
38    /// Write a value to this location and convert to an initialized pointer
39    ///
40    /// # Safety
41    ///
42    /// The pointer must be properly aligned for T and point to allocated memory
43    /// that can be safely written to.
44    pub unsafe fn put<T>(self, value: T) -> PtrMut<'mem> {
45        unsafe {
46            core::ptr::write(self.0 as *mut T, value);
47            self.assume_init()
48        }
49    }
50
51    /// Returns the underlying raw pointer as a byte pointer
52    pub fn as_mut_byte_ptr(self) -> *mut u8 {
53        self.0
54    }
55
56    /// Returns the underlying raw pointer as a const byte pointer
57    pub fn as_byte_ptr(self) -> *const u8 {
58        self.0
59    }
60
61    /// Returns a pointer with the given offset added
62    ///
63    /// # Safety
64    ///
65    /// Offset is within the bounds of the allocated memory
66    pub unsafe fn field_uninit_at(self, offset: usize) -> PtrUninit<'mem> {
67        PtrUninit(unsafe { self.0.byte_add(offset) }, PhantomData)
68    }
69
70    /// Returns a pointer with the given offset added, assuming it's initialized
71    ///
72    /// # Safety
73    ///
74    /// The pointer plus offset must be:
75    /// - Within bounds of the allocated object
76    /// - Properly aligned for the type being pointed to
77    /// - Point to initialized data of the correct type
78    pub unsafe fn field_init_at(self, offset: usize) -> PtrMut<'mem> {
79        PtrMut(
80            unsafe { NonNull::new_unchecked(self.0.add(offset)) },
81            PhantomData,
82        )
83    }
84}
85
86/// A type-erased read-only pointer to an initialized value.
87///
88/// Cannot be null. May be dangling (for ZSTs)
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
90pub struct PtrConst<'mem>(NonNull<u8>, PhantomData<&'mem ()>);
91
92unsafe impl Send for PtrConst<'_> {}
93unsafe impl Sync for PtrConst<'_> {}
94
95impl<'mem> PtrConst<'mem> {
96    /// Create a new opaque const pointer from a raw pointer
97    ///
98    /// # Safety
99    ///
100    /// The pointer must be non-null, valid, aligned, and point to initialized memory
101    /// of the correct type, and be valid for lifetime `'mem`.
102    ///
103    /// It's encouraged to take the address of something with `&raw const x`, rather than `&x`
104    pub const fn new<T>(ptr: *const T) -> Self {
105        unsafe { Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData) }
106    }
107
108    /// Gets the underlying raw pointer as a byte pointer
109    pub const fn as_byte_ptr(self) -> *const u8 {
110        self.0.as_ptr()
111    }
112
113    /// Gets the underlying raw pointer as a pointer of type T
114    ///
115    /// # Safety
116    ///
117    /// Must be called with the original type T that was used to create this pointer
118    pub const unsafe fn as_ptr<T>(self) -> *const T {
119        self.0.as_ptr() as *const T
120    }
121
122    /// Gets the underlying raw pointer as a const pointer of type T
123    ///
124    /// # Safety
125    ///
126    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
127    pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
128        // TODO: rename to `get`, or something else? it's technically a borrow...
129        unsafe { &*(self.0.as_ptr() as *const T) }
130    }
131
132    /// Returns a pointer with the given offset added
133    ///
134    /// # Safety
135    ///
136    /// Offset must be within the bounds of the allocated memory,
137    /// and the resulting pointer must be properly aligned.
138    pub const unsafe fn field(self, offset: usize) -> PtrConst<'mem> {
139        PtrConst(
140            unsafe { NonNull::new_unchecked(self.0.as_ptr().byte_add(offset)) },
141            PhantomData,
142        )
143    }
144
145    /// Exposes [`core::ptr::read`]
146    ///
147    /// # Safety
148    ///
149    /// `T` must be the actual underlying type of the pointed-to memory.
150    /// The memory must be properly initialized and aligned for type `T`.
151    pub const unsafe fn read<T>(self) -> T {
152        unsafe { core::ptr::read(self.as_ptr()) }
153    }
154}
155
156/// A type-erased pointer to an initialized value
157#[derive(Clone, Copy)]
158pub struct PtrMut<'mem>(NonNull<u8>, PhantomData<&'mem mut ()>);
159
160impl<'mem> PtrMut<'mem> {
161    /// Create a new opaque pointer from a raw pointer
162    ///
163    /// # Safety
164    ///
165    /// The pointer must be valid, aligned, and point to initialized memory
166    /// of the correct type, and be valid for lifetime `'mem`.
167    ///
168    /// It's encouraged to take the address of something with `&raw mut x`, rather than `&x`
169    pub const fn new<T>(ptr: *mut T) -> Self {
170        Self(
171            unsafe { NonNull::new_unchecked(ptr as *mut u8) },
172            PhantomData,
173        )
174    }
175
176    /// Gets the underlying raw pointer
177    pub const fn as_byte_ptr(self) -> *const u8 {
178        self.0.as_ptr()
179    }
180
181    /// Gets the underlying raw pointer as mutable
182    pub const fn as_mut_byte_ptr(self) -> *mut u8 {
183        self.0.as_ptr()
184    }
185
186    /// Gets the underlying raw pointer as a pointer of type T
187    ///
188    /// # Safety
189    ///
190    /// Must be called with the original type T that was used to create this pointer
191    pub const unsafe fn as_ptr<T>(self) -> *const T {
192        self.0.as_ptr() as *const T
193    }
194
195    /// Gets the underlying raw pointer as a mutable pointer of type T
196    ///
197    /// # Safety
198    ///
199    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
200    pub const unsafe fn as_mut<'borrow: 'mem, T>(self) -> &'borrow mut T {
201        unsafe { &mut *(self.0.as_ptr() as *mut T) }
202    }
203
204    /// Gets the underlying raw pointer as a const pointer of type T
205    ///
206    /// # Safety
207    ///
208    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
209    /// You must respect AXM (aliasing xor mutability). Holding onto the borrow while
210    /// calling as_mut is UB.
211    ///
212    /// Basically this is UB land. Careful.
213    pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
214        unsafe { &*(self.0.as_ptr() as *const T) }
215    }
216
217    /// Make a const ptr out of this mut ptr
218    pub const fn as_const<'borrow: 'mem>(self) -> PtrConst<'borrow> {
219        PtrConst(self.0, PhantomData)
220    }
221
222    /// Exposes [`core::ptr::read`]
223    ///
224    /// # Safety
225    ///
226    /// `T` must be the actual underlying type of the pointed-to memory.
227    /// The memory must be properly initialized and aligned for type `T`.
228    pub const unsafe fn read<T>(self) -> T {
229        unsafe { core::ptr::read(self.as_mut()) }
230    }
231
232    /// Exposes [`core::ptr::drop_in_place`]
233    ///
234    /// # Safety
235    ///
236    /// `T` must be the actual underlying type of the pointed-to memory.
237    /// The memory must be properly initialized and aligned for type `T`.
238    /// After calling this function, the memory should not be accessed again
239    /// until it is properly reinitialized.
240    pub unsafe fn drop_in_place<T>(self) -> PtrUninit<'mem> {
241        unsafe { core::ptr::drop_in_place(self.as_mut::<T>()) }
242        PtrUninit(self.0.as_ptr(), PhantomData)
243    }
244
245    /// Write a value to this location after dropping the existing value
246    ///
247    /// # Safety
248    ///
249    /// - The pointer must be properly aligned for T and point to allocated memory
250    ///   that can be safely written to.
251    /// - T must be the actual type of the object being pointed to
252    /// - The memory must already be initialized to a valid T value
253    pub unsafe fn replace<T>(self, value: T) -> Self {
254        unsafe { self.drop_in_place::<T>().put(value) }
255    }
256}