1#![forbid(unsafe_code)]
21
22macro_rules! seq4 {
23 [$($e:expr),*] => { [$($e,$e,$e,$e,)*] }
24}
25
26macro_rules! rep4 {
27 [$($e:expr),*] => { [$($e,)*$($e,)*$($e,)*$($e,)*] }
28}
29
30static E0: [char; 256] = seq4![
31 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
32 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
33 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
34 '5', '6', '7', '8', '9', '+', '/'
35];
36
37static E1: [char; 256] = rep4![
38 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
39 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
40 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
41 '5', '6', '7', '8', '9', '+', '/'
42];
43
44static E2: [char; 256] = E1;
45
46const FF: u32 = 33554431;
47
48static D0: [u32; 256] = [
49 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
50 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 248, FF, FF, FF,
51 252, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, FF, FF, FF, FF, FF, FF, FF, 0, 4, 8, 12,
52 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, FF,
53 FF, FF, FF, FF, FF, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160,
54 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
55 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
56 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
57 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
58 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
59 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
60 FF, FF, FF,
61];
62
63static D1: [u32; 256] = [
64 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
65 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 57347, FF, FF, FF,
66 61443, 16387, 20483, 24579, 28675, 32771, 36867, 40963, 45059, 49155, 53251, FF, FF, FF, FF,
67 FF, FF, FF, 0, 4096, 8192, 12288, 16384, 20480, 24576, 28672, 32768, 36864, 40960, 45056,
68 49152, 53248, 57344, 61440, 1, 4097, 8193, 12289, 16385, 20481, 24577, 28673, 32769, 36865, FF,
69 FF, FF, FF, FF, FF, 40961, 45057, 49153, 53249, 57345, 61441, 2, 4098, 8194, 12290, 16386,
70 20482, 24578, 28674, 32770, 36866, 40962, 45058, 49154, 53250, 57346, 61442, 3, 4099, 8195,
71 12291, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
72 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
73 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
74 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
75 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
76 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
77];
78
79static D2: [u32; 256] = [
80 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
81 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 8392448, FF, FF,
82 FF, 12586752, 3328, 4197632, 8391936, 12586240, 3584, 4197888, 8392192, 12586496, 3840,
83 4198144, FF, FF, FF, FF, FF, FF, FF, 0, 4194304, 8388608, 12582912, 256, 4194560, 8388864,
84 12583168, 512, 4194816, 8389120, 12583424, 768, 4195072, 8389376, 12583680, 1024, 4195328,
85 8389632, 12583936, 1280, 4195584, 8389888, 12584192, 1536, 4195840, FF, FF, FF, FF, FF, FF,
86 8390144, 12584448, 1792, 4196096, 8390400, 12584704, 2048, 4196352, 8390656, 12584960, 2304,
87 4196608, 8390912, 12585216, 2560, 4196864, 8391168, 12585472, 2816, 4197120, 8391424, 12585728,
88 3072, 4197376, 8391680, 12585984, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
89 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
90 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
91 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
92 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
93 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
94];
95
96static D3: [u32; 256] = [
97 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
98 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 4063232, FF, FF,
99 FF, 4128768, 3407872, 3473408, 3538944, 3604480, 3670016, 3735552, 3801088, 3866624, 3932160,
100 3997696, FF, FF, FF, FF, FF, FF, FF, 0, 65536, 131072, 196608, 262144, 327680, 393216, 458752,
101 524288, 589824, 655360, 720896, 786432, 851968, 917504, 983040, 1048576, 1114112, 1179648,
102 1245184, 1310720, 1376256, 1441792, 1507328, 1572864, 1638400, FF, FF, FF, FF, FF, FF, 1703936,
103 1769472, 1835008, 1900544, 1966080, 2031616, 2097152, 2162688, 2228224, 2293760, 2359296,
104 2424832, 2490368, 2555904, 2621440, 2686976, 2752512, 2818048, 2883584, 2949120, 3014656,
105 3080192, 3145728, 3211264, 3276800, 3342336, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
106 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
107 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
108 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
109 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
110 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF,
111 FF,
112];
113
114pub fn encode(data: &[u8]) -> String {
116 let len = data.len();
117
118 let mut dest = vec![0u8; ((4 * len / 3) + 3) & !3];
119
120 let mut i = 0;
121 let mut j = 0;
122
123 if len > 2 {
124 while i < len - 2 {
125 let t1 = data[i];
126 let t2 = data[i + 1];
127 let t3 = data[i + 2];
128
129 dest[j] = E0[t1 as usize] as u8;
130 dest[j + 1] = E1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize] as u8;
131 dest[j + 2] = E1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) as usize] as u8;
132 dest[j + 3] = E2[t3 as usize] as u8;
133
134 i += 3;
135 j += 4;
136 }
137 }
138 match len - i {
139 0 => {}
140 1 => {
141 let t1 = data[i];
142
143 dest[j] = E0[t1 as usize] as u8;
144 dest[j + 1] = E1[((t1 & 0x03) << 4) as usize] as u8;
145 dest[j + 2] = b'=';
146 dest[j + 3] = b'=';
147 }
148 _ => {
149 let t1 = data[i] as usize;
151 let t2 = data[i + 1] as usize;
152
153 dest[j] = E0[t1] as u8;
154 dest[j + 1] = E1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)] as u8;
155 dest[j + 2] = E2[(t2 & 0x0F) << 2] as u8;
156 dest[j + 3] = b'=';
157 }
158 }
159
160 String::from_utf8(dest).unwrap()
161}
162
163pub fn decode(data: &str) -> Option<Vec<u8>> {
166 if !data.is_ascii() || data.is_empty() {
167 return None
168 }
169
170 let data = match data.len() % 4 {
171 1 => return None, 2 => format!("{data}=="),
173 3 => format!("{data}="),
174 _ => data.to_string(), };
176
177 let data = data.as_bytes();
178
179 let mut padding_count = 0;
180 for (i, &byte) in data.iter().enumerate() {
181 let valid = byte.is_ascii_uppercase() ||
182 byte.is_ascii_lowercase() ||
183 byte.is_ascii_digit() ||
184 byte == b'+' ||
185 byte == b'/' ||
186 byte == b'=';
187
188 if !valid {
189 return None
190 }
191
192 if byte == b'=' {
193 padding_count += 1;
194 if i < data.len() - 2 {
195 return None
196 }
197 } else if padding_count > 0 {
198 return None
199 }
200 }
201
202 if padding_count > 2 {
203 return None
204 }
205
206 let mut len = data.len();
207
208 if data[len - 1] == b'=' {
209 len -= 1;
210 if data[len - 1] == b'=' {
211 len -= 1;
212 }
213 }
214
215 let mut dest = vec![0u8; (3 * (data.len() / 4)) - (data.len() - len)];
216
217 let leftover = len % 4;
218 let chunks = if leftover == 0 { len / 4 - 1 } else { len / 4 };
219
220 let mut j = 0;
221 let mut k = 0;
222 for _ in 0..chunks {
223 let x: u32 = D0[data[k] as usize] |
224 D1[data[1 + k] as usize] |
225 D2[data[2 + k] as usize] |
226 D3[data[3 + k] as usize];
227
228 dest[j] = x as u8;
229 dest[j + 1] = (x >> 8) as u8;
230 dest[j + 2] = (x >> 16) as u8;
231
232 j += 3;
233 k += 4;
234 }
235
236 match leftover {
237 0 => {
238 let x: u32 = D0[data[k] as usize] |
239 D1[data[1 + k] as usize] |
240 D2[data[2 + k] as usize] |
241 D3[data[3 + k] as usize];
242
243 dest[j] = x as u8;
244 dest[j + 1] = (x >> 8) as u8;
245 dest[j + 2] = (x >> 16) as u8;
246
247 return Some(dest)
249 }
250 1 => {
251 let x: u32 = D0[data[k] as usize]; dest[j] = x as u8;
254 }
255 2 => {
256 let x: u32 = D0[data[k] as usize] | D1[data[1 + k] as usize]; dest[j] = x as u8;
259 }
260 _ => {
261 let x: u32 = D0[data[k] as usize] | D1[data[1 + k] as usize] | D2[data[2 + k] as usize]; dest[j] = x as u8;
263 dest[j + 1] = (x >> 8) as u8;
264 }
265 }
266
267 Some(dest)
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 pub fn b64_encdec() {
277 const EXAMPLES: [(&[u8], &str); 3] = [
278 (b"abc123!?$*&()'-=@~", "YWJjMTIzIT8kKiYoKSctPUB+"),
279 (b"gm world", "Z20gd29ybGQ="),
280 (
281 b"Man is distinguished, not only by his reason, but by this singular passion from \
282 other animals, which is a lust of the mind, that by a perseverance of delight \
283 in the continued and indefatigable generation of knowledge, exceeds the short \
284 vehemence of any carnal pleasure.",
285 "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\
286 IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\
287 dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\
288 dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\
289 ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=",
290 ),
291 ];
292
293 for &(input, answer) in EXAMPLES.iter() {
294 let res = encode(input);
295 assert_eq!(answer, res);
296
297 let res = decode(answer).unwrap();
298 assert_eq!(input, res);
299 }
300
301 assert_eq!(b"a".to_vec(), decode("YQ").unwrap());
303 assert_eq!(b"a".to_vec(), decode("YQ=").unwrap());
304 assert_eq!(b"a0".to_vec(), decode("YTA").unwrap());
305 assert_eq!(b"gm world".to_vec(), decode("Z20gd29ybGQ").unwrap());
306
307 assert!(decode("").is_none());
309 assert!(decode("a").is_none());
310 assert!(decode("a=").is_none());
311 assert!(decode("a==").is_none());
312 assert!(decode("a===").is_none());
313 assert!(decode("=").is_none());
314 assert!(decode("==").is_none());
315 assert!(decode("===").is_none());
316 assert!(decode("====").is_none());
317 assert!(decode("This is a random string.").is_none());
318 }
319}