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}