#![allow(dead_code)] use crate::bytes::Bytes; use std::str; pub struct BytesBase64(pub Vec); 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 to_base64(c: &[u8]) -> Vec { let mut v = c.to_vec(); // pad with bytes for conversion while v.len() < 3 { v.push(0); } let mut result = vec![ (v[0] & 0b11111100) >> 2, (v[0] & 0b00000011) << 4 | (v[1] & 0b11110000) >> 4, (v[1] & 0b00001111) << 2 | (v[2] & 0b11000000) >> 6, (v[2] & 0b00111111) << 0, ]; // removed padded bytes for _ in c.len()..3 { result.pop(); } result } BytesBase64(bytes.chunks(3).map(|c| to_base64(c)).flatten().collect()) } pub fn to_bytes(&self) -> Bytes { let BytesBase64(v) = self; fn to_bytes(c: &[u8]) -> Vec { let mut v = c.to_vec(); // pad with bytes for conversion while v.len() < 4 { v.push(0); } let mut result: Vec = vec![ ((v[0] & 0b00111111) << 2) | ((v[1] & 0b00110000) >> 4), ((v[1] & 0b00001111) << 4) | ((v[2] & 0b00111100) >> 2), ((v[2] & 0b00000011) << 6) | ((v[3] & 0b00111111) >> 0), ]; // removed padded bytes for _ in c.len()..4 { result.pop(); } result } Bytes(v.chunks(4).map(|c| to_bytes(c)).flatten().collect()) } pub fn from_base64(s: &str) -> Result { let mut r: Vec = 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), '\n' => (), '=' => (), ' ' => (), _ => { return Err(format!("Unexpected character '{}'", c)); } } } Ok(BytesBase64(r)) } pub fn to_string(&self) -> String { let BytesBase64(digits) = self; let mut r: Vec = 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), _ => panic!("Unexpected lenght for padding '{}'", r.len()), } str::from_utf8(r.as_slice()).unwrap().to_string() } }