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}