use alloc::vec::Vec;
pub(crate) fn decode_pg_hex_escape(s: &str) -> Result<Vec<u8>, usize> {
let bytes = s.as_bytes();
if bytes.len() < 2 || bytes[0] != b'\\' || bytes[1] != b'x' {
return Err(0);
}
let hex = &bytes[2..];
if hex.len() % 2 != 0 {
return Err(bytes.len().saturating_sub(1));
}
let mut out = Vec::with_capacity(hex.len() / 2);
for (i, chunk) in hex.chunks_exact(2).enumerate() {
let hi = hex_nibble(chunk[0]).ok_or(2 + i * 2)?;
let lo = hex_nibble(chunk[1]).ok_or(2 + i * 2 + 1)?;
out.push((hi << 4) | lo);
}
Ok(out)
}
#[inline]
fn hex_nibble(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}
pub(crate) fn decode_base64(s: &str) -> Result<Vec<u8>, ()> {
let src = s.as_bytes();
let mut effective_len = src.len();
while effective_len > 0 && src[effective_len - 1] == b'=' {
effective_len -= 1;
}
if src.len() % 4 != 0 {
return Err(());
}
let mut out = Vec::with_capacity((effective_len * 3) / 4);
let mut buf = 0u32;
let mut collected = 0u32;
for &c in &src[..effective_len] {
let v = base64_char(c).ok_or(())?;
buf = (buf << 6) | u32::from(v);
collected += 6;
if collected >= 8 {
collected -= 8;
out.push(((buf >> collected) & 0xFF) as u8);
}
}
Ok(out)
}
#[inline]
fn base64_char(c: u8) -> Option<u8> {
match c {
b'A'..=b'Z' => Some(c - b'A'),
b'a'..=b'z' => Some(c - b'a' + 26),
b'0'..=b'9' => Some(c - b'0' + 52),
b'+' => Some(62),
b'/' => Some(63),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn hex_decode_deadbeef() {
assert_eq!(
decode_pg_hex_escape("\\xdeadbeef").unwrap(),
vec![0xDE, 0xAD, 0xBE, 0xEF]
);
}
#[test]
fn hex_decode_uppercase() {
assert_eq!(
decode_pg_hex_escape("\\xDEADBEEF").unwrap(),
vec![0xDE, 0xAD, 0xBE, 0xEF]
);
}
#[test]
fn hex_decode_empty() {
assert_eq!(decode_pg_hex_escape("\\x").unwrap(), Vec::<u8>::new());
}
#[test]
fn hex_decode_missing_prefix() {
assert!(decode_pg_hex_escape("deadbeef").is_err());
}
#[test]
fn hex_decode_odd_length() {
assert!(decode_pg_hex_escape("\\xdea").is_err());
}
#[test]
fn hex_decode_invalid_char() {
assert!(decode_pg_hex_escape("\\xzz").is_err());
}
#[test]
fn base64_roundtrip() {
assert_eq!(
decode_base64("3q2+7w==").unwrap(),
vec![0xDE, 0xAD, 0xBE, 0xEF]
);
assert_eq!(decode_base64("AQID").unwrap(), vec![0x01, 0x02, 0x03]);
assert_eq!(decode_base64("").unwrap(), Vec::<u8>::new());
}
#[test]
fn base64_invalid_char() {
assert!(decode_base64("!!!!").is_err());
}
#[test]
fn base64_invalid_length() {
assert!(decode_base64("abc").is_err());
}
}