facet_core/
ptr.rs

1//! Opaque pointers
2//!
3//! Type-erased pointer helpers for working with reflected values
4
5use core::{marker::PhantomData, mem::transmute, 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    #[inline]
23    pub unsafe fn copy_from<'src>(
24        self,
25        src: PtrConst<'src>,
26        shape: &'static Shape,
27    ) -> Result<PtrMut<'mem>, UnsizedError> {
28        let layout = shape.layout.sized_layout()?;
29        // SAFETY: The caller is responsible for upholding the invariants:
30        // - `src` must be valid for reads of `shape.size` bytes
31        // - `self` must be valid for writes of `shape.size` bytes and properly aligned
32        // - The regions may not overlap
33        unsafe {
34            core::ptr::copy_nonoverlapping(src.as_byte_ptr(), self.0, layout.size());
35            Ok(self.assume_init())
36        }
37    }
38
39    /// Create a new opaque pointer from a mutable pointer
40    ///
41    /// This is safe because it's generic over T
42    #[inline]
43    pub fn new<T>(ptr: *mut T) -> Self {
44        Self(ptr as *mut u8, PhantomData)
45    }
46
47    /// Creates a new opaque pointer from a reference to a [`core::mem::MaybeUninit`]
48    ///
49    /// The pointer will point to the potentially uninitialized contents
50    ///
51    /// This is safe because it's generic over T
52    #[inline]
53    pub fn from_maybe_uninit<T>(borrow: &'mem mut core::mem::MaybeUninit<T>) -> Self {
54        Self(borrow.as_mut_ptr() as *mut u8, PhantomData)
55    }
56
57    /// Assumes the pointer is initialized and returns an `Opaque` pointer
58    ///
59    /// # Safety
60    ///
61    /// The pointer must actually be pointing to initialized memory of the correct type.
62    #[inline]
63    pub unsafe fn assume_init(self) -> PtrMut<'mem> {
64        let ptr = unsafe { NonNull::new_unchecked(self.0) };
65        PtrMut(ptr, PhantomData)
66    }
67
68    /// Write a value to this location and convert to an initialized pointer
69    ///
70    /// # Safety
71    ///
72    /// The pointer must be properly aligned for T and point to allocated memory
73    /// that can be safely written to.
74    #[inline]
75    pub unsafe fn put<T>(self, value: T) -> PtrMut<'mem> {
76        unsafe {
77            core::ptr::write(self.0 as *mut T, value);
78            self.assume_init()
79        }
80    }
81
82    /// Returns the underlying raw pointer as a byte pointer
83    #[inline]
84    pub fn as_mut_byte_ptr(self) -> *mut u8 {
85        self.0
86    }
87
88    /// Returns the underlying raw pointer as a const byte pointer
89    #[inline]
90    pub fn as_byte_ptr(self) -> *const u8 {
91        self.0
92    }
93
94    /// Returns a pointer with the given offset added
95    ///
96    /// # Safety
97    ///
98    /// Offset is within the bounds of the allocated memory
99    pub unsafe fn field_uninit_at(self, offset: usize) -> PtrUninit<'mem> {
100        PtrUninit(unsafe { self.0.byte_add(offset) }, PhantomData)
101    }
102
103    /// Returns a pointer with the given offset added, assuming it's initialized
104    ///
105    /// # Safety
106    ///
107    /// The pointer plus offset must be:
108    /// - Within bounds of the allocated object
109    /// - Properly aligned for the type being pointed to
110    /// - Point to initialized data of the correct type
111    #[inline]
112    pub unsafe fn field_init_at(self, offset: usize) -> PtrMut<'mem> {
113        PtrMut(
114            unsafe { NonNull::new_unchecked(self.0.add(offset)) },
115            PhantomData,
116        )
117    }
118}
119
120impl<'mem, T> From<TypedPtrUninit<'mem, T>> for PtrUninit<'mem> {
121    fn from(ptr: TypedPtrUninit<'mem, T>) -> Self {
122        PtrUninit::new(ptr.0)
123    }
124}
125
126/// A pointer to an uninitialized value with a lifetime.
127#[derive(Debug)]
128#[repr(transparent)]
129pub struct TypedPtrUninit<'mem, T>(*mut T, PhantomData<&'mem mut ()>);
130
131impl<'mem, T> TypedPtrUninit<'mem, T> {
132    /// Create a new opaque pointer from a mutable pointer
133    ///
134    /// This is safe because it's generic over T
135    #[inline]
136    pub fn new(ptr: *mut T) -> Self {
137        Self(ptr, PhantomData)
138    }
139
140    /// Write a value to this location and convert to an initialized pointer
141    ///
142    /// # Safety
143    ///
144    /// The pointer must be properly aligned for T and point to allocated memory
145    /// that can be safely written to.
146    #[inline]
147    pub unsafe fn put(self, value: T) -> &'mem mut T {
148        unsafe {
149            core::ptr::write(self.0, value);
150            self.assume_init()
151        }
152    }
153    /// Assumes the pointer is initialized and returns an `Opaque` pointer
154    ///
155    /// # Safety
156    ///
157    /// The pointer must actually be pointing to initialized memory of the correct type.
158    #[inline]
159    pub unsafe fn assume_init(self) -> &'mem mut T {
160        unsafe { &mut *self.0 }
161    }
162
163    /// Returns a pointer with the given offset added
164    ///
165    /// # Safety
166    ///
167    /// Offset is within the bounds of the allocated memory and `U` is the correct type for the field.
168    #[inline]
169    pub unsafe fn field_uninit_at<U>(&mut self, offset: usize) -> TypedPtrUninit<'mem, U> {
170        TypedPtrUninit(unsafe { self.0.byte_add(offset).cast() }, PhantomData)
171    }
172}
173
174/// A type-erased read-only pointer to an initialized value.
175///
176/// Cannot be null. May be dangling (for ZSTs)
177#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
178#[repr(transparent)]
179pub struct PtrConst<'mem>(NonNull<u8>, PhantomData<&'mem ()>);
180
181unsafe impl Send for PtrConst<'_> {}
182unsafe impl Sync for PtrConst<'_> {}
183
184impl<'mem> PtrConst<'mem> {
185    /// Create a new opaque const pointer from a raw pointer
186    ///
187    /// # Safety
188    ///
189    /// The pointer must be non-null, 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 const x`, rather than `&x`
193    #[inline]
194    pub const fn new<T>(ptr: *const T) -> Self {
195        unsafe { Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData) }
196    }
197
198    /// Gets the underlying raw pointer as a byte pointer
199    #[inline]
200    pub const fn as_byte_ptr(self) -> *const u8 {
201        self.0.as_ptr()
202    }
203
204    /// Gets the underlying raw pointer as a pointer of type T
205    ///
206    /// # Safety
207    ///
208    /// Must be called with the original type T that was used to create this pointer
209    #[inline]
210    pub const unsafe fn as_ptr<T>(self) -> *const T {
211        if core::mem::size_of::<*const T>() == core::mem::size_of::<*const u8>() {
212            unsafe { core::mem::transmute_copy(&(self.0.as_ptr())) }
213        } else {
214            panic!("cannot!");
215        }
216    }
217
218    /// Gets the underlying raw pointer as a const pointer of type T
219    ///
220    /// # Safety
221    ///
222    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
223    #[inline]
224    pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
225        // TODO: rename to `get`, or something else? it's technically a borrow...
226        unsafe { &*(self.0.as_ptr() as *const T) }
227    }
228
229    /// Returns a pointer with the given offset added
230    ///
231    /// # Safety
232    ///
233    /// Offset must be within the bounds of the allocated memory,
234    /// and the resulting pointer must be properly aligned.
235    #[inline]
236    pub const unsafe fn field(self, offset: usize) -> PtrConst<'mem> {
237        PtrConst(
238            unsafe { NonNull::new_unchecked(self.0.as_ptr().byte_add(offset)) },
239            PhantomData,
240        )
241    }
242
243    /// Exposes [`core::ptr::read`]
244    ///
245    /// # Safety
246    ///
247    /// `T` must be the actual underlying type of the pointed-to memory.
248    /// The memory must be properly initialized and aligned for type `T`.
249    #[inline]
250    pub const unsafe fn read<T>(self) -> T {
251        unsafe { core::ptr::read(self.as_ptr()) }
252    }
253}
254
255/// A type-erased pointer to an initialized value
256#[derive(Clone, Copy)]
257#[repr(transparent)]
258pub struct PtrMut<'mem>(NonNull<u8>, PhantomData<&'mem mut ()>);
259
260impl<'mem> PtrMut<'mem> {
261    /// Create a new opaque pointer from a raw pointer
262    ///
263    /// # Safety
264    ///
265    /// The pointer must be valid, aligned, and point to initialized memory
266    /// of the correct type, and be valid for lifetime `'mem`.
267    ///
268    /// It's encouraged to take the address of something with `&raw mut x`, rather than `&x`
269    #[inline]
270    pub const fn new<T>(ptr: *mut T) -> Self {
271        Self(
272            unsafe { NonNull::new_unchecked(ptr as *mut u8) },
273            PhantomData,
274        )
275    }
276
277    /// Gets the underlying raw pointer
278    #[inline]
279    pub const fn as_byte_ptr(self) -> *const u8 {
280        self.0.as_ptr()
281    }
282
283    /// Gets the underlying raw pointer as mutable
284    #[inline]
285    pub const fn as_mut_byte_ptr(self) -> *mut u8 {
286        self.0.as_ptr()
287    }
288
289    /// Gets the underlying raw pointer as a pointer of type T
290    ///
291    /// # Safety
292    ///
293    /// Must be called with the original type T that was used to create this pointer
294    #[inline]
295    pub const unsafe fn as_ptr<T>(self) -> *const T {
296        self.0.as_ptr() as *const T
297    }
298
299    /// Gets the underlying raw pointer as a mutable pointer of type T
300    ///
301    /// # Safety
302    ///
303    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
304    #[inline]
305    pub const unsafe fn as_mut<'borrow: 'mem, T>(self) -> &'borrow mut T {
306        unsafe { &mut *(self.0.as_ptr() as *mut T) }
307    }
308
309    /// Gets the underlying raw pointer as a const pointer of type T
310    ///
311    /// # Safety
312    ///
313    /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
314    /// You must respect AXM (aliasing xor mutability). Holding onto the borrow while
315    /// calling as_mut is UB.
316    ///
317    /// Basically this is UB land. Careful.
318    #[inline]
319    pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
320        unsafe { &*(self.0.as_ptr() as *const T) }
321    }
322
323    /// Make a const ptr out of this mut ptr
324    #[inline]
325    pub const fn as_const<'borrow: 'mem>(self) -> PtrConst<'borrow> {
326        PtrConst(self.0, PhantomData)
327    }
328
329    /// Exposes [`core::ptr::read`]
330    ///
331    /// # Safety
332    ///
333    /// `T` must be the actual underlying type of the pointed-to memory.
334    /// The memory must be properly initialized and aligned for type `T`.
335    #[inline]
336    pub const unsafe fn read<T>(self) -> T {
337        unsafe { core::ptr::read(self.as_mut()) }
338    }
339
340    /// Exposes [`core::ptr::drop_in_place`]
341    ///
342    /// # Safety
343    ///
344    /// `T` must be the actual underlying type of the pointed-to memory.
345    /// The memory must be properly initialized and aligned for type `T`.
346    /// After calling this function, the memory should not be accessed again
347    /// until it is properly reinitialized.
348    #[inline]
349    pub unsafe fn drop_in_place<T>(self) -> PtrUninit<'mem> {
350        unsafe { core::ptr::drop_in_place(self.as_mut::<T>()) }
351        PtrUninit(self.0.as_ptr(), PhantomData)
352    }
353
354    /// Write a value to this location after dropping the existing value
355    ///
356    /// # Safety
357    ///
358    /// - The pointer must be properly aligned for T and point to allocated memory
359    ///   that can be safely written to.
360    /// - T must be the actual type of the object being pointed to
361    /// - The memory must already be initialized to a valid T value
362    #[inline]
363    pub unsafe fn replace<T>(self, value: T) -> Self {
364        unsafe { self.drop_in_place::<T>().put(value) }
365    }
366}
367
368#[derive(Clone, Copy)]
369#[repr(C)]
370/// Wide pointer (fat pointer) structure holding a data pointer and metadata (for unsized types).
371struct PtrWide {
372    ptr: NonNull<u8>,
373    metadata: usize,
374}
375
376impl PtrWide {
377    const fn from_ptr<T: ?Sized>(ptr: *mut T) -> Self {
378        if size_of_val(&ptr) != size_of::<Self>() {
379            panic!("Tried to construct a wide pointer from a thin pointer");
380        }
381        let ptr_ref = &ptr;
382        let self_ref = unsafe { transmute::<&*mut T, &Self>(ptr_ref) };
383        *self_ref
384    }
385
386    unsafe fn to_ptr<T: ?Sized>(self) -> *mut T {
387        if size_of::<*mut T>() != size_of::<Self>() {
388            panic!("Tried to get a wide pointer as a thin pointer");
389        }
390        let self_ref = &self;
391        let ptr_ref = unsafe { transmute::<&Self, &*mut T>(self_ref) };
392        *ptr_ref
393    }
394}
395
396/// A type-erased, wide pointer to an uninitialized value.
397///
398/// This can be useful for working with dynamically sized types, like slices or trait objects,
399/// where both a pointer and metadata (such as length or vtable) need to be stored.
400///
401/// The lifetime `'mem` represents the borrow of the underlying uninitialized memory.
402#[derive(Clone, Copy)]
403#[repr(transparent)]
404pub struct PtrUninitWide<'mem> {
405    ptr: PtrWide,
406    phantom: PhantomData<&'mem mut ()>,
407}
408
409/// A type-erased, read-only wide pointer to an initialized value.
410///
411/// Like [`PtrConst`], but for unsized types where metadata is needed. Cannot be null
412/// (but may be dangling for ZSTs). The lifetime `'mem` represents the borrow of the
413/// underlying memory, which must remain valid and initialized.
414#[derive(Clone, Copy)]
415#[repr(transparent)]
416pub struct PtrConstWide<'mem> {
417    ptr: PtrWide,
418    phantom: PhantomData<&'mem ()>,
419}
420
421impl<'mem> PtrConstWide<'mem> {
422    /// Creates a new wide const pointer from a raw pointer to a (potentially unsized) object.
423    ///
424    /// # Arguments
425    ///
426    /// * `ptr` - Raw pointer to the object. Can be a pointer to a DST (e.g., slice, trait object).
427    ///
428    /// # Panics
429    ///
430    /// Panics if a thin pointer is provided where a wide pointer is expected.
431    #[inline]
432    pub const fn new<T: ?Sized>(ptr: *const T) -> Self {
433        Self {
434            ptr: PtrWide::from_ptr(ptr.cast_mut()),
435            phantom: PhantomData,
436        }
437    }
438
439    /// Returns the underlying data pointer as a pointer to `u8` (the address of the object).
440    #[inline]
441    pub fn as_byte_ptr(self) -> *const u8 {
442        self.ptr.ptr.as_ptr()
443    }
444
445    /// Borrows the underlying object as a reference of type `T`.
446    ///
447    /// # Safety
448    ///
449    /// - `T` must be the actual underlying (potentially unsized) type of the pointed-to memory.
450    /// - The memory must remain valid and not be mutated while this reference exists.
451    /// - The pointer must be correctly aligned and point to a valid, initialized value for type `T`.
452    #[inline]
453    pub unsafe fn get<T: ?Sized>(self) -> &'mem T {
454        unsafe { self.ptr.to_ptr::<T>().as_ref().unwrap() }
455    }
456}
457
458/// A type-erased, mutable wide pointer to an initialized value.
459///
460/// Like [`PtrMut`], but for unsized types where metadata is needed. Provides mutable access
461/// to the underlying object, whose borrow is tracked by lifetime `'mem`.
462#[derive(Clone, Copy)]
463#[repr(transparent)]
464pub struct PtrMutWide<'mem> {
465    ptr: PtrWide,
466    phantom: PhantomData<&'mem mut ()>,
467}
468
469impl<'mem> PtrMutWide<'mem> {
470    /// Creates a new mutable wide pointer from a raw pointer to a (potentially unsized) object.
471    ///
472    /// # Arguments
473    ///
474    /// * `ptr` - Raw mutable pointer to the object. Can be a pointer to a DST (e.g., slice, trait object).
475    ///
476    /// # Panics
477    ///
478    /// Panics if a thin pointer is provided where a wide pointer is expected.
479    #[inline]
480    pub const fn new<T: ?Sized>(ptr: *mut T) -> Self {
481        Self {
482            ptr: PtrWide::from_ptr(ptr),
483            phantom: PhantomData,
484        }
485    }
486}
487
488/// A generic wrapper for either a thin or wide constant pointer.
489/// This enables working with both sized and unsized types using a single enum.
490#[derive(Clone, Copy)]
491pub enum GenericPtr<'mem> {
492    /// A thin pointer, used for sized types.
493    Thin(PtrConst<'mem>),
494    /// A wide pointer, used for unsized types such as slices and trait objects.
495    Wide(PtrConstWide<'mem>),
496}
497
498impl<'a> From<PtrConst<'a>> for GenericPtr<'a> {
499    fn from(value: PtrConst<'a>) -> Self {
500        GenericPtr::Thin(value)
501    }
502}
503
504impl<'a> From<PtrConstWide<'a>> for GenericPtr<'a> {
505    fn from(value: PtrConstWide<'a>) -> Self {
506        GenericPtr::Wide(value)
507    }
508}
509
510impl<'mem> GenericPtr<'mem> {
511    /// Returns the size of the pointer, which may be thin or wide.
512    #[inline(always)]
513    pub fn new<T: ?Sized>(ptr: *const T) -> Self {
514        if size_of_val(&ptr) == size_of::<PtrConst>() {
515            GenericPtr::Thin(PtrConst::new(ptr.cast::<()>()))
516        } else if size_of_val(&ptr) == size_of::<PtrConstWide>() {
517            GenericPtr::Wide(PtrConstWide::new(ptr))
518        } else {
519            panic!("Couldn't determine if pointer to T is thin or wide");
520        }
521    }
522
523    /// Returns the inner [`PtrConst`] if this is a thin pointer, or `None` if this is a wide pointer.
524    #[inline(always)]
525    pub fn thin(self) -> Option<PtrConst<'mem>> {
526        match self {
527            GenericPtr::Thin(ptr) => Some(ptr),
528            GenericPtr::Wide(_ptr) => None,
529        }
530    }
531
532    /// Returns the inner [`PtrConstWide`] if this is a wide pointer, or `None` if this is a thin pointer.
533    #[inline(always)]
534    pub fn wide(self) -> Option<PtrConstWide<'mem>> {
535        match self {
536            GenericPtr::Wide(ptr) => Some(ptr),
537            GenericPtr::Thin(_ptr) => None,
538        }
539    }
540
541    /// Downcasts this pointer into a reference — wide or not
542    ///
543    /// # Safety
544    ///
545    /// The pointer must be valid for reads for the given type `T`.
546    #[inline(always)]
547    pub unsafe fn get<T: ?Sized>(self) -> &'mem T {
548        match self {
549            GenericPtr::Thin(ptr) => {
550                let ptr = ptr.as_byte_ptr();
551                let ptr_ref = &ptr;
552
553                (unsafe { transmute::<&*const u8, &&T>(ptr_ref) }) as _
554            }
555            GenericPtr::Wide(ptr) => unsafe { ptr.get() },
556        }
557    }
558
559    /// Returns the inner pointer as a raw (possibly wide) `*const ()`.
560    ///
561    /// If this is a thin pointer, the returned value is
562    #[inline(always)]
563    pub fn as_byte_ptr(self) -> *const u8 {
564        match self {
565            GenericPtr::Thin(ptr) => ptr.as_byte_ptr(),
566            GenericPtr::Wide(ptr) => ptr.as_byte_ptr(),
567        }
568    }
569
570    /// Returns a pointer with the given offset added
571    ///
572    /// # Safety
573    ///
574    /// Offset must be within the bounds of the allocated memory,
575    /// and the resulting pointer must be properly aligned.
576    #[inline(always)]
577    pub unsafe fn field(self, offset: usize) -> GenericPtr<'mem> {
578        match self {
579            GenericPtr::Thin(ptr) => GenericPtr::Thin(unsafe { ptr.field(offset) }),
580            GenericPtr::Wide(_ptr) => {
581                // For wide pointers, we can't do field access safely without more context
582                // This is a limitation of the current design
583                panic!("Field access on wide pointers is not supported")
584            }
585        }
586    }
587}