mnemosyne_arena/scratch/aligned_vec/bytes.rs
1//! Byte, `Pod`, and UTF-8 string views over an [`AlignedVec`]'s initialized
2//! prefix.
3//!
4//! These reinterpret what is already there and never change the length, so
5//! they sit apart from the operations that do.
6
7use super::AlignedVec;
8use super::ScratchElement;
9
10impl<T: ScratchElement> AlignedVec<T> {
11 /// Zero-copy view of the initialized elements as raw bytes.
12 ///
13 /// Available with `features = ["bytemuck"]` because this method requires
14 /// `T: bytemuck::Pod` — the guarantee that no byte in the element's
15 /// representation is uninitialized (padding bytes are not Pod-safe).
16 ///
17 /// The result length is `self.len() * size_of::<T>()`.
18 #[cfg(feature = "bytemuck")]
19 #[inline]
20 pub fn as_bytes(&self) -> &[u8]
21 where
22 T: bytemuck::Pod,
23 {
24 bytemuck::cast_slice(self.as_slice())
25 }
26
27 /// Zero-copy mutable view of the initialized elements as raw bytes.
28 ///
29 /// See [`as_bytes`][Self::as_bytes] for the requirements and the
30 /// relationship between the returned slice length and `len()`.
31 #[cfg(feature = "bytemuck")]
32 #[inline]
33 pub fn as_bytes_mut(&mut self) -> &mut [u8]
34 where
35 T: bytemuck::Pod,
36 {
37 bytemuck::cast_slice_mut(self.as_mut_slice())
38 }
39
40 /// Zero-copy reinterpretation of the initialized elements as a slice of
41 /// a different type `U`.
42 ///
43 /// Available with `features = ["bytemuck"]`. Both `T` and `U` must be
44 /// `bytemuck::Pod`. The call panics when `size_of::<T>() * len()` is not
45 /// a multiple of `size_of::<U>()` — the same contract as
46 /// `bytemuck::cast_slice`.
47 ///
48 /// # Use cases
49 ///
50 /// - View `AlignedVec<Complex32>` (interleaved re/im as f32 pairs) as
51 /// `&[f32]` for partial in-place transforms or GPU upload.
52 /// - View `AlignedVec<u32>` GPU index data as `&[u8]` for zero-copy
53 /// DMA staging, without an intermediate `Vec<u8>` copy.
54 #[cfg(feature = "bytemuck")]
55 #[inline]
56 pub fn cast_slice<U: bytemuck::Pod>(&self) -> &[U]
57 where
58 T: bytemuck::Pod,
59 {
60 bytemuck::cast_slice(self.as_slice())
61 }
62
63 /// Zero-copy mutable reinterpretation of the initialized elements as `U`.
64 ///
65 /// See [`cast_slice`][Self::cast_slice] for requirements and panics.
66 #[cfg(feature = "bytemuck")]
67 #[inline]
68 pub fn cast_slice_mut<U: bytemuck::Pod>(&mut self) -> &mut [U]
69 where
70 T: bytemuck::Pod,
71 {
72 bytemuck::cast_slice_mut(self.as_mut_slice())
73 }
74}
75
76/// `AlignedVec<u8>` as a `core::fmt::Write` sink.
77///
78/// Enables zero-allocation formatted output into an aligned buffer:
79///
80/// ```rust
81/// use core::fmt::Write as _;
82/// use mnemosyne_arena::AlignedVec;
83///
84/// let mut buf = AlignedVec::<u8>::with_capacity(64);
85/// write!(buf, "hello {}", 42).unwrap();
86/// assert_eq!(buf.as_slice(), b"hello 42");
87/// ```
88impl core::fmt::Write for AlignedVec<u8> {
89 /// Appends the UTF-8 bytes of `s` to the buffer, growing if needed.
90 #[inline]
91 fn write_str(&mut self, s: &str) -> core::fmt::Result {
92 self.extend_from_slice(s.as_bytes());
93 Ok(())
94 }
95}
96
97/// Formats an `AlignedVec<u8>` as a UTF-8 string (lossy).
98///
99/// Non-UTF-8 bytes are replaced with the Unicode replacement character U+FFFD.
100impl core::fmt::Display for AlignedVec<u8> {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 match core::str::from_utf8(self.as_slice()) {
103 Ok(s) => f.write_str(s),
104 Err(_) => {
105 // Replace non-UTF-8 bytes with U+FFFD replacement character.
106 let s = alloc::string::String::from_utf8_lossy(self.as_slice());
107 f.write_str(&s)
108 }
109 }
110 }
111}
112
113impl From<&str> for AlignedVec<u8> {
114 /// Copies the bytes of `s` into a new buffer.
115 #[inline]
116 fn from(s: &str) -> Self {
117 Self::from_slice(s.as_bytes())
118 }
119}
120
121impl AlignedVec<u8> {
122 /// Appends the bytes of `s` to the buffer.
123 ///
124 /// Equivalent to `self.extend_from_slice(s.as_bytes())` but named for
125 /// discoverability alongside the [`From<&str>`][From] impl and the
126 /// [`core::fmt::Write`] impl.
127 #[inline]
128 pub fn push_str(&mut self, s: &str) {
129 self.extend_from_slice(s.as_bytes());
130 }
131
132 /// Interprets the initialized bytes as a UTF-8 string slice.
133 ///
134 /// Returns `Err` if the bytes are not valid UTF-8.
135 #[inline]
136 pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> {
137 core::str::from_utf8(self.as_slice())
138 }
139
140 /// Interprets the initialized bytes as a UTF-8 string, replacing invalid
141 /// sequences with U+FFFD.
142 #[inline]
143 #[must_use]
144 pub fn to_string_lossy(&self) -> alloc::borrow::Cow<'_, str> {
145 alloc::string::String::from_utf8_lossy(self.as_slice())
146 }
147}