escape_bytes/
escape.rs

1use core::borrow::Borrow;
2
3/// Escape the bytes.
4///
5/// See [crate] for the exact rules.
6///
7/// ## Example
8///
9/// ```rust
10/// let str = b"hello\xc3world";
11/// let escaped = escape_bytes::escape(str);
12/// assert_eq!(escaped, br"hello\xc3world");
13/// ```
14#[cfg(feature = "alloc")]
15#[cfg_attr(feature = "doc", doc(cfg(feature = "alloc")))]
16pub fn escape<I>(i: I) -> alloc::vec::Vec<u8>
17where
18    I: IntoIterator,
19    I::Item: Borrow<u8>,
20{
21    let mut escaped = alloc::vec::Vec::<u8>::new();
22    for b in Escape::new(i) {
23        escaped.push(b);
24    }
25    escaped
26}
27
28/// Escape into error occurs when escaping into a slice cannot continue.
29#[derive(Debug, PartialEq, Eq)]
30pub enum EscapeIntoError {
31    /// Writing into the slice would write to a position that is out-of-bounds.
32    OutOfBounds,
33}
34
35/// Escape the bytes into the slice.
36///
37/// See [crate] for the exact rules.
38///
39/// Returns the number of bytes written to the slice.
40///
41/// ## Errors
42///
43/// If the slice is not large enough to receive the escaped value. No
44/// information is provided to support continuing escaping into a new buffer
45/// from where it stops. Use the [`Escape`] iterator directly if that is needed.
46pub fn escape_into<I>(out: &mut [u8], i: I) -> Result<usize, EscapeIntoError>
47where
48    I: IntoIterator,
49    I::Item: Borrow<u8>,
50{
51    let mut count = 0usize;
52    for (idx, b) in Escape::new(i).enumerate() {
53        let Some(v) = out.get_mut(idx) else {
54            return Err(EscapeIntoError::OutOfBounds);
55        };
56        *v = b;
57        count += 1;
58    }
59    Ok(count)
60}
61
62/// Returns the maximum escaped length of the given unescaped length.
63#[must_use]
64pub const fn escaped_max_len(len: usize) -> Option<usize> {
65    len.checked_mul(4)
66}
67
68/// Returns the escaped length of the input.
69pub fn escaped_len<I>(i: I) -> usize
70where
71    I: IntoIterator,
72    Escape<I>: Iterator,
73{
74    Escape::new(i).count()
75}
76
77/// Iterator that escapes the input iterator.
78///
79/// See [crate] for the exact rules.
80///
81/// Use [`escape`] or [`escape_into`].
82#[derive(Debug)]
83pub struct Escape<I>
84where
85    I: IntoIterator,
86{
87    next: Next,
88    input: I::IntoIter,
89}
90
91impl<I> Clone for Escape<I>
92where
93    I: IntoIterator,
94    I::IntoIter: Clone,
95{
96    fn clone(&self) -> Self {
97        Self {
98            next: self.next.clone(),
99            input: self.input.clone(),
100        }
101    }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
105enum Next {
106    Input,
107    Byte1(u8),
108    Byte2(u8, u8),
109    Byte3(u8, u8, u8),
110}
111
112impl<I> Escape<I>
113where
114    I: IntoIterator,
115{
116    pub fn new(i: I) -> Self {
117        Self {
118            next: Next::Input,
119            input: i.into_iter(),
120        }
121    }
122}
123
124impl<I> Iterator for Escape<I>
125where
126    I: IntoIterator,
127    I::Item: Borrow<u8>,
128{
129    type Item = u8;
130
131    /// Returns the next escaped byte.
132    fn next(&mut self) -> Option<Self::Item> {
133        match self.next {
134            Next::Input => {
135                let Some(b) = self.input.next() else {
136                    return None;
137                };
138                let b = *b.borrow();
139                match b {
140                    // Backslash is rendered as double backslash.
141                    b'\\' => {
142                        self.next = Next::Byte1(b'\\');
143                        Some(b'\\')
144                    }
145                    // Nul is rendered as backslash 0.
146                    b'\0' => {
147                        self.next = Next::Byte1(b'0');
148                        Some(b'\\')
149                    }
150                    // Tab is rendered as backslash t.
151                    b'\t' => {
152                        self.next = Next::Byte1(b't');
153                        Some(b'\\')
154                    }
155                    // Carriage return is rendered as backslash r.
156                    b'\r' => {
157                        self.next = Next::Byte1(b'r');
158                        Some(b'\\')
159                    }
160                    // Line feed is rendered as backslash n.
161                    b'\n' => {
162                        self.next = Next::Byte1(b'n');
163                        Some(b'\\')
164                    }
165                    // All other printable ASCII characters render as their ASCII character.
166                    b' '..=b'~' => Some(b),
167                    // All other values rendered as an escaped hex value.
168                    _ => {
169                        const HEX_ALPHABET: [u8; 16] = *b"0123456789abcdef";
170                        self.next = Next::Byte3(
171                            b'x',
172                            HEX_ALPHABET[(b >> 4) as usize],
173                            HEX_ALPHABET[(b & 0xF) as usize],
174                        );
175                        Some(b'\\')
176                    }
177                }
178            }
179            Next::Byte1(b1) => {
180                self.next = Next::Input;
181                Some(b1)
182            }
183            Next::Byte2(b1, b2) => {
184                self.next = Next::Byte1(b2);
185                Some(b1)
186            }
187            Next::Byte3(b1, b2, b3) => {
188                self.next = Next::Byte2(b2, b3);
189                Some(b1)
190            }
191        }
192    }
193
194    fn size_hint(&self) -> (usize, Option<usize>) {
195        let input_hint = self.input.size_hint();
196        (input_hint.0, input_hint.1.and_then(escaped_max_len))
197    }
198}