struct B64 {
table: Vec<char>,
}
impl B64 {
fn new() -> B64 {
B64{table: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".chars().collect()}
}
fn encode(&self, s: &str) -> String {
let mut bs: Vec<bool> = s.as_bytes().iter()
.flat_map(|b| (0..8).map(move |i| b >> (7 - i) & 1u8 != 0u8)).collect();
while 0 < bs.len() % 6 {bs.push(false)}
let mut cs: Vec<char> = bs.chunks(6)
.map(|b6| b6.iter().enumerate().fold(0, |acc, (i, b)| acc + ((*b as u8) << (5 - i))))
.map(|u| self.table[u as usize]).collect();
while 0 < cs.len() % 4 {cs.push('=')}
cs.into_iter().collect()
}
fn decode(&self, s: &str) -> String {
let us: Vec<u8> = s.chars().take_while(|d| *d != '=')
.flat_map(|c| self.table.iter().position(|e| *e == c))
.flat_map(|b| (0..6).map(move |i| b as u8 >> (5 - i) & 1u8 != 0u8))
.collect::<Vec<bool>>().chunks(8).filter(|b8| b8.len() == 8)
.map(|b8| b8.iter().enumerate().fold(0, |acc, (i, b)| acc + ((*b as u8) << (7 - i))))
.collect();
String::from_utf8(us).unwrap_or(String::new())
}
}
fn main() {
let b64 = B64::new();
let p = |s| {
let e = b64.encode(s);
let d = b64.decode(e.as_str());
println!("{}\n{}\n{}\n", s, e, d);
};
p("ABCDEFG");
p("Hello, World!");
p("0123456789\"#$%&'()`=@");
p("💖");
}
c3RydWN0IEI2NCB7CiAgICB0YWJsZTogVmVjPGNoYXI+LAp9CmltcGwgQjY0IHsKICAgIGZuIG5ldygpIC0+IEI2NCB7CiAgICAgICAgQjY0e3RhYmxlOiAiQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLyIuY2hhcnMoKS5jb2xsZWN0KCl9CiAgICB9CiAgICBmbiBlbmNvZGUoJnNlbGYsIHM6ICZzdHIpIC0+IFN0cmluZyB7CiAgICAgICAgbGV0IG11dCBiczogVmVjPGJvb2w+ID0gcy5hc19ieXRlcygpLml0ZXIoKQogICAgICAgICAgICAuZmxhdF9tYXAofGJ8ICgwLi44KS5tYXAobW92ZSB8aXwgYiA+PiAoNyAtIGkpICYgMXU4ICE9IDB1OCkpLmNvbGxlY3QoKTsKICAgICAgICB3aGlsZSAwIDwgYnMubGVuKCkgJSA2IHticy5wdXNoKGZhbHNlKX0KICAgICAgICBsZXQgbXV0IGNzOiBWZWM8Y2hhcj4gPSBicy5jaHVua3MoNikKICAgICAgICAgICAgLm1hcCh8YjZ8IGI2Lml0ZXIoKS5lbnVtZXJhdGUoKS5mb2xkKDAsIHxhY2MsIChpLCBiKXwgYWNjICsgKCgqYiBhcyB1OCkgPDwgKDUgLSBpKSkpKQogICAgICAgICAgICAubWFwKHx1fCBzZWxmLnRhYmxlW3UgYXMgdXNpemVdKS5jb2xsZWN0KCk7CiAgICAgICAgd2hpbGUgMCA8IGNzLmxlbigpICUgNCB7Y3MucHVzaCgnPScpfQogICAgICAgIGNzLmludG9faXRlcigpLmNvbGxlY3QoKQogICAgfQogICAgZm4gZGVjb2RlKCZzZWxmLCBzOiAmc3RyKSAtPiBTdHJpbmcgewogICAgICAgIGxldCB1czogVmVjPHU4PiA9IHMuY2hhcnMoKS50YWtlX3doaWxlKHxkfCAqZCAhPSAnPScpCiAgICAgICAgICAgIC5mbGF0X21hcCh8Y3wgc2VsZi50YWJsZS5pdGVyKCkucG9zaXRpb24ofGV8ICplID09IGMpKQogICAgICAgICAgICAuZmxhdF9tYXAofGJ8ICgwLi42KS5tYXAobW92ZSB8aXwgYiBhcyB1OCA+PiAoNSAtIGkpICYgMXU4ICE9IDB1OCkpCiAgICAgICAgICAgIC5jb2xsZWN0Ojo8VmVjPGJvb2w+PigpLmNodW5rcyg4KS5maWx0ZXIofGI4fCBiOC5sZW4oKSA9PSA4KQogICAgICAgICAgICAubWFwKHxiOHwgYjguaXRlcigpLmVudW1lcmF0ZSgpLmZvbGQoMCwgfGFjYywgKGksIGIpfCBhY2MgKyAoKCpiIGFzIHU4KSA8PCAoNyAtIGkpKSkpCiAgICAgICAgICAgIC5jb2xsZWN0KCk7CiAgICAgICAgU3RyaW5nOjpmcm9tX3V0ZjgodXMpLnVud3JhcF9vcihTdHJpbmc6Om5ldygpKQogICAgfQp9CmZuIG1haW4oKSB7CiAgICBsZXQgYjY0ID0gQjY0OjpuZXcoKTsKICAgIGxldCBwID0gfHN8IHsKICAgICAgICBsZXQgZSA9IGI2NC5lbmNvZGUocyk7CiAgICAgICAgbGV0IGQgPSBiNjQuZGVjb2RlKGUuYXNfc3RyKCkpOwogICAgICAgIHByaW50bG4hKCJ7fVxue31cbnt9XG4iLCBzLCBlLCBkKTsKICAgIH07CiAgICBwKCJBQkNERUZHIik7CiAgICBwKCJIZWxsbywgV29ybGQhIik7CiAgICBwKCIwMTIzNDU2Nzg5XCIjJCUmJygpYD1AIik7CiAgICBwKCLwn5KWIik7Cn0=