Add `NullBorrowFormatter`, a formatter that writes a null terminated string to an array or slice buffer. Because this type needs to manage the trailing null marker, the existing formatters cannot be used to implement this type. Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx> --- rust/kernel/str.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 78b2f95eb3171..05d79cf40c201 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -860,6 +860,65 @@ fn deref_mut(&mut self) -> &mut Self::Target { } } +/// A mutable reference to a byte buffer where a string can be written into. +/// +/// The buffer will be automatically null terminated after the last written character. +/// +/// # Invariants +/// +/// `buffer` is always null terminated. +pub(crate) struct NullBorrowFormatter<'a> { + buffer: &'a mut [u8], + pos: usize, +} + +impl<'a> NullBorrowFormatter<'a> { + /// Create a new [`Self`] instance. + pub(crate) fn new(buffer: &'a mut [u8]) -> Result<NullBorrowFormatter<'a>> { + *(buffer.first_mut().ok_or(EINVAL)?) = 0; + + // INVARIANT: We null terminated the buffer above. + Ok(Self { buffer, pos: 0 }) + } + + #[expect(dead_code)] + pub(crate) fn from_array<const N: usize>( + a: &'a mut [crate::ffi::c_char; N], + ) -> Result<NullBorrowFormatter<'a>> { + Self::new( + // SAFETY: the buffer of `a` is valid for read and write as `u8` for + // at least `N` bytes. + unsafe { core::slice::from_raw_parts_mut(a.as_mut_ptr().cast::<u8>(), N) }, + ) + } + + /// Return the position of the write pointer in the underlying buffer. + #[expect(dead_code)] + pub(crate) fn pos(&self) -> usize { + self.pos + } +} + +impl Write for NullBorrowFormatter<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + let bytes = s.as_bytes(); + let len = bytes.len(); + + // We want space for a null terminator + if self.pos + len > self.buffer.len() - 1 { + return Err(fmt::Error); + } + + self.buffer[self.pos..self.pos + len].copy_from_slice(bytes); + self.pos += len; + + // INVARIANT: The buffer is null terminated. + self.buffer[self.pos] = 0; + + Ok(()) + } +} + /// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end. /// /// Used for interoperability with kernel APIs that take C strings. -- 2.47.2