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, 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 pub unsafe fn copy_from<'src>(
23 self,
24 src: PtrConst<'src>,
25 shape: &'static Shape,
26 ) -> Result<PtrMut<'mem>, UnsizedError> {
27 let layout = shape.layout.sized_layout()?;
28 // SAFETY: The caller is responsible for upholding the invariants:
29 // - `src` must be valid for reads of `shape.size` bytes
30 // - `self` must be valid for writes of `shape.size` bytes and properly aligned
31 // - The regions may not overlap
32 unsafe {
33 core::ptr::copy_nonoverlapping(src.as_byte_ptr(), self.0, layout.size());
34 Ok(self.assume_init())
35 }
36 }
37
38 /// Create a new opaque pointer from a mutable pointer
39 ///
40 /// This is safe because it's generic over T
41 pub fn new<T>(ptr: *mut T) -> Self {
42 Self(ptr as *mut u8, PhantomData)
43 }
44
45 /// Creates a new opaque pointer from a reference to a [`core::mem::MaybeUninit`]
46 ///
47 /// The pointer will point to the potentially uninitialized contents
48 ///
49 /// This is safe because it's generic over T
50 pub fn from_maybe_uninit<T>(borrow: &'mem mut core::mem::MaybeUninit<T>) -> Self {
51 Self(borrow.as_mut_ptr() as *mut u8, PhantomData)
52 }
53
54 /// Assumes the pointer is initialized and returns an `Opaque` pointer
55 ///
56 /// # Safety
57 ///
58 /// The pointer must actually be pointing to initialized memory of the correct type.
59 pub unsafe fn assume_init(self) -> PtrMut<'mem> {
60 let ptr = unsafe { NonNull::new_unchecked(self.0) };
61 PtrMut(ptr, PhantomData)
62 }
63
64 /// Write a value to this location and convert to an initialized pointer
65 ///
66 /// # Safety
67 ///
68 /// The pointer must be properly aligned for T and point to allocated memory
69 /// that can be safely written to.
70 pub unsafe fn put<T>(self, value: T) -> PtrMut<'mem> {
71 unsafe {
72 core::ptr::write(self.0 as *mut T, value);
73 self.assume_init()
74 }
75 }
76
77 /// Returns the underlying raw pointer as a byte pointer
78 pub fn as_mut_byte_ptr(self) -> *mut u8 {
79 self.0
80 }
81
82 /// Returns the underlying raw pointer as a const byte pointer
83 pub fn as_byte_ptr(self) -> *const u8 {
84 self.0
85 }
86
87 /// Returns a pointer with the given offset added
88 ///
89 /// # Safety
90 ///
91 /// Offset is within the bounds of the allocated memory
92 pub unsafe fn field_uninit_at(self, offset: usize) -> PtrUninit<'mem> {
93 PtrUninit(unsafe { self.0.byte_add(offset) }, PhantomData)
94 }
95
96 /// Returns a pointer with the given offset added, assuming it's initialized
97 ///
98 /// # Safety
99 ///
100 /// The pointer plus offset must be:
101 /// - Within bounds of the allocated object
102 /// - Properly aligned for the type being pointed to
103 /// - Point to initialized data of the correct type
104 pub unsafe fn field_init_at(self, offset: usize) -> PtrMut<'mem> {
105 PtrMut(
106 unsafe { NonNull::new_unchecked(self.0.add(offset)) },
107 PhantomData,
108 )
109 }
110}
111
112impl<'mem, T> From<TypedPtrUninit<'mem, T>> for PtrUninit<'mem> {
113 fn from(ptr: TypedPtrUninit<'mem, T>) -> Self {
114 PtrUninit::new(ptr.0)
115 }
116}
117
118/// A pointer to an uninitialized value with a lifetime.
119#[derive(Debug)]
120#[repr(transparent)]
121pub struct TypedPtrUninit<'mem, T>(*mut T, PhantomData<&'mem mut ()>);
122
123impl<'mem, T> TypedPtrUninit<'mem, T> {
124 /// Create a new opaque pointer from a mutable pointer
125 ///
126 /// This is safe because it's generic over T
127 pub fn new(ptr: *mut T) -> Self {
128 Self(ptr, PhantomData)
129 }
130
131 /// Write a value to this location and convert to an initialized pointer
132 ///
133 /// # Safety
134 ///
135 /// The pointer must be properly aligned for T and point to allocated memory
136 /// that can be safely written to.
137 pub unsafe fn put(self, value: T) -> &'mem mut T {
138 unsafe {
139 core::ptr::write(self.0, value);
140 self.assume_init()
141 }
142 }
143 /// Assumes the pointer is initialized and returns an `Opaque` pointer
144 ///
145 /// # Safety
146 ///
147 /// The pointer must actually be pointing to initialized memory of the correct type.
148 pub unsafe fn assume_init(self) -> &'mem mut T {
149 unsafe { &mut *self.0 }
150 }
151
152 /// Returns a pointer with the given offset added
153 ///
154 /// # Safety
155 ///
156 /// Offset is within the bounds of the allocated memory and `U` is the correct type for the field.
157 pub unsafe fn field_uninit_at<U>(&mut self, offset: usize) -> TypedPtrUninit<'mem, U> {
158 TypedPtrUninit(unsafe { self.0.byte_add(offset).cast() }, PhantomData)
159 }
160}
161// /// Copies memory from a source pointer into this location and returns PtrMut
162// ///
163// /// # Safety
164// ///
165// /// - The source pointer must be valid for reads of `len` bytes
166// /// - This pointer must be valid for writes of `len` bytes and properly aligned
167// /// - The regions may not overlap
168// pub unsafe fn copy_from<'src>(
169// self,
170// src: PtrConst<'src>,
171// shape: &'static Shape,
172// ) -> Result<PtrMut<'mem>, UnsizedError> {
173// let layout = shape.layout.sized_layout()?;
174// // SAFETY: The caller is responsible for upholding the invariants:
175// // - `src` must be valid for reads of `shape.size` bytes
176// // - `self` must be valid for writes of `shape.size` bytes and properly aligned
177// // - The regions may not overlap
178// unsafe {
179// core::ptr::copy_nonoverlapping(src.as_byte_ptr(), self.0, layout.size());
180// Ok(self.assume_init())
181// }
182// }
183
184// /// Creates a new opaque pointer from a reference to a [`core::mem::MaybeUninit`]
185// ///
186// /// The pointer will point to the potentially uninitialized contents
187// ///
188// /// This is safe because it's generic over T
189// pub fn from_maybe_uninit<T>(borrow: &'mem mut core::mem::MaybeUninit<T>) -> Self {
190// Self(borrow.as_mut_ptr() as *mut u8, PhantomData)
191// }
192
193// /// Assumes the pointer is initialized and returns an `Opaque` pointer
194// ///
195// /// # Safety
196// ///
197// /// The pointer must actually be pointing to initialized memory of the correct type.
198// pub unsafe fn assume_init(self) -> PtrMut<'mem> {
199// let ptr = unsafe { NonNull::new_unchecked(self.0) };
200// PtrMut(ptr, PhantomData)
201// }
202
203// /// Returns the underlying raw pointer as a byte pointer
204// pub fn as_mut_byte_ptr(self) -> *mut u8 {
205// self.0
206// }
207
208// /// Returns the underlying raw pointer as a const byte pointer
209// pub fn as_byte_ptr(self) -> *const u8 {
210// self.0
211// }
212
213// /// Returns a pointer with the given offset added, assuming it's initialized
214// ///
215// /// # Safety
216// ///
217// /// The pointer plus offset must be:
218// /// - Within bounds of the allocated object
219// /// - Properly aligned for the type being pointed to
220// /// - Point to initialized data of the correct type
221// pub unsafe fn field_init_at(self, offset: usize) -> PtrMut<'mem> {
222// PtrMut(
223// unsafe { NonNull::new_unchecked(self.0.add(offset)) },
224// PhantomData,
225// )
226// }
227// }
228
229/// A type-erased read-only pointer to an initialized value.
230///
231/// Cannot be null. May be dangling (for ZSTs)
232#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
233#[repr(transparent)]
234pub struct PtrConst<'mem>(NonNull<u8>, PhantomData<&'mem ()>);
235
236unsafe impl Send for PtrConst<'_> {}
237unsafe impl Sync for PtrConst<'_> {}
238
239impl<'mem> PtrConst<'mem> {
240 /// Create a new opaque const pointer from a raw pointer
241 ///
242 /// # Safety
243 ///
244 /// The pointer must be non-null, valid, aligned, and point to initialized memory
245 /// of the correct type, and be valid for lifetime `'mem`.
246 ///
247 /// It's encouraged to take the address of something with `&raw const x`, rather than `&x`
248 pub const fn new<T>(ptr: *const T) -> Self {
249 unsafe { Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData) }
250 }
251
252 /// Gets the underlying raw pointer as a byte pointer
253 pub const fn as_byte_ptr(self) -> *const u8 {
254 self.0.as_ptr()
255 }
256
257 /// Gets the underlying raw pointer as a pointer of type T
258 ///
259 /// # Safety
260 ///
261 /// Must be called with the original type T that was used to create this pointer
262 pub const unsafe fn as_ptr<T>(self) -> *const T {
263 self.0.as_ptr() as *const T
264 }
265
266 /// Gets the underlying raw pointer as a const pointer of type T
267 ///
268 /// # Safety
269 ///
270 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
271 pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
272 // TODO: rename to `get`, or something else? it's technically a borrow...
273 unsafe { &*(self.0.as_ptr() as *const T) }
274 }
275
276 /// Returns a pointer with the given offset added
277 ///
278 /// # Safety
279 ///
280 /// Offset must be within the bounds of the allocated memory,
281 /// and the resulting pointer must be properly aligned.
282 pub const unsafe fn field(self, offset: usize) -> PtrConst<'mem> {
283 PtrConst(
284 unsafe { NonNull::new_unchecked(self.0.as_ptr().byte_add(offset)) },
285 PhantomData,
286 )
287 }
288
289 /// Exposes [`core::ptr::read`]
290 ///
291 /// # Safety
292 ///
293 /// `T` must be the actual underlying type of the pointed-to memory.
294 /// The memory must be properly initialized and aligned for type `T`.
295 pub const unsafe fn read<T>(self) -> T {
296 unsafe { core::ptr::read(self.as_ptr()) }
297 }
298}
299
300/// A type-erased pointer to an initialized value
301#[derive(Clone, Copy)]
302#[repr(transparent)]
303pub struct PtrMut<'mem>(NonNull<u8>, PhantomData<&'mem mut ()>);
304
305impl<'mem> PtrMut<'mem> {
306 /// Create a new opaque pointer from a raw pointer
307 ///
308 /// # Safety
309 ///
310 /// The pointer must be valid, aligned, and point to initialized memory
311 /// of the correct type, and be valid for lifetime `'mem`.
312 ///
313 /// It's encouraged to take the address of something with `&raw mut x`, rather than `&x`
314 pub const fn new<T>(ptr: *mut T) -> Self {
315 Self(
316 unsafe { NonNull::new_unchecked(ptr as *mut u8) },
317 PhantomData,
318 )
319 }
320
321 /// Gets the underlying raw pointer
322 pub const fn as_byte_ptr(self) -> *const u8 {
323 self.0.as_ptr()
324 }
325
326 /// Gets the underlying raw pointer as mutable
327 pub const fn as_mut_byte_ptr(self) -> *mut u8 {
328 self.0.as_ptr()
329 }
330
331 /// Gets the underlying raw pointer as a pointer of type T
332 ///
333 /// # Safety
334 ///
335 /// Must be called with the original type T that was used to create this pointer
336 pub const unsafe fn as_ptr<T>(self) -> *const T {
337 self.0.as_ptr() as *const T
338 }
339
340 /// Gets the underlying raw pointer as a mutable pointer of type T
341 ///
342 /// # Safety
343 ///
344 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
345 pub const unsafe fn as_mut<'borrow: 'mem, T>(self) -> &'borrow mut T {
346 unsafe { &mut *(self.0.as_ptr() as *mut T) }
347 }
348
349 /// Gets the underlying raw pointer as a const pointer of type T
350 ///
351 /// # Safety
352 ///
353 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
354 /// You must respect AXM (aliasing xor mutability). Holding onto the borrow while
355 /// calling as_mut is UB.
356 ///
357 /// Basically this is UB land. Careful.
358 pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
359 unsafe { &*(self.0.as_ptr() as *const T) }
360 }
361
362 /// Make a const ptr out of this mut ptr
363 pub const fn as_const<'borrow: 'mem>(self) -> PtrConst<'borrow> {
364 PtrConst(self.0, PhantomData)
365 }
366
367 /// Exposes [`core::ptr::read`]
368 ///
369 /// # Safety
370 ///
371 /// `T` must be the actual underlying type of the pointed-to memory.
372 /// The memory must be properly initialized and aligned for type `T`.
373 pub const unsafe fn read<T>(self) -> T {
374 unsafe { core::ptr::read(self.as_mut()) }
375 }
376
377 /// Exposes [`core::ptr::drop_in_place`]
378 ///
379 /// # Safety
380 ///
381 /// `T` must be the actual underlying type of the pointed-to memory.
382 /// The memory must be properly initialized and aligned for type `T`.
383 /// After calling this function, the memory should not be accessed again
384 /// until it is properly reinitialized.
385 pub unsafe fn drop_in_place<T>(self) -> PtrUninit<'mem> {
386 unsafe { core::ptr::drop_in_place(self.as_mut::<T>()) }
387 PtrUninit(self.0.as_ptr(), PhantomData)
388 }
389
390 /// Write a value to this location after dropping the existing value
391 ///
392 /// # Safety
393 ///
394 /// - The pointer must be properly aligned for T and point to allocated memory
395 /// that can be safely written to.
396 /// - T must be the actual type of the object being pointed to
397 /// - The memory must already be initialized to a valid T value
398 pub unsafe fn replace<T>(self, value: T) -> Self {
399 unsafe { self.drop_in_place::<T>().put(value) }
400 }
401}