musli_zerocopy/buf/
padder.rs

1use core::marker::PhantomData;
2use core::mem::{align_of, size_of, size_of_val, transmute};
3use core::ptr::NonNull;
4
5use crate::buf;
6use crate::traits::ZeroCopy;
7
8/// A struct padder as provided to the [`ZeroCopy::pad`] method.
9///
10/// This knows how to find and initialize padding regions in `repr(C)` types,
11/// and provides a builder-like API to doing so.
12#[must_use = "For the writer to have an effect on `OwnedBuf` you must call `Padder::remaining` / `Padder::remaining_unsized`"]
13pub struct Padder<'a, T: ?Sized> {
14    data: NonNull<u8>,
15    offset: usize,
16    _marker: PhantomData<&'a mut T>,
17}
18
19impl<'a, T: ?Sized> Padder<'a, T> {
20    #[inline]
21    pub(crate) fn new(data: NonNull<u8>) -> Self {
22        Self {
23            data,
24            offset: 0,
25            _marker: PhantomData,
26        }
27    }
28
29    /// Indicate that this validate is transparent over `U`.
30    //
31    /// # Safety
32    ///
33    /// This is only allowed if `T` is `#[repr(transparent)]` over `U`.
34    #[inline]
35    pub unsafe fn transparent<U>(&mut self) -> &mut Padder<'a, U> {
36        unsafe { transmute(self) }
37    }
38
39    /// Pad around the given field with zeros.
40    ///
41    /// Note that this is necessary to do correctly in order to satisfy the
42    /// safety requirements by [`remaining()`].
43    ///
44    /// This is typically not called directly, but rather is implemented by the
45    /// [`ZeroCopy`] derive.
46    ///
47    /// [`remaining()`]: Self::remaining
48    /// [`ZeroCopy`]: derive@crate::ZeroCopy
49    ///
50    /// # Safety
51    ///
52    /// The caller must ensure that the field type `F` is an actual field in
53    /// order in the struct being padded.
54    #[inline]
55    pub unsafe fn pad<F>(&mut self)
56    where
57        F: ZeroCopy,
58    {
59        unsafe {
60            self.pad_with::<F>(align_of::<F>());
61        }
62    }
63
64    /// Pad around the given field with zeros using a custom alignment `align`.
65    ///
66    /// Note that this is necessary to do correctly in order to satisfy the
67    /// safety requirements by [`remaining()`].
68    ///
69    /// This is typically not called directly, but rather is implemented by the
70    /// [`ZeroCopy`] derive.
71    ///
72    /// [`remaining()`]: Self::remaining
73    /// [`ZeroCopy`]: derive@crate::ZeroCopy
74    ///
75    /// # Safety
76    ///
77    /// The caller must ensure that the field type `F` is an actual field in
78    /// order in the struct being padded and that `align` matches the argument
79    /// provided to `#[repr(packed)]` (note that empty means 1).
80    #[inline]
81    pub unsafe fn pad_with<F>(&mut self, align: usize)
82    where
83        F: ZeroCopy,
84    {
85        unsafe {
86            let count = buf::padding_to(self.offset, align);
87            // zero out padding.
88            self.data.as_ptr().add(self.offset).write_bytes(0, count);
89            self.offset += count;
90
91            if F::PADDED {
92                let ptr = NonNull::new_unchecked(self.data.as_ptr().add(self.offset));
93                let mut padder = Padder::new(ptr);
94                F::pad(&mut padder);
95                padder.remaining();
96            }
97
98            self.offset += size_of::<F>();
99        }
100    }
101
102    /// Specific method to both pad for a discriminant and load it
103    /// simultaneously for inspection.
104    ///
105    /// # Safety
106    ///
107    /// The caller must ensure that the field type `D` is the actual type of the
108    /// discriminant first in order in the enum being padded and that `D` is a
109    /// primitive that does not contain any interior padding.
110    #[inline]
111    pub unsafe fn pad_discriminant<D>(&mut self) -> D
112    where
113        D: ZeroCopy,
114    {
115        unsafe {
116            let count = buf::padding_to(self.offset, align_of::<D>());
117            // zero out padding.
118            self.data.as_ptr().add(self.offset).write_bytes(0, count);
119            let at = self.offset + count;
120            let value = self.data.as_ptr().add(at).cast::<D>().read_unaligned();
121            self.offset = at + size_of::<D>();
122            value
123        }
124    }
125
126    /// Finish writing the current buffer.
127    ///
128    /// This is typically not called directly, but rather is implemented by the
129    /// [`ZeroCopy`] derive.
130    ///
131    /// [`ZeroCopy`]: derive@crate::ZeroCopy
132    ///
133    /// # Safety
134    ///
135    /// Before calling `remaining()`, the caller must ensure that they've called
136    /// [`pad::<F>()`] *in order* for every field in a struct being serialized
137    /// where `F` is the type of the field. Otherwise we might not have written
138    /// the necessary padding to ensure that all bytes related to the struct are
139    /// initialized. Failure to do so would result in undefined behavior.
140    ///
141    /// Fields which are [`ZeroSized`] can be skipped.
142    ///
143    /// [`pad::<F>()`]: Self::pad
144    /// [`ZeroSized`]: crate::traits::ZeroSized
145    #[inline]
146    pub unsafe fn remaining(self)
147    where
148        T: Sized,
149    {
150        unsafe {
151            let count = size_of::<T>() - self.offset;
152            self.data.as_ptr().add(self.offset).write_bytes(0, count);
153        }
154    }
155
156    /// Finalize remaining padding based on the size of an unsized value.
157    #[inline]
158    pub(crate) unsafe fn remaining_unsized(self, value: &T) {
159        unsafe {
160            let count = size_of_val(value) - self.offset;
161            self.data.as_ptr().add(self.offset).write_bytes(0, count);
162        }
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use std::mem::size_of;
169
170    use anyhow::Result;
171
172    use crate::{OwnedBuf, ZeroCopy, buf};
173
174    #[test]
175    fn ensure_padding() -> Result<()> {
176        #[derive(Debug, PartialEq, Eq, ZeroCopy)]
177        #[repr(C)]
178        #[zero_copy(crate)]
179        struct ZeroPadded(u8, u16, u64);
180
181        let padded = ZeroPadded(
182            0x01u8.to_be(),
183            0x0203u16.to_be(),
184            0x0405060708090a0bu64.to_be(),
185        );
186
187        let mut buf = OwnedBuf::new();
188
189        // Note: You're responsible for ensuring that the buffer has enough
190        // capacity.
191        buf.reserve(size_of::<ZeroPadded>());
192
193        // SAFETY: We do not pad beyond known fields and are making sure to
194        // initialize all of the buffer.
195        unsafe {
196            buf::store_unaligned(buf.as_nonnull(), &padded);
197            buf.advance(size_of::<ZeroPadded>());
198        }
199
200        // Note: The bytes are explicitly convert to big-endian encoding above.
201        assert_eq!(
202            buf.as_slice(),
203            &[1, 0, 2, 3, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11]
204        );
205
206        Ok(())
207    }
208}