devela/sys/mem/ptr/
fat.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// devela::sys::mem::ptr::fat
//
//! Fat pointers.
//
// https://github.com/storycraft/unsized-stack
// Vendored from [unsized-stack](https://crates.io/crates/unsized-stack/0.2.0)
//
// MODIFICATIONS:
// - refactored, documented, namespaced.

// WAIT: [ptr_metadata](https://github.com/rust-lang/rust/issues/81513)
// use core::ptr::{metadata, from_raw_parts};

/// Represents a fat pointer with separate data and metadata pointers.
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct FatPtr {
    ptr: *const (),
    metadata: *const (),
}

impl FatPtr {
    /// Creates a new `FatPtr` from a data pointer and metadata.
    pub const fn new<T>(ptr: *const T, metadata: *const ()) -> Self {
        Self { ptr: ptr as _, metadata }
    }

    /// Returns the raw pointer part of the fat pointer.
    pub const fn ptr(&self) -> *const () {
        self.ptr
    }

    /// Returns the metadata part of the fat pointer.
    pub const fn metadata(&self) -> *const () {
        self.metadata
    }

    /* */

    /// Checks if a type `T` is a valid dynamically sized type (DST).
    // MAYBE make private
    pub const fn check_valid<T: ?Sized>() {
        if size_of::<*const T>() != size_of::<FatPtr>() {
            panic!("Type is not valid DST");
        }
    }

    /// Composes a fat pointer `*const T` from a `FatPtr` structure.
    pub const fn to_raw_ptr<T: ?Sized>(fat_ptr: FatPtr) -> *const T {
        Self::check_valid::<T>();

        // SAFETY: relying on unspecified fat pointer representation
        unsafe { FatPtrRepr { fat_ptr }.ptr_const }
    }
    /// Decomposes a fat pointer `*const T` into a `FatPtr` structure.
    pub const fn from_raw_ptr<T: ?Sized>(fat_ptr: *const T) -> FatPtr {
        Self::check_valid::<T>();

        // SAFETY: relying on unspecified fat pointer representation
        unsafe { FatPtrRepr { ptr_const: fat_ptr }.fat_ptr }
    }

    // WAIT: [ptr_metadata](https://github.com/rust-lang/rust/issues/81513)
    // /// Composes a fat pointer `*const T` from a `FatPtr` using stable APIs.
    // pub const fn compose2<T: ?Sized>(fat_ptr: FatPtr) -> *const T {
    //     unsafe { from_raw_parts(fat_ptr.ptr, fat_ptr.metadata as _) }
    // }
    // /// Decomposes a fat pointer `*const T` into a `FatPtr` using stable APIs.
    // pub const fn decompose2<T: ?Sized>(ptr: *const T) -> FatPtr {
    //     FatPtr {
    //         ptr: ptr as *const (),
    //         metadata: metadata(ptr) as *const (),
    //     }
    // }
}

/// A union that represents either a raw or fat pointer.
#[repr(C)]
union FatPtrRepr<T: ?Sized> {
    pub ptr_const: *const T,
    pub fat_ptr: FatPtr,
}

#[cfg(test)]
mod tests {
    use super::FatPtr;

    #[test]
    fn from_raw_ptr() {
        let slice: &[usize] = &[0_usize];
        let fat_ptr = FatPtr::from_raw_ptr(slice as *const [usize]);
        assert_eq!(fat_ptr.metadata(), 1 as *const ());
    }
}