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