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}