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