73 lines
2.2 KiB
Rust
73 lines
2.2 KiB
Rust
|
// 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(())
|
||
|
}
|
||
|
}
|