1use lazy_static::lazy_static;
2
3pub const B64_URL_ENCODE: [u8; 64] = [
4 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
5 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
6 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
7 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2d, 0x5f,
8];
9
10pub const B64_URL_DECODE: [u8; 255] = [
11 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
14 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
16 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x3f,
17 0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
18 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
19 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
21 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
22 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
23 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27];
28
29pub const B64_URL_PAD: u8 = 0x3d;
30
31lazy_static! {
32 static ref DEFAULT_CONFIG: B64Config = B64Config::default();
33}
34
35#[derive(Default)]
36pub struct B64ConfigPadding {
37 pub omit: bool,
38}
39
40#[derive(Default)]
41pub struct B64Config {
42 pub padding: B64ConfigPadding,
43}
44
45#[inline(always)]
46pub fn _b64_url_encode_calculate_destination_capacity(length: usize) -> usize {
47 length / 3 * 4 + 4
48}
49
50#[inline(always)]
51pub unsafe fn _b64_url_encode_with_config(
52 mut source: *const u8,
53 mut source_length: usize,
54 mut destination: *mut u8,
55 config: &B64Config,
56) -> usize {
57 let mut bytes = 0;
58 while source_length >= 3 {
59 let value = ((*source as u32) << 16)
60 | ((*source.offset(1) as u32) << 8)
61 | (*source.offset(2) as u32);
62 *destination = B64_URL_ENCODE[((value >> 18) & 0b11_1111) as usize];
63 *destination.offset(1) = B64_URL_ENCODE[((value >> 12) & 0b11_1111) as usize];
64 *destination.offset(2) = B64_URL_ENCODE[((value >> 6) & 0b11_1111) as usize];
65 *destination.offset(3) = B64_URL_ENCODE[(value & 0b11_1111) as usize];
66 source = source.offset(3);
67 destination = destination.offset(4);
68 source_length -= 3;
69 bytes += 4;
70 }
71 match source_length {
72 2 => {
73 let value = ((*source as u32) << 16) | ((*source.offset(1) as u32) << 8);
74 *destination = B64_URL_ENCODE[((value >> 18) & 0b11_1111) as usize];
75 *destination.offset(1) = B64_URL_ENCODE[((value >> 12) & 0b11_1111) as usize];
76 *destination.offset(2) = B64_URL_ENCODE[((value >> 6) & 0b11_1111) as usize];
77 if !config.padding.omit {
78 *destination.offset(3) = B64_URL_PAD;
79 bytes += 4;
80 } else {
81 bytes += 3;
82 }
83 }
84 1 => {
85 let value = (*source as u32) << 16;
86 *destination = B64_URL_ENCODE[((value >> 18) & 0b11_1111) as usize];
87 *destination.offset(1) = B64_URL_ENCODE[((value >> 12) & 0b11_1111) as usize];
88 if !config.padding.omit {
89 *destination.offset(2) = B64_URL_PAD;
90 *destination.offset(3) = B64_URL_PAD;
91 bytes += 4;
92 } else {
93 bytes += 2;
94 }
95 }
96 _ => {}
97 }
98 bytes
99}
100
101#[inline(always)]
102pub fn b64_url_encode_with_config(bytes: &[u8], config: &B64Config) -> Vec<u8> {
103 let length = bytes.len();
104 let mut vec = Vec::<u8>::with_capacity(_b64_url_encode_calculate_destination_capacity(length));
105 unsafe {
106 let bytes = _b64_url_encode_with_config(bytes.as_ptr(), length, vec.as_mut_ptr(), config);
107 vec.set_len(bytes);
108 }
109 vec
110}
111
112#[cfg(test)]
113mod b64_url_encode_with_config_tests {
114 use super::*;
115
116 mod with_omit_pads {
117 use super::*;
118
119 #[test]
120 fn empty() {
121 let config = B64Config {
122 padding: B64ConfigPadding { omit: true },
123 };
124 let result_bytes = b64_url_encode_with_config(b"", &config);
125 let result = String::from_utf8(result_bytes).unwrap();
126 assert_eq!(result, "");
127 }
128
129 #[test]
130 fn f() {
131 let config = B64Config {
132 padding: B64ConfigPadding { omit: true },
133 };
134 let result_bytes = b64_url_encode_with_config(b"f", &config);
135 let result = String::from_utf8(result_bytes).unwrap();
136 assert_eq!(result, "Zg");
137 }
138
139 #[test]
140 fn fo() {
141 let config = B64Config {
142 padding: B64ConfigPadding { omit: true },
143 };
144 let result_bytes = b64_url_encode_with_config(b"fo", &config);
145 let result = String::from_utf8(result_bytes).unwrap();
146 assert_eq!(result, "Zm8");
147 }
148
149 #[test]
150 fn foo() {
151 let config = B64Config {
152 padding: B64ConfigPadding { omit: true },
153 };
154 let result_bytes = b64_url_encode_with_config(b"foo", &config);
155 let result = String::from_utf8(result_bytes).unwrap();
156 assert_eq!(result, "Zm9v");
157 }
158
159 #[test]
160 fn foob() {
161 let config = B64Config {
162 padding: B64ConfigPadding { omit: true },
163 };
164 let result_bytes = b64_url_encode_with_config(b"foob", &config);
165 let result = String::from_utf8(result_bytes).unwrap();
166 assert_eq!(result, "Zm9vYg");
167 }
168
169 #[test]
170 fn fooba() {
171 let config = B64Config {
172 padding: B64ConfigPadding { omit: true },
173 };
174 let result_bytes = b64_url_encode_with_config(b"fooba", &config);
175 let result = String::from_utf8(result_bytes).unwrap();
176 assert_eq!(result, "Zm9vYmE");
177 }
178
179 #[test]
180 fn foobar() {
181 let config = B64Config {
182 padding: B64ConfigPadding { omit: true },
183 };
184 let result_bytes = b64_url_encode_with_config(b"foobar", &config);
185 let result = String::from_utf8(result_bytes).unwrap();
186 assert_eq!(result, "Zm9vYmFy");
187 }
188 }
189}
190
191#[inline(always)]
192pub fn b64_url_encode(bytes: &[u8]) -> Vec<u8> {
193 b64_url_encode_with_config(bytes, &DEFAULT_CONFIG)
194}
195
196#[cfg(test)]
197mod b64_url_encode_tests {
198 use super::*;
199
200 #[test]
201 fn empty() {
202 let result_bytes = b64_url_encode(b"");
203 let result = String::from_utf8(result_bytes).unwrap();
204 assert_eq!(result, "");
205 }
206
207 #[test]
208 fn f() {
209 let result_bytes = b64_url_encode(b"f");
210 let result = String::from_utf8(result_bytes).unwrap();
211 assert_eq!(result, "Zg==");
212 }
213
214 #[test]
215 fn fo() {
216 let result_bytes = b64_url_encode(b"fo");
217 let result = String::from_utf8(result_bytes).unwrap();
218 assert_eq!(result, "Zm8=");
219 }
220
221 #[test]
222 fn foo() {
223 let result_bytes = b64_url_encode(b"foo");
224 let result = String::from_utf8(result_bytes).unwrap();
225 assert_eq!(result, "Zm9v");
226 }
227
228 #[test]
229 fn foob() {
230 let result_bytes = b64_url_encode(b"foob");
231 let result = String::from_utf8(result_bytes).unwrap();
232 assert_eq!(result, "Zm9vYg==");
233 }
234
235 #[test]
236 fn fooba() {
237 let result_bytes = b64_url_encode(b"fooba");
238 let result = String::from_utf8(result_bytes).unwrap();
239 assert_eq!(result, "Zm9vYmE=");
240 }
241
242 #[test]
243 fn foobar() {
244 let result_bytes = b64_url_encode(b"foobar");
245 let result = String::from_utf8(result_bytes).unwrap();
246 assert_eq!(result, "Zm9vYmFy");
247 }
248}
249
250#[inline(always)]
254pub unsafe fn b64_url_decode_without_validation(bytes: &[u8]) -> Vec<u8> {
255 b64_url_decode_with_config_without_validation(bytes, &DEFAULT_CONFIG)
256}
257
258#[cfg(test)]
259mod b64_url_decode_without_validation_tests {
260 use super::*;
261
262 #[test]
263 fn empty() {
264 let result_bytes = unsafe { b64_url_decode_without_validation(b"") };
265 let result = String::from_utf8(result_bytes).unwrap();
266 assert_eq!(result, "");
267 }
268
269 #[test]
270 fn f() {
271 let result_bytes = unsafe { b64_url_decode_without_validation(b"Zg==") };
272 let result = String::from_utf8(result_bytes).unwrap();
273 assert_eq!(result, "f");
274 }
275
276 #[test]
277 fn fo() {
278 let result_bytes = unsafe { b64_url_decode_without_validation(b"Zm8=") };
279 let result = String::from_utf8(result_bytes).unwrap();
280 assert_eq!(result, "fo");
281 }
282
283 #[test]
284 fn foo() {
285 let result_bytes = unsafe { b64_url_decode_without_validation(b"Zm9v") };
286 let result = String::from_utf8(result_bytes).unwrap();
287 assert_eq!(result, "foo");
288 }
289
290 #[test]
291 fn foob() {
292 let result_bytes = unsafe { b64_url_decode_without_validation(b"Zm9vYg==") };
293 let result = String::from_utf8(result_bytes).unwrap();
294 assert_eq!(result, "foob");
295 }
296
297 #[test]
298 fn fooba() {
299 let result_bytes = unsafe { b64_url_decode_without_validation(b"Zm9vYmE=") };
300 let result = String::from_utf8(result_bytes).unwrap();
301 assert_eq!(result, "fooba");
302 }
303
304 #[test]
305 fn foobar() {
306 let result_bytes = unsafe { b64_url_decode_without_validation(b"Zm9vYmFy") };
307 let result = String::from_utf8(result_bytes).unwrap();
308 assert_eq!(result, "foobar");
309 }
310}
311
312#[inline(always)]
316pub unsafe fn b64_url_decode_with_config_without_validation(
317 bytes: &[u8],
318 config: &B64Config,
319) -> Vec<u8> {
320 let length = bytes.len();
321 let mut vec = Vec::<u8>::with_capacity(length * 3 / 4);
322 let mut index = 0;
323 if length > 4 {
324 while index < length - 4 {
325 let value = ((B64_URL_DECODE[(bytes[index] as usize)] as u32) << 18)
326 | ((B64_URL_DECODE[(bytes[index + 1] as usize)] as u32) << 12)
327 | ((B64_URL_DECODE[(bytes[index + 2] as usize)] as u32) << 6)
328 | (B64_URL_DECODE[(bytes[index + 3] as usize)] as u32);
329 vec.push(((value >> 16) & 0b1111_1111) as u8);
330 vec.push(((value >> 8) & 0b1111_1111) as u8);
331 vec.push((value & 0b1111_1111) as u8);
332 index += 4;
333 }
334 }
335 if config.padding.omit {
336 if index + 2 <= length {
337 let mut value = ((B64_URL_DECODE[(bytes[index] as usize)] as u32) << 18)
338 | ((B64_URL_DECODE[(bytes[index + 1] as usize)] as u32) << 12);
339 if index + 3 <= length {
340 value |= (B64_URL_DECODE[(bytes[index + 2] as usize)] as u32) << 6;
341 if index + 4 <= length {
342 value |= B64_URL_DECODE[(bytes[index + 3] as usize)] as u32;
343 vec.push(((value >> 16) & 0b1111_1111) as u8);
344 vec.push(((value >> 8) & 0b1111_1111) as u8);
345 vec.push((value & 0b1111_1111) as u8);
346 } else {
347 vec.push(((value >> 16) & 0b1111_1111) as u8);
348 vec.push(((value >> 8) & 0b1111_1111) as u8);
349 }
350 } else {
351 vec.push(((value >> 16) & 0b1111_1111) as u8);
352 }
353 }
354 return vec;
355 }
356 if index + 4 == length {
357 let mut value = ((B64_URL_DECODE[(bytes[index] as usize)] as u32) << 18)
358 | ((B64_URL_DECODE[(bytes[index + 1] as usize)] as u32) << 12);
359 if bytes[index + 2] != B64_URL_PAD {
360 value |= (B64_URL_DECODE[(bytes[index + 2] as usize)] as u32) << 6;
361 if bytes[index + 3] != B64_URL_PAD {
362 value |= B64_URL_DECODE[(bytes[index + 3] as usize)] as u32;
363 vec.push(((value >> 16) & 0b1111_1111) as u8);
364 vec.push(((value >> 8) & 0b1111_1111) as u8);
365 vec.push((value & 0b1111_1111) as u8);
366 } else {
367 vec.push(((value >> 16) & 0b1111_1111) as u8);
368 vec.push(((value >> 8) & 0b1111_1111) as u8);
369 }
370 } else {
371 vec.push(((value >> 16) & 0b1111_1111) as u8);
372 }
373 }
374 vec
375}
376
377#[cfg(test)]
378mod b64_url_decode_with_config_without_validation_tests {
379 use super::*;
380
381 mod with_omit_pads {
382 use super::*;
383
384 #[test]
385 fn empty() {
386 let config = B64Config {
387 padding: B64ConfigPadding { omit: true },
388 };
389 let result_bytes =
390 unsafe { b64_url_decode_with_config_without_validation(b"", &config) };
391 let result = String::from_utf8(result_bytes).unwrap();
392 assert_eq!(result, "");
393 }
394
395 #[test]
396 fn f() {
397 let config = B64Config {
398 padding: B64ConfigPadding { omit: true },
399 };
400 let result_bytes =
401 unsafe { b64_url_decode_with_config_without_validation(b"Zg", &config) };
402 let result = String::from_utf8(result_bytes).unwrap();
403 assert_eq!(result, "f");
404 }
405
406 #[test]
407 fn fo() {
408 let config = B64Config {
409 padding: B64ConfigPadding { omit: true },
410 };
411 let result_bytes =
412 unsafe { b64_url_decode_with_config_without_validation(b"Zm8", &config) };
413 let result = String::from_utf8(result_bytes).unwrap();
414 assert_eq!(result, "fo");
415 }
416
417 #[test]
418 fn foo() {
419 let config = B64Config {
420 padding: B64ConfigPadding { omit: true },
421 };
422 let result_bytes =
423 unsafe { b64_url_decode_with_config_without_validation(b"Zm9v", &config) };
424 let result = String::from_utf8(result_bytes).unwrap();
425 assert_eq!(result, "foo");
426 }
427
428 #[test]
429 fn foob() {
430 let config = B64Config {
431 padding: B64ConfigPadding { omit: true },
432 };
433 let result_bytes =
434 unsafe { b64_url_decode_with_config_without_validation(b"Zm9vYg", &config) };
435 let result = String::from_utf8(result_bytes).unwrap();
436 assert_eq!(result, "foob");
437 }
438
439 #[test]
440 fn fooba() {
441 let config = B64Config {
442 padding: B64ConfigPadding { omit: true },
443 };
444 let result_bytes =
445 unsafe { b64_url_decode_with_config_without_validation(b"Zm9vYmE", &config) };
446 let result = String::from_utf8(result_bytes).unwrap();
447 assert_eq!(result, "fooba");
448 }
449
450 #[test]
451 fn foobar() {
452 let config = B64Config {
453 padding: B64ConfigPadding { omit: true },
454 };
455 let result_bytes =
456 unsafe { b64_url_decode_with_config_without_validation(b"Zm9vYmFy", &config) };
457 let result = String::from_utf8(result_bytes).unwrap();
458 assert_eq!(result, "foobar");
459 }
460 }
461}