1
//! This is adapted from the `erasable` crate, but actually allows one to pass an `?Sized` type that stores its length inline. For example types implementing the `SliceDst` trait.
2

            
3
use std::marker::PhantomData;
4
use std::ptr::NonNull;
5

            
6
/// A thin, type-erased pointer. This should mimic the interface of NonNull, but
7
/// with the ability to erase the type information.
8
#[derive(Clone)]
9
pub struct Thin<T: ?Sized + Erasable> {
10
    ptr: ErasedPtr,
11
    marker: PhantomData<fn() -> T>,
12
}
13

            
14
impl<T: Erasable + Copy> Copy for Thin<T> {}
15

            
16
impl<T: ?Sized + Erasable> Thin<T> {
17
    pub fn new(ptr: NonNull<T>) -> Self {
18
        Self {
19
            ptr: T::erase(ptr),
20
            marker: PhantomData,
21
        }
22
    }
23
}
24

            
25
impl<T: ?Sized + Erasable> Thin<T> {
26
    pub fn as_ptr(&self) -> *mut T {
27
        unsafe { T::unerase(self.ptr) }.as_ptr()
28
    }
29

            
30
    pub fn as_nonnull(&self) -> NonNull<T> {
31
        unsafe { T::unerase(self.ptr) }
32
    }
33

            
34
    /// # Safety
35
    ///
36
    /// The caller must ensure that the underlying pointer is valid for reads.
37
    pub unsafe fn as_ref(&self) -> &T {
38
        unsafe { T::unerase(self.ptr).as_ref() }
39
    }
40
}
41

            
42
/// This is the trait that allows a type to be erased and unerased.
43
///
44
/// # Safety
45
///
46
/// See the documentation of the trait functions.
47
pub unsafe trait Erasable {
48
    /// Turn this erasable pointer into an erased pointer.
49
    ///
50
    /// To retrieve the original pointer, use `unerase`.
51
    ///
52
    /// # Safety
53
    ///
54
    /// The returned erased pointer must only be used with `unerase` for the same type.
55
    fn erase(this: NonNull<Self>) -> ErasedPtr;
56

            
57
    /// Unerase this erased pointer.
58
    ///
59
    /// # Safety
60
    ///
61
    /// The erased pointer must have been created by `erase`.
62
    unsafe fn unerase(this: ErasedPtr) -> NonNull<Self>;
63
}
64

            
65
unsafe impl<T: Sized> Erasable for T {
66
    fn erase(this: NonNull<Self>) -> ErasedPtr {
67
        // If the type is Sized, we can safely cast it to a pointer.
68
        this.cast::<Erased>().cast()
69
    }
70

            
71
    unsafe fn unerase(this: ErasedPtr) -> NonNull<Self> {
72
        // If the type is Sized, we can safely cast it back to a pointer.
73
        this.cast::<Self>()
74
    }
75
}
76

            
77
/// This is simply a u8, but with a concrete type to avoid confusion. Must be a
78
/// type that has size one and alignment one. Can be converted to an extern type
79
/// when `extern type` is stabilized.
80
pub struct Erased(#[allow(unused)] u8);
81

            
82
/// Static assertion to ensure that `ErasedPtr` is the same size as a `usize`.
83
const _: () = assert!(std::mem::size_of::<ErasedPtr>() == std::mem::size_of::<usize>());
84

            
85
/// A thin, type-erased pointer.
86
///
87
/// The `Erased` type is private, and should be treated as an opaque type.
88
/// When `extern type` is stabilized, `Erased` will be defined as one.
89
///
90
/// The current implementation uses a `struct Erased` with size 0 and align 1.
91
/// If you want to offset the pointer, make sure to cast to a `u8` or other known type pointer first.
92
/// When `Erased` becomes an extern type, it will properly have unknown size and align.
93
pub type ErasedPtr = NonNull<Erased>;