• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! String representations.
4 
5 use core::fmt;
6 
7 /// Allows formatting of [`fmt::Arguments`] into a raw buffer.
8 ///
9 /// It does not fail if callers write past the end of the buffer so that they can calculate the
10 /// size required to fit everything.
11 ///
12 /// # Invariants
13 ///
14 /// The memory region between `pos` (inclusive) and `end` (exclusive) is valid for writes if `pos`
15 /// is less than `end`.
16 pub(crate) struct RawFormatter {
17     // Use `usize` to use `saturating_*` functions.
18     #[allow(dead_code)]
19     beg: usize,
20     pos: usize,
21     end: usize,
22 }
23 
24 impl RawFormatter {
25     /// Creates a new instance of [`RawFormatter`] with the given buffer pointers.
26     ///
27     /// # Safety
28     ///
29     /// If `pos` is less than `end`, then the region between `pos` (inclusive) and `end`
30     /// (exclusive) must be valid for writes for the lifetime of the returned [`RawFormatter`].
from_ptrs(pos: *mut u8, end: *mut u8) -> Self31     pub(crate) unsafe fn from_ptrs(pos: *mut u8, end: *mut u8) -> Self {
32         // INVARIANT: The safety requirements guarantee the type invariants.
33         Self {
34             beg: pos as _,
35             pos: pos as _,
36             end: end as _,
37         }
38     }
39 
40     /// Returns the current insert position.
41     ///
42     /// N.B. It may point to invalid memory.
pos(&self) -> *mut u843     pub(crate) fn pos(&self) -> *mut u8 {
44         self.pos as _
45     }
46 }
47 
48 impl fmt::Write for RawFormatter {
write_str(&mut self, s: &str) -> fmt::Result49     fn write_str(&mut self, s: &str) -> fmt::Result {
50         // `pos` value after writing `len` bytes. This does not have to be bounded by `end`, but we
51         // don't want it to wrap around to 0.
52         let pos_new = self.pos.saturating_add(s.len());
53 
54         // Amount that we can copy. `saturating_sub` ensures we get 0 if `pos` goes past `end`.
55         let len_to_copy = core::cmp::min(pos_new, self.end).saturating_sub(self.pos);
56 
57         if len_to_copy > 0 {
58             // SAFETY: If `len_to_copy` is non-zero, then we know `pos` has not gone past `end`
59             // yet, so it is valid for write per the type invariants.
60             unsafe {
61                 core::ptr::copy_nonoverlapping(
62                     s.as_bytes().as_ptr(),
63                     self.pos as *mut u8,
64                     len_to_copy,
65                 )
66             };
67         }
68 
69         self.pos = pos_new;
70         Ok(())
71     }
72 }
73