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