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}