1pub const MAX_STANDARD_BASE64_INPUT_BYTES: usize = 16 * 1024 * 1024;
8
9pub fn decode_standard_base64(input: &str) -> Result<Vec<u8>, String> {
11 if input.len() > MAX_STANDARD_BASE64_INPUT_BYTES {
12 return Err(format!(
13 "base64 input exceeds {} bytes",
14 MAX_STANDARD_BASE64_INPUT_BYTES
15 ));
16 }
17 fn val(c: u8) -> Result<u8, String> {
18 match c {
19 b'A'..=b'Z' => Ok(c - b'A'),
20 b'a'..=b'z' => Ok(c - b'a' + 26),
21 b'0'..=b'9' => Ok(c - b'0' + 52),
22 b'+' => Ok(62),
23 b'/' => Ok(63),
24 _ => Err(format!("invalid base64 char: {c:#x}")),
25 }
26 }
27 let bytes = input.as_bytes();
28 let stripped: Vec<u8> = bytes.iter().copied().take_while(|&c| c != b'=').collect();
29 let mut out = Vec::with_capacity(stripped.len() * 3 / 4);
30 for chunk in stripped.chunks(4) {
31 let v0 = val(chunk[0])?;
32 let v1 = val(*chunk.get(1).ok_or_else(|| "truncated base64".to_string())?)?;
33 out.push((v0 << 2) | (v1 >> 4));
34 if let Some(&c2) = chunk.get(2) {
35 let v2 = val(c2)?;
36 out.push(((v1 & 0x0F) << 4) | (v2 >> 2));
37 if let Some(&c3) = chunk.get(3) {
38 let v3 = val(c3)?;
39 out.push(((v2 & 0x03) << 6) | v3);
40 }
41 }
42 }
43 Ok(out)
44}