Skip to main content

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}