jiff/util/
escape.rs

1/*!
2Provides convenience routines for escaping raw bytes.
3
4This was copied from `regex-automata` with a few light edits.
5*/
6
7use super::utf8;
8
9/// Provides a convenient `Debug` implementation for a `u8`.
10///
11/// The `Debug` impl treats the byte as an ASCII, and emits a human
12/// readable representation of it. If the byte isn't ASCII, then it's
13/// emitted as a hex escape sequence.
14#[derive(Clone, Copy)]
15pub(crate) struct Byte(pub u8);
16
17impl core::fmt::Display for Byte {
18    #[inline(never)]
19    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
20        if self.0 == b' ' {
21            return f.write_str(" ");
22        }
23        // 10 bytes is enough for any output from ascii::escape_default.
24        let mut bytes = [0u8; 10];
25        let mut len = 0;
26        for (i, mut b) in core::ascii::escape_default(self.0).enumerate() {
27            // capitalize \xab to \xAB
28            if i >= 2 && b'a' <= b && b <= b'f' {
29                b -= 32;
30            }
31            bytes[len] = b;
32            len += 1;
33        }
34        f.write_str(core::str::from_utf8(&bytes[..len]).unwrap())
35    }
36}
37
38impl core::fmt::Debug for Byte {
39    #[inline(never)]
40    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
41        f.write_str("\"")?;
42        core::fmt::Display::fmt(self, f)?;
43        f.write_str("\"")?;
44        Ok(())
45    }
46}
47
48/// Provides a convenient `Debug` implementation for `&[u8]`.
49///
50/// This generally works best when the bytes are presumed to be mostly
51/// UTF-8, but will work for anything. For any bytes that aren't UTF-8,
52/// they are emitted as hex escape sequences.
53#[derive(Clone, Copy)]
54pub(crate) struct Bytes<'a>(pub &'a [u8]);
55
56impl<'a> core::fmt::Display for Bytes<'a> {
57    #[inline(never)]
58    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
59        // This is a sad re-implementation of a similar impl found in bstr.
60        let mut bytes = self.0;
61        while let Some(result) = utf8::decode(bytes) {
62            let ch = match result {
63                Ok(ch) => ch,
64                Err(err) => {
65                    // The decode API guarantees `errant_bytes` is non-empty.
66                    write!(f, r"\x{:02x}", err.as_slice()[0])?;
67                    bytes = &bytes[1..];
68                    continue;
69                }
70            };
71            bytes = &bytes[ch.len_utf8()..];
72            match ch {
73                '\0' => f.write_str(r"\0")?,
74                '\x01'..='\x7f' => {
75                    core::fmt::Display::fmt(&(ch as u8).escape_ascii(), f)?;
76                }
77                _ => {
78                    core::fmt::Display::fmt(&ch.escape_debug(), f)?;
79                }
80            }
81        }
82        Ok(())
83    }
84}
85
86impl<'a> core::fmt::Debug for Bytes<'a> {
87    #[inline(never)]
88    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
89        f.write_str("\"")?;
90        core::fmt::Display::fmt(self, f)?;
91        f.write_str("\"")?;
92        Ok(())
93    }
94}
95
96/// A helper for repeating a single byte utilizing `Byte`.
97///
98/// This is limited to repeating a byte up to `u8::MAX` times in order
99/// to reduce its size overhead. And in practice, Jiff just doesn't
100/// need more than this (at time of writing, 2025-11-29).
101pub(crate) struct RepeatByte {
102    pub(crate) byte: u8,
103    pub(crate) count: u8,
104}
105
106impl core::fmt::Display for RepeatByte {
107    #[inline(never)]
108    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
109        for _ in 0..self.count {
110            core::fmt::Display::fmt(&Byte(self.byte), f)?;
111        }
112        Ok(())
113    }
114}
115
116impl core::fmt::Debug for RepeatByte {
117    #[inline(never)]
118    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
119        f.write_str("\"")?;
120        core::fmt::Display::fmt(self, f)?;
121        f.write_str("\"")?;
122        Ok(())
123    }
124}