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//     /// Copies memory from a source pointer into this location and returns PtrMut
162//     ///
163//     /// # Safety
164//     ///
165//     /// - The source pointer must be valid for reads of `len` bytes
166//     /// - This pointer must be valid for writes of `len` bytes and properly aligned
167//     /// - The regions may not overlap
168//     pub unsafe fn copy_from<'src>(
169//         self,
170//         src: PtrConst<'src>,
171//         shape: &'static Shape,
172//     ) -> Result<PtrMut<'mem>, UnsizedError> {
173//         let layout = shape.layout.sized_layout()?;
174//         // SAFETY: The caller is responsible for upholding the invariants:
175//         // - `src` must be valid for reads of `shape.size` bytes
176//         // - `self` must be valid for writes of `shape.size` bytes and properly aligned
177//         // - The regions may not overlap
178//         unsafe {
179//             core::ptr::copy_nonoverlapping(src.as_byte_ptr(), self.0, layout.size());
180//             Ok(self.assume_init())
181//         }
182//     }
183
184//     /// Creates a new opaque pointer from a reference to a [`core::mem::MaybeUninit`]
185//     ///
186//     /// The pointer will point to the potentially uninitialized contents
187//     ///
188//     /// This is safe because it's generic over T
189//     pub fn from_maybe_uninit<T>(borrow: &'mem mut core::mem::MaybeUninit<T>) -> Self {
190//         Self(borrow.as_mut_ptr() as *mut u8, PhantomData)
191//     }
192
193//     /// Assumes the pointer is initialized and returns an `Opaque` pointer
194//     ///
195//     /// # Safety
196//     ///
197//     /// The pointer must actually be pointing to initialized memory of the correct type.
198//     pub unsafe fn assume_init(self) -> PtrMut<'mem> {
199//         let ptr = unsafe { NonNull::new_unchecked(self.0) };
200//         PtrMut(ptr, PhantomData)
201//     }
202
203//     /// Returns the underlying raw pointer as a byte pointer
204//     pub fn as_mut_byte_ptr(self) -> *mut u8 {
205//         self.0
206//     }
207
208//     /// Returns the underlying raw pointer as a const byte pointer
209//     pub fn as_byte_ptr(self) -> *const u8 {
210//         self.0
211//     }
212
213//     /// Returns a pointer with the given offset added, assuming it's initialized
214//     ///
215//     /// # Safety
216//     ///
217//     /// The pointer plus offset must be:
218//     /// - Within bounds of the allocated object
219//     /// - Properly aligned for the type being pointed to
220//     /// - Point to initialized data of the correct type
221//     pub unsafe fn field_init_at(self, offset: usize) -> PtrMut<'mem> {
222//         PtrMut(
223//             unsafe { NonNull::new_unchecked(self.0.add(offset)) },
224//             PhantomData,
225//         )
226//     }
227// }
228
229/// A type-erased read-only pointer to an initialized value.
230///
231/// Cannot be null. May be dangling (for ZSTs)
232#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
233#[repr(transparent)]
234pub struct PtrConst<'mem>(NonNull<u8>, PhantomData<&'mem ()>);
235
236unsafe impl Send for PtrConst<'_> {}
237unsafe impl Sync for PtrConst<'_> {}
238
239impl<'mem> PtrConst<'mem> {
240    /// Create a new opaque const pointer from a raw pointer
241    ///
242    /// # Safety
243    ///
244    /// The pointer must be non-null, valid, aligned, and point to initialized memory
245    /// of the correct type, and be valid for lifetime `'mem`.
246    ///
247    /// It's encouraged to take the address of something with `&raw const x`, rather than `&x`
248    pub const fn new<T>(ptr: *const T) -> Self {
249        unsafe { Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData) }
250    }
251
252    /// Gets the underlying raw pointer as a byte pointer
253    pub const fn as_byte_ptr(self) -> *const u8 {
254        self.0.as_ptr()
255    }
256
257    /// Gets the underlying raw pointer as a pointer of type T
258    ///
259    /// # Safety
260    ///
261    /// Must be called with the original type T that was used to create this pointer
262    pub const unsafe fn as_ptr<T>(self) -> *const T {
263        if core::mem::size_of::<*const T>() == core::mem::size_of::<*const u8>() {
264            unsafe { core::mem::transmute_copy(&(self.0.as_ptr())) }
265        } else {
266            panic!("cannot!");
267        }
268    }
269
270    /// Gets the underlying raw pointer as a const pointer of type T
271    ///
272    /// # Safety
273    ///
274    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
275    pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
276        // TODO: rename to `get`, or something else? it's technically a borrow...
277        unsafe { &*(self.0.as_ptr() as *const T) }
278    }
279
280    /// Returns a pointer with the given offset added
281    ///
282    /// # Safety
283    ///
284    /// Offset must be within the bounds of the allocated memory,
285    /// and the resulting pointer must be properly aligned.
286    pub const unsafe fn field(self, offset: usize) -> PtrConst<'mem> {
287        PtrConst(
288            unsafe { NonNull::new_unchecked(self.0.as_ptr().byte_add(offset)) },
289            PhantomData,
290        )
291    }
292
293    /// Exposes [`core::ptr::read`]
294    ///
295    /// # Safety
296    ///
297    /// `T` must be the actual underlying type of the pointed-to memory.
298    /// The memory must be properly initialized and aligned for type `T`.
299    pub const unsafe fn read<T>(self) -> T {
300        unsafe { core::ptr::read(self.as_ptr()) }
301    }
302}
303
304/// A type-erased pointer to an initialized value
305#[derive(Clone, Copy)]
306#[repr(transparent)]
307pub struct PtrMut<'mem>(NonNull<u8>, PhantomData<&'mem mut ()>);
308
309impl<'mem> PtrMut<'mem> {
310    /// Create a new opaque pointer from a raw pointer
311    ///
312    /// # Safety
313    ///
314    /// The pointer must be valid, aligned, and point to initialized memory
315    /// of the correct type, and be valid for lifetime `'mem`.
316    ///
317    /// It's encouraged to take the address of something with `&raw mut x`, rather than `&x`
318    pub const fn new<T>(ptr: *mut T) -> Self {
319        Self(
320            unsafe { NonNull::new_unchecked(ptr as *mut u8) },
321            PhantomData,
322        )
323    }
324
325    /// Gets the underlying raw pointer
326    pub const fn as_byte_ptr(self) -> *const u8 {
327        self.0.as_ptr()
328    }
329
330    /// Gets the underlying raw pointer as mutable
331    pub const fn as_mut_byte_ptr(self) -> *mut u8 {
332        self.0.as_ptr()
333    }
334
335    /// Gets the underlying raw pointer as a pointer of type T
336    ///
337    /// # Safety
338    ///
339    /// Must be called with the original type T that was used to create this pointer
340    pub const unsafe fn as_ptr<T>(self) -> *const T {
341        self.0.as_ptr() as *const T
342    }
343
344    /// Gets the underlying raw pointer as a mutable pointer of type T
345    ///
346    /// # Safety
347    ///
348    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
349    pub const unsafe fn as_mut<'borrow: 'mem, T>(self) -> &'borrow mut T {
350        unsafe { &mut *(self.0.as_ptr() as *mut T) }
351    }
352
353    /// Gets the underlying raw pointer as a const pointer of type T
354    ///
355    /// # Safety
356    ///
357    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
358    /// You must respect AXM (aliasing xor mutability). Holding onto the borrow while
359    /// calling as_mut is UB.
360    ///
361    /// Basically this is UB land. Careful.
362    pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
363        unsafe { &*(self.0.as_ptr() as *const T) }
364    }
365
366    /// Make a const ptr out of this mut ptr
367    pub const fn as_const<'borrow: 'mem>(self) -> PtrConst<'borrow> {
368        PtrConst(self.0, PhantomData)
369    }
370
371    /// Exposes [`core::ptr::read`]
372    ///
373    /// # Safety
374    ///
375    /// `T` must be the actual underlying type of the pointed-to memory.
376    /// The memory must be properly initialized and aligned for type `T`.
377    pub const unsafe fn read<T>(self) -> T {
378        unsafe { core::ptr::read(self.as_mut()) }
379    }
380
381    /// Exposes [`core::ptr::drop_in_place`]
382    ///
383    /// # Safety
384    ///
385    /// `T` must be the actual underlying type of the pointed-to memory.
386    /// The memory must be properly initialized and aligned for type `T`.
387    /// After calling this function, the memory should not be accessed again
388    /// until it is properly reinitialized.
389    pub unsafe fn drop_in_place<T>(self) -> PtrUninit<'mem> {
390        unsafe { core::ptr::drop_in_place(self.as_mut::<T>()) }
391        PtrUninit(self.0.as_ptr(), PhantomData)
392    }
393
394    /// Write a value to this location after dropping the existing value
395    ///
396    /// # Safety
397    ///
398    /// - The pointer must be properly aligned for T and point to allocated memory
399    ///   that can be safely written to.
400    /// - T must be the actual type of the object being pointed to
401    /// - The memory must already be initialized to a valid T value
402    pub unsafe fn replace<T>(self, value: T) -> Self {
403        unsafe { self.drop_in_place::<T>().put(value) }
404    }
405}