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 MaybeUninit<T>
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 write<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 /// Returns the underlying raw pointer as a byte pointer
52 pub fn as_mut_bytes(self) -> *mut u8 {
53 self.0
54 }
55
56 /// Returns the underlying raw pointer as a const byte pointer
57 pub fn as_bytes(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(self, offset: usize) -> OpaqueUninit<'mem> {
67 OpaqueUninit(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(self, offset: usize) -> Opaque<'mem> {
79 Opaque(
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)]
90pub struct OpaqueConst<'mem>(NonNull<u8>, PhantomData<&'mem ()>);
91
92impl<'mem> OpaqueConst<'mem> {
93 /// Create a new opaque const pointer from a raw pointer
94 ///
95 /// # Safety
96 ///
97 /// The pointer must be non-null, valid, aligned, and point to initialized memory
98 /// of the correct type, and be valid for lifetime `'mem`.
99 pub fn new<T>(ptr: *const T) -> Self {
100 unsafe { Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData) }
101 }
102
103 /// Gets the underlying raw pointer as a byte pointer
104 pub fn as_byte_ptr(self) -> *const u8 {
105 self.0.as_ptr()
106 }
107
108 /// Gets the underlying raw pointer as a pointer of type T
109 ///
110 /// # Safety
111 ///
112 /// Must be called with the original type T that was used to create this pointer
113 pub unsafe fn as_ptr<T>(self) -> *const T {
114 self.0.as_ptr() as *const T
115 }
116
117 /// Gets the underlying raw pointer as a const pointer of type T
118 ///
119 /// # Safety
120 ///
121 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
122 pub unsafe fn as_ref<'borrow: 'mem, T>(self) -> &'borrow T {
123 unsafe { &*(self.0.as_ptr() as *const T) }
124 }
125
126 /// Returns a pointer with the given offset added
127 ///
128 /// # Safety
129 ///
130 /// Offset must be within the bounds of the allocated memory,
131 /// and the resulting pointer must be properly aligned.
132 pub unsafe fn field(self, offset: usize) -> OpaqueConst<'mem> {
133 OpaqueConst(
134 unsafe { NonNull::new_unchecked(self.0.as_ptr().byte_add(offset)) },
135 PhantomData,
136 )
137 }
138
139 /// Exposes [`core::ptr::read`]
140 ///
141 /// # Safety
142 ///
143 /// `T` must be the actual underlying type of the pointed-to memory.
144 /// The memory must be properly initialized and aligned for type `T`.
145 pub unsafe fn read<T>(self) -> T {
146 unsafe { core::ptr::read(self.as_ptr()) }
147 }
148}
149
150/// A type-erased pointer to an initialized value
151#[derive(Clone, Copy)]
152pub struct Opaque<'mem>(NonNull<u8>, PhantomData<&'mem mut ()>);
153
154impl<'mem> Opaque<'mem> {
155 /// Create a new opaque pointer from a raw pointer
156 ///
157 /// # Safety
158 ///
159 /// The pointer must be valid, aligned, and point to initialized memory
160 /// of the correct type, and be valid for lifetime `'mem`.
161 pub fn new<T>(ptr: *mut T) -> Self {
162 Self(
163 unsafe { NonNull::new_unchecked(ptr as *mut u8) },
164 PhantomData,
165 )
166 }
167
168 /// Gets the underlying raw pointer
169 pub fn as_byte_ptr(self) -> *const u8 {
170 self.0.as_ptr()
171 }
172
173 /// Gets the underlying raw pointer as mutable
174 pub fn as_mut_byte_ptr(self) -> *mut u8 {
175 self.0.as_ptr()
176 }
177
178 /// Gets the underlying raw pointer as a pointer of type T
179 ///
180 /// # Safety
181 ///
182 /// Must be called with the original type T that was used to create this pointer
183 pub unsafe fn as_ptr<T>(self) -> *const T {
184 self.0.as_ptr() as *const T
185 }
186
187 /// Gets the underlying raw pointer as a mutable pointer of type T
188 ///
189 /// # Safety
190 ///
191 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
192 pub unsafe fn as_mut<'borrow: 'mem, T>(self) -> &'borrow mut T {
193 unsafe { &mut *(self.0.as_ptr() as *mut T) }
194 }
195
196 /// Gets the underlying raw pointer as a const pointer of type T
197 ///
198 /// # Safety
199 ///
200 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
201 /// You must respect AXM (aliasing xor mutability). Holding onto the borrow while
202 /// calling as_mut is UB.
203 ///
204 /// Basically this is UB land. Careful.
205 pub unsafe fn as_ref<'borrow: 'mem, T>(self) -> &'borrow T {
206 unsafe { &*(self.0.as_ptr() as *const T) }
207 }
208
209 /// Make a const ptr out of this mut ptr
210 pub fn as_const<'borrow: 'mem>(self) -> OpaqueConst<'borrow> {
211 OpaqueConst(self.0, PhantomData)
212 }
213
214 /// Exposes [`core::ptr::read`]
215 ///
216 /// # Safety
217 ///
218 /// `T` must be the actual underlying type of the pointed-to memory.
219 /// The memory must be properly initialized and aligned for type `T`.
220 pub unsafe fn read<T>(self) -> T {
221 unsafe { core::ptr::read(self.as_mut()) }
222 }
223
224 /// Exposes [`core::ptr::drop_in_place`]
225 ///
226 /// # Safety
227 ///
228 /// `T` must be the actual underlying type of the pointed-to memory.
229 /// The memory must be properly initialized and aligned for type `T`.
230 /// After calling this function, the memory should not be accessed again
231 /// until it is properly reinitialized.
232 pub unsafe fn drop_in_place<T>(self) {
233 unsafe { core::ptr::drop_in_place(self.as_mut::<T>()) }
234 }
235}