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