diff options
Diffstat (limited to 'rust/kernel/str.rs')
| -rw-r--r-- | rust/kernel/str.rs | 72 | 
1 files changed, 72 insertions, 0 deletions
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs new file mode 100644 index 000000000000..e45ff220ae50 --- /dev/null +++ b/rust/kernel/str.rs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! String representations. + +use core::fmt; + +/// Allows formatting of [`fmt::Arguments`] into a raw buffer. +/// +/// It does not fail if callers write past the end of the buffer so that they can calculate the +/// size required to fit everything. +/// +/// # Invariants +/// +/// The memory region between `pos` (inclusive) and `end` (exclusive) is valid for writes if `pos` +/// is less than `end`. +pub(crate) struct RawFormatter { +    // Use `usize` to use `saturating_*` functions. +    #[allow(dead_code)] +    beg: usize, +    pos: usize, +    end: usize, +} + +impl RawFormatter { +    /// Creates a new instance of [`RawFormatter`] with the given buffer pointers. +    /// +    /// # Safety +    /// +    /// If `pos` is less than `end`, then the region between `pos` (inclusive) and `end` +    /// (exclusive) must be valid for writes for the lifetime of the returned [`RawFormatter`]. +    pub(crate) unsafe fn from_ptrs(pos: *mut u8, end: *mut u8) -> Self { +        // INVARIANT: The safety requierments guarantee the type invariants. +        Self { +            beg: pos as _, +            pos: pos as _, +            end: end as _, +        } +    } + +    /// Returns the current insert position. +    /// +    /// N.B. It may point to invalid memory. +    pub(crate) fn pos(&self) -> *mut u8 { +        self.pos as _ +    } +} + +impl fmt::Write for RawFormatter { +    fn write_str(&mut self, s: &str) -> fmt::Result { +        // `pos` value after writing `len` bytes. This does not have to be bounded by `end`, but we +        // don't want it to wrap around to 0. +        let pos_new = self.pos.saturating_add(s.len()); + +        // Amount that we can copy. `saturating_sub` ensures we get 0 if `pos` goes past `end`. +        let len_to_copy = core::cmp::min(pos_new, self.end).saturating_sub(self.pos); + +        if len_to_copy > 0 { +            // SAFETY: If `len_to_copy` is non-zero, then we know `pos` has not gone past `end` +            // yet, so it is valid for write per the type invariants. +            unsafe { +                core::ptr::copy_nonoverlapping( +                    s.as_bytes().as_ptr(), +                    self.pos as *mut u8, +                    len_to_copy, +                ) +            }; +        } + +        self.pos = pos_new; +        Ok(()) +    } +}  |