| // 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(()) |
| } |
| } |