use core::borrow::Borrow;
#[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
}
#[derive(Debug, PartialEq, Eq)]
pub enum EscapeIntoError {
OutOfBounds,
}
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)
}
#[must_use]
pub const fn escaped_max_len(len: usize) -> Option<usize> {
len.checked_mul(4)
}
pub fn escaped_len<I>(i: I) -> usize
where
I: IntoIterator,
Escape<I>: Iterator,
{
Escape::new(i).count()
}
#[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;
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 {
b'\\' => {
self.next = Next::Byte1(b'\\');
Some(b'\\')
}
b'\0' => {
self.next = Next::Byte1(b'0');
Some(b'\\')
}
b'\t' => {
self.next = Next::Byte1(b't');
Some(b'\\')
}
b'\r' => {
self.next = Next::Byte1(b'r');
Some(b'\\')
}
b'\n' => {
self.next = Next::Byte1(b'n');
Some(b'\\')
}
b' '..=b'~' => Some(b),
_ => {
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))
}
}