//! Standard base64 (RFC 4648 ยง4, padded): MCP image content and the control channel's inline PNGs. const A: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; pub fn encode(data: &[u8]) -> String { let mut s = String::with_capacity(data.len().div_ceil(3) * 4); for c in data.chunks(3) { let n = (c[0] as u32) << 16 | (*c.get(1).unwrap_or(&0) as u32) << 8 | *c.get(2).unwrap_or(&0) as u32; for i in 0..4 { s.push(if i <= c.len() { A[(n >> (18 - 6 * i) & 63) as usize] as char } else { '=' }); } } s } /// Decode, ignoring ASCII whitespace; `None` on any other invalid input. pub fn decode(s: &str) -> Option> { let val = |c: u8| -> Option { Some(match c { b'A'..=b'Z' => c - b'A', b'a'..=b'z' => c - b'a' + 26, b'0'..=b'9' => c - b'0' + 52, b'+' => 62, b'/' => 63, _ => return None, } as u32) }; let bytes: Vec = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect(); if !bytes.len().is_multiple_of(4) { return None; } let mut out = Vec::with_capacity(bytes.len() / 4 * 3); for (ci, c) in bytes.chunks(4).enumerate() { let last = ci == bytes.len() / 4 - 1; let pad = c.iter().rev().take_while(|b| **b == b'=').count(); if pad > 2 || (pad > 0 && !last) { return None; } let mut n = 0u32; for &b in &c[..4 - pad] { n = n << 6 | val(b)?; } n <<= 6 * pad as u32; out.extend_from_slice(&[(n >> 16) as u8, (n >> 8) as u8, n as u8][..3 - pad]); } Some(out) } #[cfg(test)] mod tests { #[test] fn rfc4648_vectors() { for (i, o) in [("", ""), ("f", "Zg=="), ("fo", "Zm8="), ("foo", "Zm9v"), ("foob", "Zm9vYg=="), ("fooba", "Zm9vYmE="), ("foobar", "Zm9vYmFy")] { assert_eq!(super::encode(i.as_bytes()), o); assert_eq!(super::decode(o).unwrap(), i.as_bytes()); } assert!(super::decode("Zm9v!").is_none()); let all: Vec = (0..=255).collect(); assert_eq!(super::decode(&super::encode(&all)).unwrap(), all); } }