100 lines
3.1 KiB
Rust
100 lines
3.1 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 {
|
|
let BytesBase64(digits) = self;
|
|
let mut r: Vec<u8> = digits
|
|
.iter()
|
|
.map(|d| match d {
|
|
0..=25 => *d + b'A',
|
|
26..=51 => *d - 26 + b'a',
|
|
52..=61 => *d - 52 + b'0',
|
|
62 => b'+',
|
|
63 => b'/',
|
|
_ => panic!("Unexpected base64 digit '{}'", d),
|
|
})
|
|
.collect();
|
|
// Handle padding
|
|
let pad = b'=';
|
|
match r.len() % 4 {
|
|
0 => (),
|
|
2 => {
|
|
r.push(pad);
|
|
r.push(pad);
|
|
}
|
|
3 => r.push(pad),
|
|
_ => panic!("Unexpected lenght for padding '{}'", r.len()),
|
|
}
|
|
let s = str::from_utf8(r.as_slice()).unwrap().to_string();
|
|
write!(f, "{}", s)
|
|
}
|
|
}
|
|
|
|
impl BytesBase64 {
|
|
pub fn from_bytes(Bytes(bytes): Bytes) -> BytesBase64 {
|
|
fn to_base64(c: &[u8]) -> Vec<u8> {
|
|
let mut v = c.to_vec();
|
|
// pad with bytes for conversion
|
|
while v.len() < 3 {
|
|
v.push(0);
|
|
}
|
|
let mut result = vec![
|
|
(v[0] & 0b1111_1100) >> 2,
|
|
(v[0] & 0b0000_0011) << 4 | (v[1] & 0b1111_0000) >> 4,
|
|
(v[1] & 0b0000_1111) << 2 | (v[2] & 0b1100_0000) >> 6,
|
|
(v[2] & 0b0011_1111),
|
|
];
|
|
// removed padded bytes
|
|
for _ in c.len()..3 {
|
|
result.pop();
|
|
}
|
|
result
|
|
}
|
|
BytesBase64(bytes.chunks(3).flat_map(to_base64).collect())
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> Bytes {
|
|
fn to_bytes(c: &[u8]) -> Vec<u8> {
|
|
let mut v = c.to_vec();
|
|
// pad with bytes for conversion
|
|
while v.len() < 4 {
|
|
v.push(0);
|
|
}
|
|
let mut result: Vec<u8> = vec![
|
|
((v[0] & 0b0011_1111) << 2) | ((v[1] & 0b0011_0000) >> 4),
|
|
((v[1] & 0b0000_1111) << 4) | ((v[2] & 0b0011_1100) >> 2),
|
|
((v[2] & 0b0000_0011) << 6) | (v[3] & 0b0011_1111),
|
|
];
|
|
// removed padded bytes
|
|
for _ in c.len()..4 {
|
|
result.pop();
|
|
}
|
|
result
|
|
}
|
|
let BytesBase64(v) = self;
|
|
Bytes(v.chunks(4).flat_map(to_bytes).collect())
|
|
}
|
|
|
|
pub fn from_base64(s: &str) -> Result<BytesBase64, String> {
|
|
let mut r: Vec<u8> = Vec::with_capacity(s.len());
|
|
for c in s.chars() {
|
|
match c {
|
|
'A'..='Z' => r.push((c as u8) - b'A'),
|
|
'a'..='z' => r.push((c as u8) - b'a' + 26),
|
|
'0'..='9' => r.push((c as u8) - b'0' + 52),
|
|
'+' => r.push(62),
|
|
'/' => r.push(63),
|
|
'\n' | '=' | ' ' => (),
|
|
_ => {
|
|
return Err(format!("Unexpected character '{}'", c));
|
|
}
|
|
}
|
|
}
|
|
Ok(BytesBase64(r))
|
|
}
|
|
}
|