repr_c_wrapper/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use core::mem::{size_of, align_of, ManuallyDrop, MaybeUninit};
4use core::ptr::{addr_of, addr_of_mut};
5
6/// Wraps a type in an opaque `#[repr(C)]` wrapper with a size equal to the original `T`
7/// 
8/// NOTE: The SIZE parameter expresses the number of `u64` required to contain `T`
9#[repr(C)]
10pub struct ReprCWrapper<const SIZE: usize, T> {
11    buffer: MaybeUninit<[u64; SIZE]>,
12    phantom: core::marker::PhantomData<T>,
13}
14
15impl<const SIZE: usize, T> From<T> for ReprCWrapper<SIZE, T> {
16    fn from(val: T) -> Self {
17        Self::new(val)
18    }
19}
20
21impl<const SIZE: usize, T> Drop for ReprCWrapper<SIZE, T> {
22    fn drop(&mut self) {
23        unsafe{
24            let val = &mut *self.buffer.as_mut_ptr().cast::<ManuallyDrop<T>>();
25            ManuallyDrop::<T>::drop(val);
26        }
27    }
28}
29
30impl<const SIZE: usize, T> core::ops::Deref for ReprCWrapper<SIZE, T> {
31    type Target = T;
32
33    fn deref(&self) -> &Self::Target {
34        unsafe{ &*addr_of!(self.buffer).cast::<ManuallyDrop::<T>>() }
35    }
36}
37
38impl<const SIZE: usize, T> core::ops::DerefMut for ReprCWrapper<SIZE, T> {
39    fn deref_mut(&mut self) -> &mut Self::Target {
40        unsafe{ &mut *addr_of_mut!(self.buffer).cast::<ManuallyDrop::<T>>() }
41    }
42}
43
44impl<const SIZE: usize, T> ReprCWrapper<SIZE, T> {
45    /// Returns a `ReprCWrapper` from a `T`
46    pub fn new(val: T) -> Self {
47        assert!(align_of::<T>() <= align_of::<Self>());
48        assert_eq!(SIZE, (size_of::<ManuallyDrop::<T>>() + size_of::<u64>() - 1) / size_of::<u64>());
49
50        let val = ManuallyDrop::<T>::new(val);
51        let mut wrapper = Self {
52            buffer: MaybeUninit::uninit(),
53            phantom: core::marker::PhantomData
54        };
55        unsafe{ (wrapper.buffer.as_mut_ptr().cast::<ManuallyDrop::<T>>()).write(val); }
56        wrapper
57    }
58    //NOTE: Use Deref & DerefMut instead of borrow() and borrow_mut()
59    // Fewer methods for wrappers reduces the chance of interfering with T's methods
60    // /// Borrows the inner `T` from a `ReprCWrapper`
61    // pub fn borrow(&self) -> &T {
62    //     unsafe{ &*addr_of!(self.buffer).cast::<ManuallyDrop::<T>>() }
63    // }
64    // /// Mutably borrows the inner `T` from a `ReprCWrapper`
65    // pub fn borrow_mut(&mut self) -> &mut T {
66    //     unsafe{ &mut *addr_of_mut!(self.buffer).cast::<ManuallyDrop::<T>>() }
67    // }
68    /// Consumes the `ReprCWrapper`, and returns the inner `T`
69    pub fn into_inner(self) -> T {
70        let val = unsafe{ core::ptr::read(addr_of!(self.buffer).cast::<ManuallyDrop<T>>()) };
71        core::mem::forget(self);
72        ManuallyDrop::<T>::into_inner(val)
73    }
74}
75
76/// A `ReprCWrapper` type that corresponds to a wrapped version of `T`
77///
78/// NOTE: This macro is a stop-gap convenience to automatically get the correct type size,
79/// until a future version of Rust stabilizes `generic_const_exprs`.  At that point, it will
80/// be as simple as using `ReprCWrapper<T>`.
81#[macro_export]
82macro_rules! repr_c_wrapper_t {
83    ( $t:ty ) => { $crate::ReprCWrapper<{(core::mem::size_of::<core::mem::ManuallyDrop::<$t>>() + core::mem::size_of::<u64>() - 1) / core::mem::size_of::<u64>()}, $t> };
84}
85
86#[cfg(test)]
87mod test {
88    use super::*;
89
90    #[test]
91    fn test_basics() {
92
93        let mut wrapper: repr_c_wrapper_t!(String) = "Hello".to_string().into();
94        assert_eq!(*wrapper, "Hello");
95
96        *wrapper = "World".to_string();
97        assert_eq!(*wrapper, "World");
98
99        let the_string = wrapper.into_inner();
100        assert_eq!(the_string, "World");
101    }
102
103    struct VecCWrapper(repr_c_wrapper_t!(Vec<usize>));
104
105    #[test]
106    fn test_repr_c_wrapper_t_macro() {
107
108        let wrapped_vec = VecCWrapper(vec![3, 2, 1].into());
109
110        assert_eq!(*wrapped_vec.0, &[3, 2, 1]);
111    }
112
113    #[repr(align(8))]
114    #[derive(Debug, Default, PartialEq)]
115    struct WithPadding([u8;3]);
116    struct WithPaddingCWrapper(repr_c_wrapper_t!(WithPadding));
117
118    #[test]
119    pub fn test_against_reading_uninit() {
120        let wrapped = WithPaddingCWrapper(WithPadding::default().into());
121        assert_eq!(&*wrapped.0, &WithPadding::default());
122        let wrapped = WithPaddingCWrapper(WithPadding::default().into());
123        assert_eq!(&*wrapped.0, &WithPadding::default());
124    }
125
126    #[repr(align(256))]
127    #[derive(Debug, PartialEq)]
128    struct WellAligned([u8;256]);
129    struct WellAlignedCWrapper(repr_c_wrapper_t!(WellAligned));
130
131    #[test]
132    #[should_panic]
133    pub fn test_against_unaligned() {
134        let wrapped_wa = WellAlignedCWrapper(WellAligned([0; 256]).into());
135        assert_eq!(&*wrapped_wa.0, &WellAligned([0; 256]));
136        let wrapped_wa = WellAlignedCWrapper(WellAligned([0; 256]).into());
137        assert_eq!(&*wrapped_wa.0, &WellAligned([0; 256]));
138    }
139
140}
141
142//FUTURE NOTE: This implementation will get much cleaner when generic_const_exprs
143// is merged into stable Rust.  https://github.com/rust-lang/rust/issues/76560
144//When that happens, the trait and macros can be totally eliminated
145
146// #![feature(generic_const_exprs)]
147
148// #[repr(C)]
149// pub struct ReprCWrapper<T>
150//     where [(); (size_of::<ManuallyDrop::<T>>() + size_of::<u64>() - 1) / size_of::<u64>()]:
151// {
152//     bytes: [u64; (size_of::<ManuallyDrop::<T>>() + size_of::<u64>() - 1) / size_of::<u64>()],
153// }