const BASE_64_ALPHABET: [u8; 64] = [
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z',
b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
b'+', b'/'
];
const BASE_64_MASK: u8 = 0x3F;
pub fn base64(input: &str) -> Vec<u8> {
let padded_input = match input.len() % 4 {
0 => {
String::from(input)
},
1 => {
panic!("bad input");
},
2 => {
let mut s = String::from(input);
s.push_str("==");
s
},
3 => {
let mut s = String::from(input);
s.push_str("=");
s
},
_ => {
unreachable!("mod 4");
}
};
padded_input.as_bytes().chunks(4).map(|x| {
if x[2] == b'=' && x[3] == b'=' {
let buf: u32 =
(((lookup(x[0]).unwrap() & BASE_64_MASK) as u32) << 18) +
(((lookup(x[1]).unwrap() & BASE_64_MASK) as u32) << 12);
vec![
(buf >> 16) as u8
]
}
else if x[3] == b'=' {
let buf: u32 =
(((lookup(x[0]).unwrap() & BASE_64_MASK) as u32) << 18) +
(((lookup(x[1]).unwrap() & BASE_64_MASK) as u32) << 12) +
(((lookup(x[2]).unwrap() & BASE_64_MASK) as u32) << 6);
vec![
(buf >> 16) as u8,
(buf >> 8) as u8
]
}
else {
let buf: u32 =
(((lookup(x[0]).unwrap() & BASE_64_MASK) as u32) << 18) +
(((lookup(x[1]).unwrap() & BASE_64_MASK) as u32) << 12) +
(((lookup(x[2]).unwrap() & BASE_64_MASK) as u32) << 6) +
((lookup(x[3]).unwrap() & BASE_64_MASK) as u32);
vec![
(buf >> 16) as u8,
(buf >> 8) as u8,
buf as u8
]
}
}).fold(Vec::new(), |mut acc, x| {
acc.extend(x);
acc
})
}
fn lookup(b: u8) -> Result<u8, ()> {
for (i, byte) in BASE_64_ALPHABET.iter().enumerate() {
if byte == &b {
return Ok(i as u8)
}
}
Err(())
}