|
7 | 7 |
|
8 | 8 | use std::{
|
9 | 9 | io::Write,
|
10 |
| - mem, |
| 10 | + mem::{self, MaybeUninit}, |
11 | 11 | ptr::NonNull,
|
12 | 12 | };
|
13 | 13 |
|
@@ -77,9 +77,27 @@ impl<W: Write> AlignedWriter<W> {
|
77 | 77 | self.written_so_far += 1;
|
78 | 78 | }
|
79 | 79 |
|
80 |
| - // Write down the binary data and exit |
81 |
| - // FIXME: Move write_bytes functionality here |
82 |
| - crate::write_bytes(&mut self.inner, data)?; |
| 80 | + // This is the correct way to reinterpret typed data as bytes, it |
| 81 | + // accounts for the fact that T may contain padding bytes. |
| 82 | + let bytes = std::slice::from_raw_parts( |
| 83 | + data.as_ptr() as *const MaybeUninit<u8>, |
| 84 | + data.len() * mem::size_of::<T>() |
| 85 | + ); |
| 86 | + |
| 87 | + // FIXME: Unfortunately, `Write::write_all()` expects initialized |
| 88 | + // bytes. This transmute is undefined behavior if T contains |
| 89 | + // uninitialized padding bytes. |
| 90 | + // |
| 91 | + // To resolve this UB, we'd need either a "freeze" operation |
| 92 | + // that turns uninitialized bytes into arbitrary initialized |
| 93 | + // bytes, or a `Write` interface that accepts uninit bytes. |
| 94 | + // |
| 95 | + // See this Rust internals forum topic for more discussion: |
| 96 | + // https://internals.rust-lang.org/t/writing-down-binary-data-with-padding-bytes/11197/ |
| 97 | + // |
| 98 | + self.inner.write_all(mem::transmute::<&[MaybeUninit<u8>], &[u8]>(bytes))?; |
| 99 | + |
| 100 | + // Keep track of the amount of emitted data and exit |
83 | 101 | self.written_so_far += mem::size_of_val::<[T]>(data);
|
84 | 102 | Ok(())
|
85 | 103 | }
|
|
0 commit comments