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