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}