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