pub fn encode(bytes: &[u8]) -> String {
const TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = String::with_capacity(bytes.len().div_ceil(3) * 4);
let mut i = 0;
while i + 3 <= bytes.len() {
let chunk =
((bytes[i] as u32) << 16) | ((bytes[i + 1] as u32) << 8) | (bytes[i + 2] as u32);
out.push(TABLE[((chunk >> 18) & 0x3F) as usize] as char);
out.push(TABLE[((chunk >> 12) & 0x3F) as usize] as char);
out.push(TABLE[((chunk >> 6) & 0x3F) as usize] as char);
out.push(TABLE[(chunk & 0x3F) as usize] as char);
i += 3;
}
match bytes.len() - i {
1 => {
let chunk = (bytes[i] as u32) << 16;
out.push(TABLE[((chunk >> 18) & 0x3F) as usize] as char);
out.push(TABLE[((chunk >> 12) & 0x3F) as usize] as char);
out.push('=');
out.push('=');
}
2 => {
let chunk = ((bytes[i] as u32) << 16) | ((bytes[i + 1] as u32) << 8);
out.push(TABLE[((chunk >> 18) & 0x3F) as usize] as char);
out.push(TABLE[((chunk >> 12) & 0x3F) as usize] as char);
out.push(TABLE[((chunk >> 6) & 0x3F) as usize] as char);
out.push('=');
}
_ => {}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rfc4648_vectors() {
assert_eq!(encode(b""), "");
assert_eq!(encode(b"f"), "Zg==");
assert_eq!(encode(b"fo"), "Zm8=");
assert_eq!(encode(b"foo"), "Zm9v");
assert_eq!(encode(b"foob"), "Zm9vYg==");
assert_eq!(encode(b"fooba"), "Zm9vYmE=");
assert_eq!(encode(b"foobar"), "Zm9vYmFy");
}
#[test]
fn non_utf8_input() {
assert_eq!(encode(b"\xFF\x00\x80"), "/wCA");
}
}