Skip to main content

musli_zerocopy/mem/
packed_maybe_uninit.rs

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