1use thiserror::Error as DeriveError;
5
6#[derive(Debug, DeriveError)]
7#[error("Base64 Error")]
8pub struct Base64Error;
9
10pub struct Base64;
12impl Base64 {
13 const PADDING: u8 = b'=';
15
16 pub fn encode_data(data: &[u8]) -> String {
18 let mut base = Vec::new();
20 for chunk in data.chunks(3) {
21 let num: usize = [16, 8, 0]
22 .iter()
23 .zip(chunk.iter())
24 .fold(0, |acc, (s, b)| acc + ((*b as usize) << *s));
25 [18usize, 12, 6, 0]
26 .iter()
27 .map(|s| (num >> s) & 0b0011_1111)
28 .for_each(|b| base.push(Self::encode_byte(b)));
29 }
30
31 let to_pad = match data.len() % 3 {
33 2 => 1,
34 1 => 2,
35 _ => 0,
36 };
37 base.iter_mut().rev().take(to_pad).for_each(|b| *b = Self::PADDING);
38
39 match String::from_utf8(base) {
40 Ok(s) => s,
41 Err(e) => {
42 let error = e.utf8_error();
43 let valid_up_to = error.valid_up_to();
44 let error_msg = format!("failure encoding to base64: valid_up_to({})", valid_up_to);
45 panic!("{}", error_msg)
46 }
47 }
48 }
49
50 pub fn decode_data(base: &[u8]) -> Result<Vec<u8>, Base64Error> {
52 let (padded, base) = match base.iter().rev().take_while(|b| **b == Self::PADDING).count() {
54 _ if base.len() % 4 != 0 => return Err(Base64Error),
55 padded if padded > 2 => return Err(Base64Error),
56 padded => (padded, &base[..base.len() - padded]),
57 };
58
59 let mut data = Vec::new();
61 for chunk in base.chunks(4) {
62 let num: usize = [18usize, 12, 6, 0]
63 .iter()
64 .zip(chunk.iter())
65 .try_fold(0, |acc, (s, b)| Self::decode_byte(*b).map(|b| acc + (b << *s)))?;
66 [16, 8, 0].iter().map(|s| (num >> s) as u8).for_each(|b| data.push(b));
67 }
68
69 data.truncate(data.len() - padded);
71 Ok(data)
72 }
73
74 fn encode_byte(b: usize) -> u8 {
76 match b {
77 b @ 0..=25 => (b as u8) + b'A',
78 b @ 26..=51 => (b as u8 - 26) + b'a',
79 b @ 52..=61 => (b as u8 - 52) + b'0',
80 62 => b'-',
81 63 => b'_',
82 _ => panic!("{:?} ({})", Base64Error, b),
83 }
84 }
85
86 fn decode_byte(b: u8) -> Result<usize, Base64Error> {
88 match b {
89 b @ b'A'..=b'Z' => Ok((b - b'A') as usize),
90 b @ b'a'..=b'z' => Ok((b - b'a') as usize + 26),
91 b @ b'0'..=b'9' => Ok((b - b'0') as usize + 52),
92 b'-' => Ok(62),
93 b'_' => Ok(63),
94 _ => Err(Base64Error),
95 }
96 }
97}
98
99pub trait Base64Encodable {
101 fn base64(&self) -> String;
102}
103
104pub trait Base64Decodable: Sized {
106 type Error;
107 fn from_base64(base: impl AsRef<[u8]>) -> Result<Self, Self::Error>;
108}
109
110impl<T: AsRef<[u8]>> Base64Encodable for T {
111 fn base64(&self) -> String {
112 Base64::encode_data(self.as_ref())
113 }
114}
115
116impl Base64Decodable for Vec<u8> {
117 type Error = Base64Error;
118 fn from_base64(base: impl AsRef<[u8]>) -> Result<Self, Self::Error> {
119 Base64::decode_data(base.as_ref())
120 }
121}