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
pub struct Thin<T: ?Sized + Erasable> {
9
    ptr: ErasedPtr,
10
    marker: PhantomData<fn() -> T>,
11
}
12

            
13
// A `Thin` is just a type-erased pointer plus a `PhantomData`, so it is always
14
// `Copy`/`Clone` regardless of whether the pointee is.
15
impl<T: ?Sized + Erasable> Clone for Thin<T> {
16
    fn clone(&self) -> Self {
17
        *self
18
    }
19
}
20

            
21
impl<T: ?Sized + Erasable> Copy for Thin<T> {}
22

            
23
impl<T: ?Sized + Erasable> Thin<T> {
24
    pub fn new(ptr: NonNull<T>) -> Self {
25
        Self {
26
            ptr: T::erase(ptr),
27
            marker: PhantomData,
28
        }
29
    }
30
}
31

            
32
impl<T: ?Sized + Erasable> Thin<T> {
33
    pub fn as_ptr(&self) -> *mut T {
34
        unsafe { T::unerase(self.ptr) }.as_ptr()
35
    }
36

            
37
    pub fn as_nonnull(&self) -> NonNull<T> {
38
        unsafe { T::unerase(self.ptr) }
39
    }
40

            
41
    /// # Safety
42
    ///
43
    /// The caller must ensure that the underlying pointer is valid for reads.
44
    pub unsafe fn as_ref(&self) -> &T {
45
        unsafe { T::unerase(self.ptr).as_ref() }
46
    }
47
}
48

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

            
64
    /// Unerase this erased pointer.
65
    ///
66
    /// # Safety
67
    ///
68
    /// The erased pointer must have been created by `erase`.
69
    unsafe fn unerase(this: ErasedPtr) -> NonNull<Self>;
70
}
71

            
72
unsafe impl<T: Sized> Erasable for T {
73
    fn erase(this: NonNull<Self>) -> ErasedPtr {
74
        // If the type is Sized, we can safely cast it to a pointer.
75
        this.cast::<Erased>().cast()
76
    }
77

            
78
    unsafe fn unerase(this: ErasedPtr) -> NonNull<Self> {
79
        // If the type is Sized, we can safely cast it back to a pointer.
80
        this.cast::<Self>()
81
    }
82
}
83

            
84
/// A stand-in for an opaque pointee with size one and alignment one (a single
85
/// `u8`). Can be replaced by an `extern type` when that is stabilized.
86
pub struct Erased(#[allow(unused)] u8);
87

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

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