#[must_use]
pub fn needs_unescape(s: &str) -> bool {
s.as_bytes().contains(&b'\\')
}
pub fn unescape_into(s: &str, out: &mut [u8]) -> Option<usize> {
let bytes = s.as_bytes();
let mut i = 0;
let mut w = 0;
while i < bytes.len() {
if bytes[i] == b'\\' && i + 1 < bytes.len() {
let next = bytes[i + 1];
let decoded = match next {
b'|' | b'[' | b']' | b';' | b',' | b'{' | b'}' | b'#' | b'@' | b'^' | b'\\' => next,
b'n' => b'\n',
_ => {
if w >= out.len() {
return None;
}
out[w] = b'\\';
w += 1;
i += 1;
continue;
}
};
if w >= out.len() {
return None;
}
out[w] = decoded;
w += 1;
i += 2;
} else {
if w >= out.len() {
return None;
}
out[w] = bytes[i];
w += 1;
i += 1;
}
}
Some(w)
}
const STRUCTURAL: &[u8] = b"|[];,{}#@^\\\n";
fn needs_escape(b: u8) -> bool {
STRUCTURAL.contains(&b)
}
pub fn escape_into(s: &str, out: &mut [u8]) -> Option<usize> {
let bytes = s.as_bytes();
let mut w = 0;
for &b in bytes {
if needs_escape(b) {
if w + 1 >= out.len() {
return None;
}
out[w] = b'\\';
w += 1;
if b == b'\n' {
out[w] = b'n';
} else {
out[w] = b;
}
w += 1;
} else {
if w >= out.len() {
return None;
}
out[w] = b;
w += 1;
}
}
Some(w)
}