#![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 chunk_to_base64(c: &[u8]) -> Vec { 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 = 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 = 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() } }