1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use crate::error::Error;
use crate::offset::TargetSize;
use crate::r#ref::Ref;
use crate::zero_copy::ZeroCopy;
/// A writer as returned from [`BufMut::store_struct`].
///
/// [`BufMut::store_struct`]: crate::buf_mut::BufMut::store_struct
pub trait StoreStruct<T, O: TargetSize> {
/// Pad around the given field with zeros.
///
/// Note that this is necessary to do correctly in order to satisfy the
/// safety requirements by [`finish()`].
///
/// This is typically not called directly, but rather is implemented by the
/// [`ZeroCopy`] derive.
///
/// [`finish()`]: Self::finish
/// [`ZeroCopy`]: derive@crate::ZeroCopy
///
/// # Examples
///
/// ```
/// use musli_zerocopy::{AlignedBuf, StoreStruct, ZeroCopy};
///
/// #[derive(Debug, PartialEq, Eq, ZeroCopy)]
/// #[repr(C)]
/// struct ZeroPadded(u8, u16);
///
/// let padded = ZeroPadded(0x01u8.to_be(), 0x0203u16.to_be());
///
/// let mut buf = AlignedBuf::new();
///
/// let mut w = buf.store_struct(&padded);
/// w.pad::<u8>();
/// w.pad::<u16>();
///
/// // Since we never called finished, the buffer has not been extended.
/// assert_eq!(buf.as_slice(), &[]);
/// # Ok::<_, musli_zerocopy::Error>(())
/// ```
fn pad<F>(&mut self)
where
F: ZeroCopy;
/// Finish writing the current buffer.
///
/// This is typically not called directly, but rather is implemented by the
/// [`ZeroCopy`] derive.
///
/// [`ZeroCopy`]: derive@crate::ZeroCopy
///
/// # Safety
///
/// Before calling `finish()`, the caller must ensure that they've called
/// [`pad::<F>()`] *in order* for every field in a struct being serialized
/// where `F` is the type of the field. Otherwise we might not have written
/// the necessary padding to ensure that all bytes related to the struct are
/// initialized. Failure to do so would result in undefined behavior.
///
/// Fields which are [`ZeroSized`] can be skipped.
///
/// [`pad::<F>()`]: Self::pad
/// [`ZeroSized`]: crate::ZeroSized
///
/// # Examples
///
/// ```
/// use musli_zerocopy::{AlignedBuf, StoreStruct, ZeroCopy};
///
/// #[derive(Debug, PartialEq, Eq, ZeroCopy)]
/// #[repr(C)]
/// struct ZeroPadded(u8, u16);
///
/// let padded = ZeroPadded(0x01u8.to_be(), 0x0203u16.to_be());
///
/// let mut buf = AlignedBuf::new();
///
/// let mut w = buf.store_struct(&padded);
/// w.pad::<u8>();
/// w.pad::<u16>();
///
/// // SAFETY: We've asserted that the struct fields have been correctly padded.
/// let ptr = unsafe { w.finish()? };
///
/// // Note: The bytes are explicitly convert to big-endian encoding above.
/// assert_eq!(buf.as_slice(), &[1, 0, 2, 3]);
///
/// let buf = buf.as_aligned();
///
/// assert_eq!(buf.load(ptr)?, &padded);
/// # Ok::<_, musli_zerocopy::Error>(())
/// ```
unsafe fn finish(self) -> Result<Ref<T, O>, Error>;
}