facet_core/opaque/
mod.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(Clone, Copy)]
9pub struct OpaqueUninit<'mem>(*mut u8, PhantomData<&'mem mut ()>);
10
11impl<'mem> OpaqueUninit<'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) -> Opaque<'mem> {
34        let ptr = unsafe { NonNull::new_unchecked(self.0) };
35        Opaque(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) -> Opaque<'mem> {
45        unsafe {
46            core::ptr::write(self.0 as *mut T, value);
47            self.assume_init()
48        }
49    }
50
51    /// Copies data from the source pointer to this location and converts to an initialized pointer
52    ///
53    /// # Safety
54    ///
55    /// - The destination pointer must be properly aligned and point to allocated memory
56    ///   that can be safely written to.
57    /// - The source pointer must point to properly initialized data.
58    /// - Both pointers must refer to objects of the same type and size.
59    pub unsafe fn write(self, source: OpaqueConst<'_>) -> Opaque<'mem> {
60        unsafe {
61            core::ptr::copy_nonoverlapping(
62                source.as_byte_ptr(),
63                self.0,
64                core::mem::size_of_val(&*source.as_byte_ptr()),
65            );
66            self.assume_init()
67        }
68    }
69
70    /// Returns the underlying raw pointer as a byte pointer
71    pub fn as_mut_bytes(self) -> *mut u8 {
72        self.0
73    }
74
75    /// Returns the underlying raw pointer as a const byte pointer
76    pub fn as_bytes(self) -> *const u8 {
77        self.0
78    }
79
80    /// Returns a pointer with the given offset added
81    ///
82    /// # Safety
83    ///
84    /// Offset is within the bounds of the allocated memory
85    pub unsafe fn field_uninit(self, offset: usize) -> OpaqueUninit<'mem> {
86        OpaqueUninit(unsafe { self.0.byte_add(offset) }, PhantomData)
87    }
88
89    /// Returns a pointer with the given offset added, assuming it's initialized
90    ///
91    /// # Safety
92    ///
93    /// The pointer plus offset must be:
94    /// - Within bounds of the allocated object
95    /// - Properly aligned for the type being pointed to
96    /// - Point to initialized data of the correct type
97    pub unsafe fn field_init(self, offset: usize) -> Opaque<'mem> {
98        Opaque(
99            unsafe { NonNull::new_unchecked(self.0.add(offset)) },
100            PhantomData,
101        )
102    }
103}
104
105/// A type-erased read-only pointer to an initialized value.
106///
107/// Cannot be null. May be dangling (for ZSTs)
108#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
109pub struct OpaqueConst<'mem>(NonNull<u8>, PhantomData<&'mem ()>);
110
111unsafe impl Send for OpaqueConst<'_> {}
112unsafe impl Sync for OpaqueConst<'_> {}
113
114impl<'mem> OpaqueConst<'mem> {
115    /// Create a new opaque const pointer from a raw pointer
116    ///
117    /// # Safety
118    ///
119    /// The pointer must be non-null, valid, aligned, and point to initialized memory
120    /// of the correct type, and be valid for lifetime `'mem`.
121    ///
122    /// It's encouraged to take the address of something with `&raw const x`, rather than `&x`
123    pub const fn new<T>(ptr: *const T) -> Self {
124        unsafe { Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData) }
125    }
126
127    /// Gets the underlying raw pointer as a byte pointer
128    pub const fn as_byte_ptr(self) -> *const u8 {
129        self.0.as_ptr()
130    }
131
132    /// Gets the underlying raw pointer as a pointer of type T
133    ///
134    /// # Safety
135    ///
136    /// Must be called with the original type T that was used to create this pointer
137    pub const unsafe fn as_ptr<T>(self) -> *const T {
138        self.0.as_ptr() as *const T
139    }
140
141    /// Gets the underlying raw pointer as a const pointer of type T
142    ///
143    /// # Safety
144    ///
145    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
146    pub const unsafe fn as_ref<'borrow: 'mem, T>(self) -> &'borrow T {
147        unsafe { &*(self.0.as_ptr() as *const T) }
148    }
149
150    /// Returns a pointer with the given offset added
151    ///
152    /// # Safety
153    ///
154    /// Offset must be within the bounds of the allocated memory,
155    /// and the resulting pointer must be properly aligned.
156    pub const unsafe fn field(self, offset: usize) -> OpaqueConst<'mem> {
157        OpaqueConst(
158            unsafe { NonNull::new_unchecked(self.0.as_ptr().byte_add(offset)) },
159            PhantomData,
160        )
161    }
162
163    /// Exposes [`core::ptr::read`]
164    ///
165    /// # Safety
166    ///
167    /// `T` must be the actual underlying type of the pointed-to memory.
168    /// The memory must be properly initialized and aligned for type `T`.
169    pub const unsafe fn read<T>(self) -> T {
170        unsafe { core::ptr::read(self.as_ptr()) }
171    }
172}
173
174/// A type-erased pointer to an initialized value
175#[derive(Clone, Copy)]
176pub struct Opaque<'mem>(NonNull<u8>, PhantomData<&'mem mut ()>);
177
178impl<'mem> Opaque<'mem> {
179    /// Create a new opaque pointer from a raw pointer
180    ///
181    /// # Safety
182    ///
183    /// The pointer must be valid, aligned, and point to initialized memory
184    /// of the correct type, and be valid for lifetime `'mem`.
185    ///
186    /// It's encouraged to take the address of something with `&raw mut x`, rather than `&x`
187    pub const fn new<T>(ptr: *mut T) -> Self {
188        Self(
189            unsafe { NonNull::new_unchecked(ptr as *mut u8) },
190            PhantomData,
191        )
192    }
193
194    /// Gets the underlying raw pointer
195    pub const fn as_byte_ptr(self) -> *const u8 {
196        self.0.as_ptr()
197    }
198
199    /// Gets the underlying raw pointer as mutable
200    pub const fn as_mut_byte_ptr(self) -> *mut u8 {
201        self.0.as_ptr()
202    }
203
204    /// Gets the underlying raw pointer as a pointer of type T
205    ///
206    /// # Safety
207    ///
208    /// Must be called with the original type T that was used to create this pointer
209    pub const unsafe fn as_ptr<T>(self) -> *const T {
210        self.0.as_ptr() as *const T
211    }
212
213    /// Gets the underlying raw pointer as a mutable pointer of type T
214    ///
215    /// # Safety
216    ///
217    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
218    pub const unsafe fn as_mut<'borrow: 'mem, T>(self) -> &'borrow mut T {
219        unsafe { &mut *(self.0.as_ptr() as *mut T) }
220    }
221
222    /// Gets the underlying raw pointer as a const pointer of type T
223    ///
224    /// # Safety
225    ///
226    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
227    /// You must respect AXM (aliasing xor mutability). Holding onto the borrow while
228    /// calling as_mut is UB.
229    ///
230    /// Basically this is UB land. Careful.
231    pub const unsafe fn as_ref<'borrow: 'mem, T>(self) -> &'borrow T {
232        unsafe { &*(self.0.as_ptr() as *const T) }
233    }
234
235    /// Make a const ptr out of this mut ptr
236    pub const fn as_const<'borrow: 'mem>(self) -> OpaqueConst<'borrow> {
237        OpaqueConst(self.0, PhantomData)
238    }
239
240    /// Exposes [`core::ptr::read`]
241    ///
242    /// # Safety
243    ///
244    /// `T` must be the actual underlying type of the pointed-to memory.
245    /// The memory must be properly initialized and aligned for type `T`.
246    pub const unsafe fn read<T>(self) -> T {
247        unsafe { core::ptr::read(self.as_mut()) }
248    }
249
250    /// Exposes [`core::ptr::drop_in_place`]
251    ///
252    /// # Safety
253    ///
254    /// `T` must be the actual underlying type of the pointed-to memory.
255    /// The memory must be properly initialized and aligned for type `T`.
256    /// After calling this function, the memory should not be accessed again
257    /// until it is properly reinitialized.
258    pub unsafe fn drop_in_place<T>(self) -> OpaqueUninit<'mem> {
259        unsafe { core::ptr::drop_in_place(self.as_mut::<T>()) }
260        OpaqueUninit(self.0.as_ptr(), PhantomData)
261    }
262
263    /// Write a value to this location after dropping the existing value
264    ///
265    /// # Safety
266    ///
267    /// - The pointer must be properly aligned for T and point to allocated memory
268    ///   that can be safely written to.
269    /// - T must be the actual type of the object being pointed to
270    /// - The memory must already be initialized to a valid T value
271    pub unsafe fn replace<T>(self, value: T) -> Self {
272        unsafe { self.drop_in_place::<T>().put(value) }
273    }
274
275    /// Copies data from the source pointer to this location
276    ///
277    /// # Safety
278    ///
279    /// - The destination pointer must be properly aligned and point to allocated memory
280    ///   that can be safely written to.
281    /// - The source pointer must point to properly initialized data.
282    /// - Both pointers must refer to objects of the same type and size.
283    pub unsafe fn write(self, source: OpaqueConst<'_>) -> Self {
284        unsafe {
285            let size = core::mem::size_of_val(&*source.as_byte_ptr());
286            core::ptr::copy_nonoverlapping(source.as_byte_ptr(), self.0.as_ptr(), size);
287            self
288        }
289    }
290}