Files
cryptopals/src/bytes_base64.rs
2022-03-26 13:40:28 -04:00

83 lines
2.4 KiB
Rust

#![allow(dead_code)]
use crate::bytes::Bytes;
use std::str;
pub struct BytesBase64(pub Vec<u8>);
impl std::fmt::Display for BytesBase64 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Base64({})", self.to_string())
}
}
impl BytesBase64 {
pub fn from_bytes(Bytes(bytes): Bytes) -> BytesBase64 {
fn chunk_to_base64(c: &[u8]) -> Vec<u8> {
let (value, iterations) = match c.len() {
0 => return vec![],
1 => ((c[0] as u32) << 16, 2),
2 => ((c[0] as u32) << 16 | (c[1] as u32) << 8, 3),
3 => ((c[0] as u32) << 16 | (c[1] as u32) << 8 | (c[2] as u32), 4),
_ => panic!("Unexpected number of chunks {}.", c.len()),
};
(0..iterations)
.map(|i| (value.rotate_right((3 - i) * 6) & 0b111111) as u8)
.collect()
}
BytesBase64(
bytes
.chunks(3)
.map(|c| chunk_to_base64(c))
.flatten()
.collect(),
)
}
pub fn to_bytes(&self) -> Bytes {
Bytes(vec![])
}
pub fn from_base64(s: &str) -> BytesBase64 {
let mut r: Vec<u8> = Vec::with_capacity(s.len());
for c in s.chars() {
match c {
'A'..='Z' => r.push((c as u8) - ('A' as u8)),
'a'..='z' => r.push((c as u8) - ('a' as u8) + 26),
'0'..='9' => r.push((c as u8) - ('0' as u8) + 52),
'+' => r.push(62),
'/' => r.push(63),
_ => (),
}
}
BytesBase64(r)
}
pub fn to_string(&self) -> String {
let BytesBase64(digits) = self;
let mut r: Vec<u8> = digits
.iter()
.map(|d| match d {
0..=25 => *d + ('A' as u8),
26..=51 => *d - 26 + ('a' as u8),
52..=61 => *d - 52 + ('0' as u8),
62 => '+' as u8,
63 => '/' as u8,
_ => panic!("Unexpected base64 digit '{}'", d),
})
.collect();
// Handle padding
let pad = '=' as u8;
match r.len() % 4 {
0 => (),
2 => {
r.push(pad);
r.push(pad);
}
3 => r.push(pad),
_ => (),
}
str::from_utf8(r.as_slice()).unwrap().to_string()
}
}