use super::{FnPtrLike, Func};
use core::ffi::c_void;
use core::ptr::NonNull;
use core::{fmt, mem, ptr};
pub(super) type ErasedFnPtrPointee = c_void;
pub type RawErasedFnPtr = *mut ErasedFnPtrPointee;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ErasedFnPtr {
inner: NonNull<ErasedFnPtrPointee>,
}
unsafe impl Send for ErasedFnPtr {}
unsafe impl Sync for ErasedFnPtr {}
impl PartialEq<RawErasedFnPtr> for ErasedFnPtr {
fn eq(&self, &other: &RawErasedFnPtr) -> bool {
ptr::eq(self.inner.as_ptr(), other)
}
}
impl fmt::Debug for ErasedFnPtr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ErasedFnPtr").field(&self.inner).finish()
}
}
impl ErasedFnPtr {
#[inline]
#[must_use]
pub(super) const fn erase<F: Func>(f: F::Ptr) -> Self {
let inner =
unsafe { transmute_copy_layout_checked::<F::Ptr, NonNull<ErasedFnPtrPointee>>(f) };
Self { inner }
}
#[inline]
#[must_use]
pub(super) unsafe fn from_raw_never_null(raw: RawErasedFnPtr) -> Self {
let inner = unsafe { NonNull::new_unchecked(raw) };
Self { inner }
}
#[inline]
#[must_use]
pub(super) const fn raw(self) -> RawErasedFnPtr {
self.inner.as_ptr()
}
#[inline]
#[must_use]
pub(super) unsafe fn typed<F: FnPtrLike>(self) -> F {
unsafe { transmute_copy_layout_checked::<NonNull<ErasedFnPtrPointee>, F>(self.inner) }
}
}
const unsafe fn transmute_copy_layout_checked<Src: Copy, Dst: Copy>(src: Src) -> Dst {
const {
assert!(size_of::<Src>() == size_of::<Dst>());
assert!(align_of::<Src>() == align_of::<Dst>());
}
unsafe { mem::transmute_copy::<Src, Dst>(&src) }
}