chaud_hot/func/ptr.rs
1use super::{FnPtrLike, Func};
2use core::ffi::c_void;
3use core::ptr::NonNull;
4use core::{fmt, mem, ptr};
5
6pub(super) type ErasedFnPtrPointee = c_void;
7
8pub type RawErasedFnPtr = *mut ErasedFnPtrPointee;
9
10#[derive(Copy, Clone, PartialEq, Eq, Hash)]
11#[repr(transparent)]
12pub struct ErasedFnPtr {
13 /// # Safety
14 ///
15 /// See the [module][super#safety] docs:
16 ///
17 /// * Must never change.
18 /// * The actual type must be a function pointer implementing [`FnPtrLike`].
19 inner: NonNull<ErasedFnPtrPointee>,
20}
21
22// SAFETY: The actual type must imlement `FnPtrLike`, which requires `Send`.
23unsafe impl Send for ErasedFnPtr {}
24
25// SAFETY: `ErasedFnPtr` is send and does not allow mutating access.
26unsafe impl Sync for ErasedFnPtr {}
27
28impl PartialEq<RawErasedFnPtr> for ErasedFnPtr {
29 fn eq(&self, &other: &RawErasedFnPtr) -> bool {
30 ptr::eq(self.inner.as_ptr(), other)
31 }
32}
33
34impl fmt::Debug for ErasedFnPtr {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
36 f.debug_tuple("ErasedFnPtr").field(&self.inner).finish()
37 }
38}
39
40impl ErasedFnPtr {
41 #[inline]
42 #[must_use]
43 pub(super) const fn erase<F: Func>(f: F::Ptr) -> Self {
44 // SAFETY: `F::Ptr` is a function pointer (and thus non-null). Aside
45 // from that, transmutes from function pointers to pointers are valid.
46 let inner =
47 unsafe { transmute_copy_layout_checked::<F::Ptr, NonNull<ErasedFnPtrPointee>>(f) };
48
49 // SAFETY: Initializing does not count as a change, and the actual type
50 // requirements are enforced or need to be upheld by the caller.
51 Self { inner }
52 }
53
54 /// # Safety
55 ///
56 /// The passed argument must be a function pointer implementing
57 /// [`FnPtrLike`] (and thus non-null).
58 #[inline]
59 #[must_use]
60 pub(super) unsafe fn from_raw_never_null(raw: RawErasedFnPtr) -> Self {
61 // SAFETY: The caller must ensure that `raw` is non-null.
62 let inner = unsafe { NonNull::new_unchecked(raw) };
63
64 // SAFETY: Initializing does not count as a change, and the actual type
65 // requirements need to be upheld by the caller.
66 Self { inner }
67 }
68
69 #[inline]
70 #[must_use]
71 pub(super) const fn raw(self) -> RawErasedFnPtr {
72 // SAFETY: `self` / `inner` are consumed by value, so `inner` does not
73 // change.
74 self.inner.as_ptr()
75 }
76
77 /// # Safety
78 ///
79 /// `F` must be the actual type of `self`.
80 #[inline]
81 #[must_use]
82 pub(super) unsafe fn typed<F: FnPtrLike>(self) -> F {
83 // SAFETY: The caller must ensure that `F` is the actual type of `self`,
84 // thus transmuting the pointer back to a F (which must be a function
85 // pointer) is valid.
86 unsafe { transmute_copy_layout_checked::<NonNull<ErasedFnPtrPointee>, F>(self.inner) }
87 }
88}
89
90/// A wrapper around [`transmute_copy`][mem::transmute_copy] that:
91///
92/// * Only works on [`Copy`] types.
93/// * Enforces that `Src` and `Dst` have the same size and alignment.
94///
95/// # Safety
96///
97/// See [`transmute_copy`][mem::transmute_copy].
98const unsafe fn transmute_copy_layout_checked<Src: Copy, Dst: Copy>(src: Src) -> Dst {
99 const {
100 assert!(size_of::<Src>() == size_of::<Dst>());
101 assert!(align_of::<Src>() == align_of::<Dst>());
102 }
103 // SAFETY: Must be upheld by the caller.
104 unsafe { mem::transmute_copy::<Src, Dst>(&src) }
105}