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, 'shape>(
23 self,
24 src: PtrConst<'src>,
25 shape: &'shape Shape<'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 if core::mem::size_of::<*const T>() == core::mem::size_of::<*const u8>() {
264 unsafe { core::mem::transmute_copy(&(self.0.as_ptr())) }
265 } else {
266 panic!("cannot!");
267 }
268 }
269
270 /// Gets the underlying raw pointer as a const pointer of type T
271 ///
272 /// # Safety
273 ///
274 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
275 pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
276 // TODO: rename to `get`, or something else? it's technically a borrow...
277 unsafe { &*(self.0.as_ptr() as *const T) }
278 }
279
280 /// Returns a pointer with the given offset added
281 ///
282 /// # Safety
283 ///
284 /// Offset must be within the bounds of the allocated memory,
285 /// and the resulting pointer must be properly aligned.
286 pub const unsafe fn field(self, offset: usize) -> PtrConst<'mem> {
287 PtrConst(
288 unsafe { NonNull::new_unchecked(self.0.as_ptr().byte_add(offset)) },
289 PhantomData,
290 )
291 }
292
293 /// Exposes [`core::ptr::read`]
294 ///
295 /// # Safety
296 ///
297 /// `T` must be the actual underlying type of the pointed-to memory.
298 /// The memory must be properly initialized and aligned for type `T`.
299 pub const unsafe fn read<T>(self) -> T {
300 unsafe { core::ptr::read(self.as_ptr()) }
301 }
302}
303
304/// A type-erased pointer to an initialized value
305#[derive(Clone, Copy)]
306#[repr(transparent)]
307pub struct PtrMut<'mem>(NonNull<u8>, PhantomData<&'mem mut ()>);
308
309impl<'mem> PtrMut<'mem> {
310 /// Create a new opaque pointer from a raw pointer
311 ///
312 /// # Safety
313 ///
314 /// The pointer must be valid, aligned, and point to initialized memory
315 /// of the correct type, and be valid for lifetime `'mem`.
316 ///
317 /// It's encouraged to take the address of something with `&raw mut x`, rather than `&x`
318 pub const fn new<T>(ptr: *mut T) -> Self {
319 Self(
320 unsafe { NonNull::new_unchecked(ptr as *mut u8) },
321 PhantomData,
322 )
323 }
324
325 /// Gets the underlying raw pointer
326 pub const fn as_byte_ptr(self) -> *const u8 {
327 self.0.as_ptr()
328 }
329
330 /// Gets the underlying raw pointer as mutable
331 pub const fn as_mut_byte_ptr(self) -> *mut u8 {
332 self.0.as_ptr()
333 }
334
335 /// Gets the underlying raw pointer as a pointer of type T
336 ///
337 /// # Safety
338 ///
339 /// Must be called with the original type T that was used to create this pointer
340 pub const unsafe fn as_ptr<T>(self) -> *const T {
341 self.0.as_ptr() as *const T
342 }
343
344 /// Gets the underlying raw pointer as a mutable pointer of type T
345 ///
346 /// # Safety
347 ///
348 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
349 pub const unsafe fn as_mut<'borrow: 'mem, T>(self) -> &'borrow mut T {
350 unsafe { &mut *(self.0.as_ptr() as *mut T) }
351 }
352
353 /// Gets the underlying raw pointer as a const pointer of type T
354 ///
355 /// # Safety
356 ///
357 /// `T` must be the _actual_ underlying type. You're downcasting with no guardrails.
358 /// You must respect AXM (aliasing xor mutability). Holding onto the borrow while
359 /// calling as_mut is UB.
360 ///
361 /// Basically this is UB land. Careful.
362 pub const unsafe fn get<'borrow: 'mem, T>(self) -> &'borrow T {
363 unsafe { &*(self.0.as_ptr() as *const T) }
364 }
365
366 /// Make a const ptr out of this mut ptr
367 pub const fn as_const<'borrow: 'mem>(self) -> PtrConst<'borrow> {
368 PtrConst(self.0, PhantomData)
369 }
370
371 /// Exposes [`core::ptr::read`]
372 ///
373 /// # Safety
374 ///
375 /// `T` must be the actual underlying type of the pointed-to memory.
376 /// The memory must be properly initialized and aligned for type `T`.
377 pub const unsafe fn read<T>(self) -> T {
378 unsafe { core::ptr::read(self.as_mut()) }
379 }
380
381 /// Exposes [`core::ptr::drop_in_place`]
382 ///
383 /// # Safety
384 ///
385 /// `T` must be the actual underlying type of the pointed-to memory.
386 /// The memory must be properly initialized and aligned for type `T`.
387 /// After calling this function, the memory should not be accessed again
388 /// until it is properly reinitialized.
389 pub unsafe fn drop_in_place<T>(self) -> PtrUninit<'mem> {
390 unsafe { core::ptr::drop_in_place(self.as_mut::<T>()) }
391 PtrUninit(self.0.as_ptr(), PhantomData)
392 }
393
394 /// Write a value to this location after dropping the existing value
395 ///
396 /// # Safety
397 ///
398 /// - The pointer must be properly aligned for T and point to allocated memory
399 /// that can be safely written to.
400 /// - T must be the actual type of the object being pointed to
401 /// - The memory must already be initialized to a valid T value
402 pub unsafe fn replace<T>(self, value: T) -> Self {
403 unsafe { self.drop_in_place::<T>().put(value) }
404 }
405}