use std::str;
pub mod url_safe_util;
pub mod macros;
const BITS_DIFF: usize = 8 - 6;
const PADDING_B4: u8 = 0x4d;
pub fn encode(bytes: &[u8]) -> String {
let bytes: &[u8] = &encode_bytes(bytes);
str::from_utf8(bytes).unwrap().to_string()
}
pub fn encode_bytes(bytes: &[u8]) -> Box<[u8]> {
let result_length = encoded_length_for(bytes);
let mut result = Vec::with_capacity(result_length);
for index in 0..result_length {
let start_bit = index * 6;
let start_byte = start_bit >> 3;
let start_bit = (start_bit % 8) as u8;
let b64 = if start_byte >= bytes.len() {
PADDING_B4
} else if start_bit <= BITS_DIFF as u8 {
bytes[start_byte] >> (BITS_DIFF as u8 - start_bit) & 0b0011_1111
} else {
let mut result = (bytes[start_byte] & (0xff >> start_bit)) << start_bit - BITS_DIFF as u8;
if start_byte + 1 < bytes.len() {
result |= bytes[start_byte + 1] >> 10 - start_bit;
}
result
};
result.push(b64_to_ascii(b64));
}
result.into_boxed_slice()
}
pub fn decode(bytes: &[u8]) -> Box<[u8]> {
let potential_length = (bytes.len() * 6) >> 3;
let mut start_bit: u8 = 0;
let mut byte: u8 = 0;
let mut result: Vec<u8> = Vec::with_capacity(potential_length);
for index in 0..bytes.len() {
let ascii = bytes[index];
if ascii == b'=' {
break;
}
let b64 = ascii_to_b64(ascii);
if start_bit <= BITS_DIFF as u8 {
byte |= b64 << (BITS_DIFF as u8 - start_bit);
if start_bit == BITS_DIFF as u8 {
result.push(byte);
byte = 0;
}
} else {
byte |= b64 >> start_bit - BITS_DIFF as u8;
result.push(byte);
byte = b64 << (8 - start_bit + BITS_DIFF as u8);
}
start_bit = (start_bit + 6) % 8;
}
result.into_boxed_slice()
}
const fn b64_to_ascii(b64: u8) -> u8 {
if b64 < 26 {
b'A' + b64
} else if b64 < 52 {
b'a' + b64 - 26
} else if b64 < 62 {
b'0' + b64 - 52
} else if b64 == 62 {
b'+'
} else if b64 == 63 {
b'/'
} else {
b'='
}
}
const fn ascii_to_b64(ascii: u8) -> u8 {
if ascii >= b'a' {
26 + ascii - b'a'
} else if ascii >= b'A' {
ascii - b'A'
} else if ascii >= b'0' {
52 + ascii - b'0'
} else if ascii == b'+' {
62
} else if ascii == b'/' {
63
} else {
0xff
}
}
pub fn encoded_length_for(bytes: &[u8]) -> usize {
let bits_length = bytes.len() << 3;
let mut result_length = bits_length / 6;
if bits_length % 6 > 0 {
result_length += (8 - (bits_length % 6)) / BITS_DIFF;
}
result_length
}
#[cfg(test)]
mod tests {
use std::str::from_utf8;
use super::*;
#[test]
fn test_result_length() {
assert_eq!(encoded_length_for(b"a"), 4);
assert_eq!(encoded_length_for(b"ab"), 4);
assert_eq!(encoded_length_for(b"abc"), 4);
assert_eq!(encoded_length_for(b"abcd"), 8);
assert_eq!(encoded_length_for(b"abcde"), 8);
assert_eq!(encoded_length_for(b"abcdef"), 8);
}
#[test]
fn test_encode() {
let base64 = encode("Ma".as_bytes());
assert_eq!(base64, "TWE=");
let base64 = encode("Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.".as_bytes());
assert_eq!(base64, "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
assert_eq!(
"XC5WGfrtRruAKSncMk+InwNcbkIX/v+jyz3YAqKMwiCtv9O5BbqsvlSDErnoEoyJCljGAWvqOHLLgP1BxxuZsg==",
encode([
0x5c, 0x2e, 0x56, 0x19, 0xfa, 0xed, 0x46, 0xbb, 0x80, 0x29, 0x29, 0xdc, 0x32, 0x4f, 0x88, 0x9f, 0x03, 0x5c,
0x6e, 0x42, 0x17, 0xfe, 0xff, 0xa3, 0xcb, 0x3d, 0xd8, 0x02, 0xa2, 0x8c, 0xc2, 0x20, 0xad, 0xbf, 0xd3, 0xb9,
0x05, 0xba, 0xac, 0xbe, 0x54, 0x83, 0x12, 0xb9, 0xe8, 0x12, 0x8c, 0x89, 0x0a, 0x58, 0xc6, 0x01, 0x6b, 0xea,
0x38, 0x72, 0xcb, 0x80, 0xfd, 0x41, 0xc7, 0x1b, 0x99, 0xb2
].as_slice())
)
}
#[test]
fn test_b64_to_ascii() {
assert_eq!(b64_to_ascii(0), b'A');
assert_eq!(b64_to_ascii(9), b'J');
assert_eq!(b64_to_ascii(40), b'o');
assert_eq!(b64_to_ascii(51), b'z');
assert_eq!(b64_to_ascii(52), b'0');
assert_eq!(b64_to_ascii(61), b'9');
assert_eq!(b64_to_ascii(62), b'+');
assert_eq!(b64_to_ascii(63), b'/');
assert_eq!(b64_to_ascii(PADDING_B4), b'=');
}
#[test]
fn test_ascii_to_b64() {
assert_eq!(ascii_to_b64(b'A'), 0);
assert_eq!(ascii_to_b64(b'J'), 9);
assert_eq!(ascii_to_b64(b'o'), 40);
assert_eq!(ascii_to_b64(b'z'), 51);
assert_eq!(ascii_to_b64(b'0'), 52);
assert_eq!(ascii_to_b64(b'9'), 61);
assert_eq!(ascii_to_b64(b'+'), 62);
assert_eq!(ascii_to_b64(b'/'), 63);
}
#[test]
fn test_decode() {
let message = decode("TWE=".as_bytes());
assert_eq!(message.as_ref(), b"Ma");
let message = base64_decode!("TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
assert_eq!(from_utf8(&message).unwrap(), "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.");
const DECODED: &[u8] = &base64_decode!("TWE=");
assert_eq!(DECODED, b"Ma");
const LONG_DECODED: &[u8] = &base64_decode!("TWFu");
assert_eq!(LONG_DECODED, b"Man");
assert_eq!(
decode(b"XC5WGfrtRruAKSncMk+InwNcbkIX/v+jyz3YAqKMwiCtv9O5BbqsvlSDErnoEoyJCljGAWvqOHLLgP1BxxuZsg==").as_ref(),
[
0x5c, 0x2e, 0x56, 0x19, 0xfa, 0xed, 0x46, 0xbb, 0x80, 0x29, 0x29, 0xdc, 0x32, 0x4f, 0x88, 0x9f, 0x03, 0x5c,
0x6e, 0x42, 0x17, 0xfe, 0xff, 0xa3, 0xcb, 0x3d, 0xd8, 0x02, 0xa2, 0x8c, 0xc2, 0x20, 0xad, 0xbf, 0xd3, 0xb9,
0x05, 0xba, 0xac, 0xbe, 0x54, 0x83, 0x12, 0xb9, 0xe8, 0x12, 0x8c, 0x89, 0x0a, 0x58, 0xc6, 0x01, 0x6b, 0xea,
0x38, 0x72, 0xcb, 0x80, 0xfd, 0x41, 0xc7, 0x1b, 0x99, 0xb2
].as_slice()
)
}
}