#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
use std::io;
const SHIFT: u8 = 5;
const MASK: u8 = (1 << SHIFT) - 1;
const CONTINUED: u8 = 1 << SHIFT;
#[derive(Debug)]
pub enum Error {
UnexpectedEof,
InvalidBase64(u8),
}
pub type Result<T> = std::result::Result<T, Error>;
fn decode64(input: u8) -> Result<u8> {
match input {
b'A'...b'Z' => Ok(input - b'A'),
b'a'...b'z' => Ok(input - b'a' + 26),
b'0'...b'9' => Ok(input - b'0' + 52),
b'+' => Ok(62),
b'/' => Ok(63),
_ => Err(Error::InvalidBase64(input)),
}
}
pub fn decode<B>(input: &mut B) -> Result<i64>
where
B: Iterator<Item = u8>,
{
let mut accum = 0;
let mut shift = 0;
let mut keep_going = true;
while keep_going {
let byte = input.next().ok_or(Error::UnexpectedEof)?;
let digit = decode64(byte)?;
keep_going = (digit & CONTINUED) != 0;
accum += ((digit & MASK) as u64) << shift;
shift += SHIFT;
}
let negate = (accum & 1) != 0;
accum >>= 1;
if negate {
accum = accum.wrapping_neg();
}
Ok(accum as i64)
}
fn encode64(value: u8) -> u8 {
debug_assert!(value < 64);
if value < 26 {
value + b'A'
} else if value < 52 {
value - 26 + b'a'
} else if value < 62 {
value - 52 + b'0'
} else if value == 62 {
b'+'
} else {
assert!(value == 63);
b'/'
}
}
pub fn encode<W>(value: i64, output: &mut W) -> io::Result<()>
where
W: io::Write,
{
let signed = value < 0;
let mut value = (value.wrapping_abs() as u64) << 1;
if signed {
value |= 1;
}
loop {
let mut digit = value as u8 & MASK;
value >>= SHIFT;
if value > 0 {
digit |= CONTINUED;
}
let bytes = [encode64(digit)];
output.write_all(&bytes[..])?;
if value == 0 {
break;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
fn decode_tester_ok(input: &[u8], expect: i64) {
let mut input = input.iter().cloned();
match ::decode(&mut input) {
Ok(x) => {
assert_eq!(x, expect);
assert!(input.next().is_none());
}
_ => assert!(false),
}
}
#[test]
fn test_decode() {
decode_tester_ok("A".as_bytes(), 0);
decode_tester_ok("B".as_bytes(), 0);
decode_tester_ok("C".as_bytes(), 1);
decode_tester_ok("D".as_bytes(), -1);
}
#[test]
fn test_roundtrip() {
for val in -512..512 {
let mut buf = Vec::<u8>::new();
match ::encode(val, &mut buf) {
Ok(()) => assert!(buf.len() > 0),
_ => assert!(false),
}
decode_tester_ok(&buf, val);
}
}
}