Skip to main content

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}