const ALPHABET: [char; 64] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
];
fn get_hex_char(b: u8) -> char {
match b {
0 => '0',
1 => '1',
2 => '2',
3 => '3',
4 => '4',
5 => '5',
6 => '6',
7 => '7',
8 => '8',
9 => '9',
10 => 'A',
11 => 'B',
12 => 'C',
13 => 'D',
14 => 'E',
15 => 'F',
_ => panic!("Too large: {}\t- Must be <= 15.")
}
}
fn get_num_from_hex(c: char) -> u8 {
match c {
'0' => 0,
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
'A' | 'a' => 10,
'B' | 'b' => 11,
'C' | 'c' => 12,
'D' | 'd' => 13,
'E' | 'e' => 14,
'F' | 'f' => 15,
_ => panic!("Letter must be [0-9A-Fa-f]: {}", c)
}
}
pub fn base64_encode(bytes: Vec<u8>) -> String {
let mut encoded = String::new();
let mut iter = bytes.into_iter();
loop {
let a = iter.next();
let b = iter.next();
let c = iter.next();
let p: usize;
let mut q: usize;
let mut r: usize;
let s: usize;
match a {
Some(n) => {
p = ((n & 0b11111100) >> 2) as usize;
q = ((n & 0b00000011) << 4) as usize;
encoded.push(ALPHABET[p]);
}
None => break
}
match b {
Some(n) => {
q |= ((n & 0b11110000) >> 4) as usize;
r = ((n & 0b00001111) << 2) as usize;
encoded.push(ALPHABET[q]);
}
None => {
encoded.push(ALPHABET[q]);
encoded.push_str("==");
break;
}
}
match c {
Some(n) => {
r |= ((n & 0b11000000) >> 6) as usize;
s = (n & 0b00111111) as usize;
encoded.push(ALPHABET[r]);
encoded.push(ALPHABET[s]);
}
None => {
encoded.push(ALPHABET[r]);
encoded.push('=');
break;
}
}
}
encoded
}
pub fn base64_decode(enc: String) -> Vec<u8> {
assert!(enc.len() % 4 == 0, "Base 64 Encoding must be multiple of four.");
let mut dec: Vec<u8> = Vec::new();
let chars = enc.chars();
let mut nums = chars.map(|x| ALPHABET.iter().position(|y| *y == x))
.collect::<Vec<Option<usize>>>().into_iter();
loop {
let p = nums.next().unwrap_or(None);
let q = nums.next().unwrap_or(None);
let r = nums.next().unwrap_or(None);
let s = nums.next().unwrap_or(None);
let mut a: u8;
let mut b: u8;
let mut c: u8;
match p {
Some(n) => {
a = (n << 2) as u8;
}
None => break
}
match q {
Some(n) => {
a |= ((n & 0b00110000) >> 4) as u8;
b = ((n & 0b00001111) << 4) as u8;
dec.push(a);
}
None => break
}
match r {
Some(n) => {
b |= ((n & 0b00111100) >> 2) as u8;
c = ((n & 0b00000011) << 6) as u8;
dec.push(b);
}
None => break
}
match s {
Some(n) => {
c |= (n & 0b00111111) as u8;
dec.push(c);
}
None => break
}
}
dec
}
pub fn hex_encode(bytes: Vec<u8>) -> String {
let mut enc = String::new();
for b in bytes {
enc.push(get_hex_char((b & 0xF0) >> 4));
enc.push(get_hex_char(b & 0x0F));
}
enc
}
pub fn hex_decode(hex: String) -> Vec<u8> {
assert!(hex.len() % 2 == 0, "Hex must be multiple of two.");
let mut nums: Vec<u8> = Vec::new();
let mut chars = hex.chars();
loop {
let a = chars.next();
let b = chars.next();
if a.is_none() {
break;
}
nums.push((get_num_from_hex(a.unwrap()) << 4) | get_num_from_hex(b.unwrap()));
}
nums
}
#[test]
fn test_base64_encode() {
let v: Vec<u8> = "this is a test sentence".to_string().into_bytes();
assert!(base64_encode(v) == "dGhpcyBpcyBhIHRlc3Qgc2VudGVuY2U=");
}
#[test]
fn test_base64_decode() {
let s: String = "dGhpcyBpcyBhIHRlc3Qgc2VudGVuY2U=".to_string();
assert!(String::from_utf8(base64_decode(s)).expect("Couln't create string.") ==
"this is a test sentence".to_string());
}
#[test]
fn test_hex_encode() {
let v: Vec<u8> = vec![0x3a, 0xf1, 0x8c, 0xe0];
assert!(hex_encode(v) == "3AF18CE0".to_string());
}
#[test]
fn test_hex_decode() {
let s = "3A8CCEF001".to_string();
let v: Vec<u8> = vec![0x3a, 0x8c, 0xce, 0xf0, 0x01];
assert!(hex_decode(s) == v);
}
#[test]
fn matasano() {
let vec = hex_decode("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d".to_string());
let b64 = base64_encode(vec);
assert!(b64 == "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t".to_string())
}