pub fn raw_from_escaped(src: &str) -> Vec<u8> {
raw_from_escaped_bytes(src.as_bytes())
}
pub fn raw_from_escaped_bytes(src: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(src.len());
let mut i = 0;
while i < src.len() {
let c = src[i];
i += 1;
if c != b'\\' {
out.push(c);
continue;
}
let Some(&esc) = src.get(i) else { break };
i += 1;
match esc {
b'a' => out.push(0x07),
b'b' => out.push(0x08),
b'f' => out.push(0x0c),
b'n' => out.push(b'\n'),
b'r' => out.push(b'\r'),
b't' => out.push(b'\t'),
b'v' => out.push(0x0b),
b'\\' => out.push(b'\\'),
b'\'' => out.push(b'\''),
b'"' => out.push(b'"'),
b'0' => out.push(0),
b'x' => {
let mut value: u32 = 0;
let mut digits = 0;
while digits < 2 {
match src.get(i).and_then(|c| (*c as char).to_digit(16)) {
Some(d) => {
value = value << 4 | d;
digits += 1;
i += 1;
}
None => break,
}
}
if digits == 0 {
continue;
}
out.push(value as u8);
}
other => out.push(other),
}
}
out
}
#[cfg(test)]
mod tests {
use super::raw_from_escaped;
#[test]
fn oracle_cases() {
assert_eq!(raw_from_escaped("hex\\x41end"), b"hexAend");
assert_eq!(raw_from_escaped("d:\\q."), b"d:q.");
assert_eq!(raw_from_escaped("u:\\u0041."), b"u:u0041.");
assert_eq!(raw_from_escaped("b:\\x4a."), b"b:J.");
assert_eq!(raw_from_escaped("a \\\"b\\\" c"), b"a \"b\" c");
assert_eq!(raw_from_escaped("x\\ty"), b"x\ty");
assert_eq!(raw_from_escaped("sq:\\tx"), b"sq:\tx");
}
#[test]
fn control_escapes() {
assert_eq!(
raw_from_escaped("\\a\\b\\f\\n\\r\\t\\v\\\\\\'"),
b"\x07\x08\x0c\n\r\t\x0b\\'"
);
assert_eq!(raw_from_escaped("a\\0b"), b"a\0b");
}
#[test]
fn hex_escape_takes_one_or_two_digits() {
assert_eq!(raw_from_escaped("\\x41"), b"A");
assert_eq!(raw_from_escaped("\\x7"), b"\x07");
assert_eq!(raw_from_escaped("\\x41x"), b"Ax");
}
#[test]
fn a_high_hex_escape_is_one_byte() {
assert_eq!(raw_from_escaped("h\\xffz"), vec![b'h', 0xFF, b'z']);
assert_eq!(raw_from_escaped("\\x80"), vec![0x80]);
assert_eq!(raw_from_escaped("\\xc3\\xa9"), vec![0xC3, 0xA9]);
}
#[test]
fn hex_escape_without_digits_reexamines_the_next_char() {
assert_eq!(raw_from_escaped("\\xzz"), b"zz");
assert_eq!(raw_from_escaped("a\\x\\tb"), b"a\tb");
}
#[test]
fn trailing_backslash_is_dropped() {
assert_eq!(raw_from_escaped("abc\\"), b"abc");
}
#[test]
fn plain_text_is_untouched() {
assert_eq!(raw_from_escaped("@asyn(PORT,0)"), b"@asyn(PORT,0)");
}
}