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}