rust_web_server/core/base64/
decode.rs1use std::fmt;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum Alphabet {
14 Standard,
16 UrlSafe,
18 Either,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub struct Decoder {
35 alphabet: Alphabet,
36 whitespace: bool,
37 skip_invalid: bool,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct DecodeError {
43 pub position: usize,
46 reason: &'static str,
47}
48
49impl fmt::Display for DecodeError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "{} at byte {}", self.reason, self.position)
52 }
53}
54
55impl std::error::Error for DecodeError {}
56
57impl From<DecodeError> for String {
58 fn from(e: DecodeError) -> String {
59 e.to_string()
60 }
61}
62
63const INVALID: u8 = 0xff;
64
65const fn table(alphabet: Alphabet) -> [u8; 256] {
66 let mut t = [INVALID; 256];
67 let mut i = 0;
68 while i < 26 {
69 t[b'A' as usize + i] = i as u8;
70 t[b'a' as usize + i] = 26 + i as u8;
71 i += 1;
72 }
73 let mut d = 0;
74 while d < 10 {
75 t[b'0' as usize + d] = 52 + d as u8;
76 d += 1;
77 }
78 if !matches!(alphabet, Alphabet::UrlSafe) {
79 t[b'+' as usize] = 62;
80 t[b'/' as usize] = 63;
81 }
82 if !matches!(alphabet, Alphabet::Standard) {
83 t[b'-' as usize] = 62;
84 t[b'_' as usize] = 63;
85 }
86 t
87}
88
89static STANDARD: [u8; 256] = table(Alphabet::Standard);
90static URL_SAFE: [u8; 256] = table(Alphabet::UrlSafe);
91static EITHER: [u8; 256] = table(Alphabet::Either);
92
93impl Decoder {
94 pub const STANDARD: Decoder = Decoder::new(Alphabet::Standard);
96 pub const URL_SAFE: Decoder = Decoder::new(Alphabet::UrlSafe);
98 pub const EITHER: Decoder = Decoder::new(Alphabet::Either);
100
101 pub const fn new(alphabet: Alphabet) -> Decoder {
103 Decoder { alphabet, whitespace: false, skip_invalid: false }
104 }
105
106 pub const fn skip_whitespace(self) -> Decoder {
108 Decoder { whitespace: true, ..self }
109 }
110
111 pub(crate) const fn skip_invalid(self) -> Decoder {
114 Decoder { whitespace: true, skip_invalid: true, ..self }
115 }
116
117 pub fn decode(&self, input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
119 let input = input.as_ref();
120 let table = match self.alphabet {
121 Alphabet::Standard => &STANDARD,
122 Alphabet::UrlSafe => &URL_SAFE,
123 Alphabet::Either => &EITHER,
124 };
125 let mut out = Vec::with_capacity(input.len() / 4 * 3 + 3);
126 let mut quantum = 0u32;
128 let mut n = 0u32;
129 let mut pads_left = 0u32;
131 let mut padding = false;
132 for (i, &c) in input.iter().enumerate() {
133 let v = table[c as usize];
134 if v != INVALID {
135 padding = false;
136 quantum = quantum << 6 | v as u32;
137 n += 1;
138 if n == 4 {
139 out.extend_from_slice(&[(quantum >> 16) as u8, (quantum >> 8) as u8, quantum as u8]);
140 quantum = 0;
141 n = 0;
142 }
143 continue;
144 }
145 if c == b'=' {
146 if self.skip_invalid {
147 continue;
148 }
149 if padding {
150 if pads_left == 0 {
151 return Err(DecodeError { position: i, reason: "too much padding" });
152 }
153 pads_left -= 1;
154 continue;
155 }
156 if n < 2 {
157 return Err(DecodeError { position: i, reason: "padding where none belongs" });
158 }
159 flush(&mut out, quantum, n);
160 pads_left = 3 - n;
161 quantum = 0;
162 n = 0;
163 padding = true;
164 continue;
165 }
166 if (self.whitespace && c.is_ascii_whitespace()) || self.skip_invalid {
167 continue;
168 }
169 return Err(DecodeError { position: i, reason: "not a base64 character" });
170 }
171 if n == 1 {
172 return Err(DecodeError { position: input.len(), reason: "a lone trailing character" });
173 }
174 flush(&mut out, quantum, n);
175 Ok(out)
176 }
177}
178
179fn flush(out: &mut Vec<u8>, quantum: u32, n: u32) {
181 match n {
182 2 => out.push((quantum >> 4) as u8),
183 3 => out.extend_from_slice(&[(quantum >> 10) as u8, (quantum >> 2) as u8]),
184 _ => {}
185 }
186}