musli_zerocopy/mem/
maybe_uninit.rs

1use core::alloc::{Layout, LayoutError};
2use core::fmt;
3use core::mem::{ManuallyDrop, size_of};
4use core::ptr::NonNull;
5use core::slice;
6
7use crate::buf;
8use crate::error::CoerceError;
9use crate::pointer::{Pointee, Size};
10use crate::traits::ZeroCopy;
11
12/// A value which might or might not have been initialized.
13///
14/// This differs from the standard library
15/// [`MaybeUninit`][core::mem::MaybeUninit] in that its methods does not inherit
16/// the alignment of the inner value so it can correctly refer to elements of
17/// `T` in unaligned memory. Which [`OwnedBuf`] might refer to.
18///
19/// # Examples
20///
21/// Writing to a pre-allocation location in an [`OwnedBuf`].
22///
23/// [`OwnedBuf`]: crate::buf::OwnedBuf
24///
25/// ```
26/// use musli_zerocopy::{OwnedBuf, Ref, ZeroCopy};
27/// use musli_zerocopy::mem::MaybeUninit;
28///
29/// #[derive(ZeroCopy)]
30/// #[repr(C)]
31/// struct Custom { string: Ref<str> }
32///
33/// let mut buf = OwnedBuf::new();
34///
35/// let reference: Ref<MaybeUninit<Custom>> = buf.store_uninit::<Custom>();
36///
37/// let string = buf.store_unsized("Hello World!");
38///
39/// buf.load_uninit_mut(reference).write(&Custom { string });
40///
41/// let reference = reference.assume_init();
42///
43/// assert_eq!(reference.offset(), 0);
44///
45/// let custom = buf.load(reference)?;
46/// assert_eq!(buf.load(custom.string)?, "Hello World!");
47/// # Ok::<_, musli_zerocopy::Error>(())
48/// ```
49#[repr(C, packed)]
50pub union MaybeUninit<T> {
51    uninit: (),
52    value: ManuallyDrop<T>,
53}
54
55impl<T> MaybeUninit<T> {
56    /// Creates a new `MaybeUninit<T>` in an uninitialized state.
57    ///
58    /// Note that dropping a `MaybeUninit<T>` will never call `T`'s drop code.
59    /// It is your responsibility to make sure `T` gets dropped if it got
60    /// initialized.
61    ///
62    /// See the [type-level documentation][MaybeUninit] for some examples.
63    ///
64    /// # Example
65    ///
66    /// ```
67    /// use musli_zerocopy::mem::MaybeUninit;
68    ///
69    /// let mut v: MaybeUninit<u32> = MaybeUninit::uninit();
70    /// ```
71    pub const fn uninit() -> Self {
72        MaybeUninit { uninit: () }
73    }
74
75    /// Write a value to the current location being pointed to.
76    ///
77    /// Note that we cannot return a reference to the written value, because it
78    /// might not be aligned.
79    ///
80    /// We can however return the underlying bytes that were written because of
81    /// this type, since they are now initialized.
82    ///
83    /// See the [type-level documentation][MaybeUninit] for some examples.
84    ///
85    /// # Example
86    ///
87    /// Writing to an uninitialized location on the stack:
88    ///
89    /// ```
90    /// use musli_zerocopy::mem::MaybeUninit;
91    ///
92    /// let mut v: MaybeUninit<u32> = MaybeUninit::uninit();
93    /// assert_eq!(v.write(&10u32.to_le()), &[10, 0, 0, 0]);
94    /// ```
95    #[inline]
96    pub fn write(&mut self, value: &T) -> &mut [u8]
97    where
98        T: ZeroCopy,
99    {
100        unsafe {
101            let ptr = NonNull::new_unchecked(self as *mut Self as *mut u8);
102            buf::store_unaligned(ptr, value);
103            slice::from_raw_parts_mut(ptr.as_ptr(), size_of::<T>())
104        }
105    }
106}
107
108impl<T> fmt::Debug for MaybeUninit<T> {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_struct("MaybeUninit").finish_non_exhaustive()
111    }
112}
113
114impl<T> Pointee for MaybeUninit<T>
115where
116    T: Pointee,
117{
118    type Metadata = T::Metadata;
119    type Stored<O>
120        = T::Stored<O>
121    where
122        O: Size;
123
124    #[inline]
125    fn try_from_metadata<O>(metadata: Self::Metadata) -> Result<Self::Stored<O>, CoerceError>
126    where
127        O: Size,
128    {
129        T::try_from_metadata(metadata)
130    }
131
132    #[inline]
133    fn size<O>(metadata: Self::Stored<O>) -> Option<usize>
134    where
135        O: Size,
136    {
137        T::size::<O>(metadata)
138    }
139
140    #[inline]
141    fn align<O>(metadata: Self::Stored<O>) -> usize
142    where
143        O: Size,
144    {
145        T::align::<O>(metadata)
146    }
147
148    #[inline]
149    fn pointee_layout<O>(metadata: Self::Stored<O>) -> Result<Layout, LayoutError>
150    where
151        O: Size,
152    {
153        T::pointee_layout::<O>(metadata)
154    }
155}