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}