63 lines
1.8 KiB
Rust
63 lines
1.8 KiB
Rust
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_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()
|
|
}
|
|
}
|