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}