facet_core/
ptr.rs

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