use log::debug;
pub(super) fn unescape_c_escape_string(s: &str) -> Vec<u8> {
let src = s.as_bytes();
let len = src.len();
let mut dst = Vec::new();
let mut p = 0;
while p < len {
if src[p] != b'\\' {
dst.push(src[p]);
p += 1;
} else {
p += 1;
if p == len {
panic!("invalid c-escaped default binary value ({s}): ends with '\'")
}
match src[p] {
b'a' => {
dst.push(0x07);
p += 1;
}
b'b' => {
dst.push(0x08);
p += 1;
}
b'f' => {
dst.push(0x0C);
p += 1;
}
b'n' => {
dst.push(0x0A);
p += 1;
}
b'r' => {
dst.push(0x0D);
p += 1;
}
b't' => {
dst.push(0x09);
p += 1;
}
b'v' => {
dst.push(0x0B);
p += 1;
}
b'\\' => {
dst.push(0x5C);
p += 1;
}
b'?' => {
dst.push(0x3F);
p += 1;
}
b'\'' => {
dst.push(0x27);
p += 1;
}
b'"' => {
dst.push(0x22);
p += 1;
}
b'0'..=b'7' => {
debug!("another octal: {}, offset: {}", s, &s[p..]);
let mut octal = 0;
for _ in 0..3 {
if p < len && src[p] >= b'0' && src[p] <= b'7' {
debug!("\toctal: {octal}");
octal = octal * 8 + (src[p] - b'0');
p += 1;
} else {
break;
}
}
dst.push(octal);
}
b'x' | b'X' => {
if p + 3 > len {
panic!("invalid c-escaped default binary value ({s}): incomplete hex value")
}
match u8::from_str_radix(&s[p + 1..p + 3], 16) {
Ok(b) => dst.push(b),
_ => panic!(
"invalid c-escaped default binary value ({}): invalid hex value",
&s[p..p + 2]
),
}
p += 3;
}
_ => panic!("invalid c-escaped default binary value ({s}): invalid escape"),
}
}
}
dst
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unescape_c_escape_string() {
assert_eq!(
&b"hello world"[..],
&unescape_c_escape_string("hello world")[..]
);
assert_eq!(&b"\0"[..], &unescape_c_escape_string(r#"\0"#)[..]);
assert_eq!(
&[0o012, 0o156],
&unescape_c_escape_string(r#"\012\156"#)[..]
);
assert_eq!(&[0x01, 0x02], &unescape_c_escape_string(r#"\x01\x02"#)[..]);
assert_eq!(
&b"\0\x01\x07\x08\x0C\n\r\t\x0B\\\'\"\xFE"[..],
&unescape_c_escape_string(r#"\0\001\a\b\f\n\r\t\v\\\'\"\xfe"#)[..]
);
}
#[test]
#[should_panic(expected = "incomplete hex value")]
fn test_unescape_c_escape_string_incomplete_hex_value() {
unescape_c_escape_string(r#"\x1"#);
}
}