const ALPHABET: &[u8; 64] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
pub fn encode(data: &[u8]) -> String {
if data.is_empty() {
return String::new();
}
let mut out = String::with_capacity((data.len() + 2) / 3 * 4);
for chunk in data.chunks(3) {
let b0 = u32::from(chunk[0]);
let b1 = u32::from(*chunk.get(1).unwrap_or(&0));
let b2 = u32::from(*chunk.get(2).unwrap_or(&0));
let n = (b0 << 16) | (b1 << 8) | b2;
out.push(char::from(ALPHABET[(n >> 18) as usize & 63]));
out.push(char::from(ALPHABET[(n >> 12) as usize & 63]));
out.push(if chunk.len() > 1 {
char::from(ALPHABET[(n >> 6) as usize & 63])
} else {
'='
});
out.push(if chunk.len() > 2 {
char::from(ALPHABET[n as usize & 63])
} else {
'='
});
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rfc4648_vectors() {
let cases = [
("", ""),
("f", "Zg=="),
("fo", "Zm8="),
("foo", "Zm9v"),
("foob", "Zm9vYg=="),
("fooba", "Zm9vYmE="),
("foobar", "Zm9vYmFy"),
];
for (input, expect) in cases {
assert_eq!(encode(input.as_bytes()), expect, "input={input}");
}
}
#[test]
fn binary_roundtrip() {
let data = [0u8, 1, 2, 3, 250, 251, 252, 253, 254, 255];
assert_eq!(encode(&data), "AAECA/r7/P3+/w==");
assert_eq!(encode(&data).len(), 16);
}
}