use std::{
fmt,
mem::{size_of, MaybeUninit},
ptr::{addr_of, addr_of_mut, copy_nonoverlapping, null, NonNull},
};
#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct Erased([*const u8; 2]);
unsafe impl Send for Erased {}
unsafe impl Sync for Erased {}
impl Erased {
pub fn new<T: ?Sized>(reference: NonNull<T>) -> Erased {
let mut ptr = Erased([null(); 2]);
let ptr_size = size_of::<NonNull<T>>();
assert!(
ptr_size <= size_of::<Erased>(),
"pointers to T are too big for storage"
);
unsafe {
copy_nonoverlapping(
addr_of!(reference).cast::<u8>(),
addr_of_mut!(ptr.0).cast::<u8>(),
ptr_size,
);
}
ptr
}
pub unsafe fn specify<T: ?Sized>(self) -> NonNull<T> {
let mut box_ref: MaybeUninit<NonNull<T>> = MaybeUninit::zeroed();
copy_nonoverlapping(
addr_of!(self.0).cast::<u8>(),
addr_of_mut!(box_ref).cast::<u8>(),
size_of::<NonNull<T>>(),
);
box_ref.assume_init()
}
}
impl fmt::Debug for Erased {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ErasedPtr({:x?})", self.0)
}
}
pub(crate) struct Nullable<T: ?Sized>(*mut T);
impl<T: ?Sized> Nullable<T> {
pub fn new(ptr: NonNull<T>) -> Nullable<T> {
Nullable(ptr.as_ptr())
}
pub fn as_null(self) -> Nullable<T> {
Nullable(self.0.with_addr(0))
}
pub fn is_null(self) -> bool {
self.as_option().is_none()
}
pub fn as_option(self) -> Option<NonNull<T>> {
NonNull::new(self.0)
}
pub fn as_ptr(self) -> *mut T {
self.0
}
pub fn from_ptr(ptr: *mut T) -> Self {
Self(ptr)
}
pub fn expect(self, msg: &str) -> NonNull<T> {
self.as_option().expect(msg)
}
pub fn unwrap(self) -> NonNull<T> {
self.as_option().unwrap()
}
pub unsafe fn unwrap_unchecked(self) -> NonNull<T> {
self.as_option().unwrap_unchecked()
}
}
impl<T: ?Sized> Clone for Nullable<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for Nullable<T> {}
#[cfg(feature = "coerce-unsized")]
impl<T, U> std::ops::CoerceUnsized<Nullable<U>> for Nullable<T>
where
T: std::marker::Unsize<U> + ?Sized,
U: ?Sized,
{
}
impl<T: ?Sized> fmt::Debug for Nullable<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Nullable({:x?})", self.0)
}
}
#[cfg(test)]
mod tests {
use core::any::Any;
use std::alloc::{dealloc, Layout};
use super::*;
#[test]
fn erased_alloc() {
let orig_ptr: &mut u8 = Box::leak(Box::new(7));
let erased_ptr = Erased::new(NonNull::from(orig_ptr));
unsafe {
let remade_ptr = erased_ptr.specify::<u8>();
assert_eq!(*remade_ptr.as_ref(), 7);
dealloc(remade_ptr.as_ptr(), Layout::for_value(remade_ptr.as_ref()));
}
}
#[test]
fn erased_alloc_slice() {
let orig_ptr: &mut [u8] = Box::leak(Box::new([7, 8, 9]));
let erased_ptr = Erased::new(NonNull::from(orig_ptr));
unsafe {
let remade_ptr = erased_ptr.specify::<[u8]>();
assert_eq!(remade_ptr.as_ref(), [7, 8, 9]);
dealloc(
remade_ptr.as_ptr().cast(),
Layout::for_value(remade_ptr.as_ref()),
);
}
}
#[test]
fn erased_alloc_dyn() {
let orig_ptr: &mut dyn Any = Box::leak(Box::new(7u8));
let erased_ptr = Erased::new(NonNull::from(orig_ptr));
unsafe {
let remade_ptr = erased_ptr.specify::<dyn Any>();
assert_eq!(*remade_ptr.as_ref().downcast_ref::<u8>().unwrap(), 7);
dealloc(
remade_ptr.as_ptr().cast(),
Layout::for_value(remade_ptr.as_ref()),
);
}
}
}