1#![cfg_attr(not(test), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4#[cfg(any(feature = "alloc", test))]
51extern crate alloc;
52
53#[cfg(all(feature = "serde", any(feature = "alloc", test)))]
54mod serde;
55#[cfg(all(feature = "serde", any(feature = "alloc", test)))]
58pub use self::serde::{deserialize, serialize};
59
60#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
64mod base64_neon;
65
66#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))]
67mod base64_avx2;
68
69#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
70mod base64_wasm_simd128;
71
72const PAD: u8 = b'=';
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum Alphabet {
93 Standard,
94 StandardNoPadding,
95 Url,
96 UrlNoPadding,
97}
98
99impl Alphabet {
100 #[inline]
101 const fn is_padded(&self) -> bool {
102 matches!(self, Alphabet::Standard | Alphabet::Url)
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum EncodeError {
109 InvalidOutputLength,
111 OutputOverflow,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub enum DecodeError {
118 InvalidInput,
121 InvalidInputLength,
123 InvalidPadding,
126 InvalidOutputLength,
128}
129
130impl core::fmt::Display for EncodeError {
131 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
132 match self {
133 Self::InvalidOutputLength => f.write_str("output buffer size must be exactly equal to decoded_len(input)"),
134 Self::OutputOverflow => f.write_str("output length overflows usize::MAX"),
135 }
136 }
137}
138
139impl core::fmt::Display for DecodeError {
140 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141 match self {
142 Self::InvalidInput => f.write_str("invalid base64 character"),
143 Self::InvalidInputLength => f.write_str("invalid base64 length"),
144 Self::InvalidOutputLength => f.write_str("output length is not valid"),
145 Self::InvalidPadding => f.write_str("invalid base64 padding"),
146 }
147 }
148}
149
150impl core::error::Error for EncodeError {}
151
152impl core::error::Error for DecodeError {}
153
154pub const fn encoded_length(data_length: usize, padding: bool) -> Option<usize> {
170 let complete_chunks = data_length / 3;
171 let remaining = data_length % 3;
172 let base = match complete_chunks.checked_mul(4) {
173 Some(v) => v,
174 None => return None,
175 };
176 if remaining == 0 {
177 Some(base)
178 } else if padding {
179 base.checked_add(4)
180 } else if remaining == 1 {
181 Some(base + 2)
182 } else {
183 Some(base + 3)
184 }
185}
186
187#[cfg(feature = "alloc")]
196pub fn encode(data: impl AsRef<[u8]>, alphabet: Alphabet) -> alloc::string::String {
197 let data = data.as_ref();
198 let len = encoded_length(data.len(), alphabet.is_padded()).expect("encoded length overflow");
199 let mut output = alloc::vec![0u8; len];
200 encode_into(&mut output, data, alphabet).unwrap();
201 unsafe { alloc::string::String::from_utf8_unchecked(output) }
203}
204
205pub const fn encode_array<const OUT: usize>(data: &[u8], alphabet: Alphabet) -> [u8; OUT] {
218 let mut out_buffer = [0u8; OUT];
219 match encode_into_constant_time(&mut out_buffer, data, alphabet) {
220 Ok(_) => {}
221 Err(_) => panic!("output buffer size is not valid"),
222 };
223 out_buffer
224}
225
226pub fn encode_into(output: &mut [u8], data: &[u8], alphabet: Alphabet) -> Result<(), EncodeError> {
246 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
247 if data.len() >= 48 {
248 check_encode_output_length(output.len(), data.len(), alphabet)?;
249 return unsafe { base64_neon::encode_into(output, data, alphabet) };
250 }
251
252 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))]
253 if data.len() >= 24 {
254 check_encode_output_length(output.len(), data.len(), alphabet)?;
255 return unsafe { base64_avx2::encode_into(output, data, alphabet) };
256 }
257
258 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
259 if data.len() >= 12 {
260 check_encode_output_length(output.len(), data.len(), alphabet)?;
261 return base64_wasm_simd128::encode_into(output, data, alphabet);
262 }
263
264 return encode_into_constant_time(output, data, alphabet);
265}
266
267pub const fn encode_into_constant_time(output: &mut [u8], data: &[u8], alphabet: Alphabet) -> Result<(), EncodeError> {
282 match check_encode_output_length(output.len(), data.len(), alphabet) {
283 Ok(_) => {}
284 Err(err) => return Err(err),
285 };
286
287 let padding = alphabet.is_padded();
288 let len = data.len();
289 let mut i = 0;
290
291 while i + 24 <= len {
292 let b0 = data[i];
293 let b1 = data[i + 1];
294 let b2 = data[i + 2];
295 let b3 = data[i + 3];
296 let b4 = data[i + 4];
297 let b5 = data[i + 5];
298 let b6 = data[i + 6];
299 let b7 = data[i + 7];
300 let b8 = data[i + 8];
301 let b9 = data[i + 9];
302 let b10 = data[i + 10];
303 let b11 = data[i + 11];
304 let b12 = data[i + 12];
305 let b13 = data[i + 13];
306 let b14 = data[i + 14];
307 let b15 = data[i + 15];
308 let b16 = data[i + 16];
309 let b17 = data[i + 17];
310 let b18 = data[i + 18];
311 let b19 = data[i + 19];
312 let b20 = data[i + 20];
313 let b21 = data[i + 21];
314 let b22 = data[i + 22];
315 let b23 = data[i + 23];
316
317 let o = (i / 3) * 4;
318 output[o] = sextet_to_base64_char(b0 >> 2, alphabet);
319 output[o + 1] = sextet_to_base64_char(((b0 & 0x03) << 4) | (b1 >> 4), alphabet);
320 output[o + 2] = sextet_to_base64_char(((b1 & 0x0F) << 2) | (b2 >> 6), alphabet);
321 output[o + 3] = sextet_to_base64_char(b2 & 0x3F, alphabet);
322
323 output[o + 4] = sextet_to_base64_char(b3 >> 2, alphabet);
324 output[o + 5] = sextet_to_base64_char(((b3 & 0x03) << 4) | (b4 >> 4), alphabet);
325 output[o + 6] = sextet_to_base64_char(((b4 & 0x0F) << 2) | (b5 >> 6), alphabet);
326 output[o + 7] = sextet_to_base64_char(b5 & 0x3F, alphabet);
327
328 output[o + 8] = sextet_to_base64_char(b6 >> 2, alphabet);
329 output[o + 9] = sextet_to_base64_char(((b6 & 0x03) << 4) | (b7 >> 4), alphabet);
330 output[o + 10] = sextet_to_base64_char(((b7 & 0x0F) << 2) | (b8 >> 6), alphabet);
331 output[o + 11] = sextet_to_base64_char(b8 & 0x3F, alphabet);
332
333 output[o + 12] = sextet_to_base64_char(b9 >> 2, alphabet);
334 output[o + 13] = sextet_to_base64_char(((b9 & 0x03) << 4) | (b10 >> 4), alphabet);
335 output[o + 14] = sextet_to_base64_char(((b10 & 0x0F) << 2) | (b11 >> 6), alphabet);
336 output[o + 15] = sextet_to_base64_char(b11 & 0x3F, alphabet);
337
338 output[o + 16] = sextet_to_base64_char(b12 >> 2, alphabet);
339 output[o + 17] = sextet_to_base64_char(((b12 & 0x03) << 4) | (b13 >> 4), alphabet);
340 output[o + 18] = sextet_to_base64_char(((b13 & 0x0F) << 2) | (b14 >> 6), alphabet);
341 output[o + 19] = sextet_to_base64_char(b14 & 0x3F, alphabet);
342
343 output[o + 20] = sextet_to_base64_char(b15 >> 2, alphabet);
344 output[o + 21] = sextet_to_base64_char(((b15 & 0x03) << 4) | (b16 >> 4), alphabet);
345 output[o + 22] = sextet_to_base64_char(((b16 & 0x0F) << 2) | (b17 >> 6), alphabet);
346 output[o + 23] = sextet_to_base64_char(b17 & 0x3F, alphabet);
347
348 output[o + 24] = sextet_to_base64_char(b18 >> 2, alphabet);
349 output[o + 25] = sextet_to_base64_char(((b18 & 0x03) << 4) | (b19 >> 4), alphabet);
350 output[o + 26] = sextet_to_base64_char(((b19 & 0x0F) << 2) | (b20 >> 6), alphabet);
351 output[o + 27] = sextet_to_base64_char(b20 & 0x3F, alphabet);
352
353 output[o + 28] = sextet_to_base64_char(b21 >> 2, alphabet);
354 output[o + 29] = sextet_to_base64_char(((b21 & 0x03) << 4) | (b22 >> 4), alphabet);
355 output[o + 30] = sextet_to_base64_char(((b22 & 0x0F) << 2) | (b23 >> 6), alphabet);
356 output[o + 31] = sextet_to_base64_char(b23 & 0x3F, alphabet);
357
358 i += 24;
359 }
360
361 while i + 3 <= len {
362 let b0 = data[i];
363 let b1 = data[i + 1];
364 let b2 = data[i + 2];
365 let o = (i / 3) * 4;
366 output[o] = sextet_to_base64_char(b0 >> 2, alphabet);
367 output[o + 1] = sextet_to_base64_char(((b0 & 0x03) << 4) | (b1 >> 4), alphabet);
368 output[o + 2] = sextet_to_base64_char(((b1 & 0x0F) << 2) | (b2 >> 6), alphabet);
369 output[o + 3] = sextet_to_base64_char(b2 & 0x3F, alphabet);
370 i += 3;
371 }
372
373 let remaining = len - i;
374 if remaining > 0 {
375 let o = (i / 3) * 4;
376 let b0 = data[i];
377 let b1 = if i + 1 < len { data[i + 1] } else { 0 };
378
379 let rem1 = (remaining == 1) as u8;
380 let rem2 = (remaining == 2) as u8;
381 let m1 = 0u8.wrapping_sub(rem1);
382 let m2 = 0u8.wrapping_sub(rem2);
383
384 output[o] = sextet_to_base64_char(b0 >> 2, alphabet);
385
386 let o1_rem1 = sextet_to_base64_char((b0 & 0x03) << 4, alphabet);
387 let o1_rem2 = sextet_to_base64_char(((b0 & 0x03) << 4) | (b1 >> 4), alphabet);
388 output[o + 1] = (o1_rem1 & m1) | (o1_rem2 & m2);
389
390 if padding {
391 let o2_rem1 = PAD;
392 let o2_rem2 = sextet_to_base64_char((b1 & 0x0F) << 2, alphabet);
393 output[o + 2] = (o2_rem1 & m1) | (o2_rem2 & m2);
394 output[o + 3] = PAD;
395 } else {
396 if remaining == 2 {
397 output[o + 2] = sextet_to_base64_char((b1 & 0x0F) << 2, alphabet);
398 }
399 }
400 }
401
402 Ok(())
403}
404
405#[cfg(feature = "alloc")]
415pub fn encode_into_string(output: &mut alloc::string::String, data: &[u8], alphabet: Alphabet) {
416 let encoded_length = encoded_length(data.len(), alphabet.is_padded()).expect("output length overflow");
417 if encoded_length <= 256 {
418 let mut buf = [0u8; 256];
420 let buf = &mut buf[..encoded_length];
421 encode_into(buf, data, alphabet).unwrap();
422 output.push_str(unsafe { core::str::from_utf8_unchecked(buf) });
424 } else {
425 let mut buf = alloc::vec![0u8; encoded_length];
426 encode_into(&mut buf, data, alphabet).unwrap();
427 output.push_str(unsafe { core::str::from_utf8_unchecked(&buf) });
429 }
430}
431
432#[inline]
434const fn check_encode_output_length(
435 output_length: usize,
436 data_length: usize,
437 alphabet: Alphabet,
438) -> Result<(), EncodeError> {
439 let padding = alphabet.is_padded();
440
441 let expected_output_length = match encoded_length(data_length, padding) {
442 Some(length) => length,
443 None => return Err(EncodeError::OutputOverflow),
444 };
445 if output_length != expected_output_length {
446 return Err(EncodeError::InvalidOutputLength);
447 }
448
449 return Ok(());
450}
451
452#[inline]
455const fn not_in_range(v: u8, lo: u8, hi: u8) -> u8 {
456 (((v.wrapping_sub(lo) as i8) | (hi.wrapping_sub(v) as i8)) >> 7) as u8
457}
458
459#[inline]
462const fn sextet_to_base64_char(v: u8, alphabet: Alphabet) -> u8 {
463 let v = v & 0x3F;
464
465 let not_upper = not_in_range(v, 0, 25);
466 let not_lower = not_in_range(v, 26, 51);
467 let not_digit = not_in_range(v, 52, 61);
468 let not_62 = not_in_range(v, 62, 62);
469 let not_63 = not_in_range(v, 63, 63);
470
471 let upper_val = v + b'A';
472 let lower_val = v.wrapping_sub(26).wrapping_add(b'a');
473 let digit_val = v.wrapping_sub(52).wrapping_add(b'0');
474
475 let (ch_62, ch_63) = match alphabet {
476 Alphabet::Standard | Alphabet::StandardNoPadding => (b'+', b'/'),
477 Alphabet::Url | Alphabet::UrlNoPadding => (b'-', b'_'),
478 };
479
480 (upper_val & !not_upper)
481 | (lower_val & !not_lower)
482 | (digit_val & !not_digit)
483 | (ch_62 & !not_62)
484 | (ch_63 & !not_63)
485}
486
487#[cfg(feature = "alloc")]
505pub fn decode(data: impl AsRef<[u8]>, alphabet: Alphabet) -> Result<alloc::vec::Vec<u8>, DecodeError> {
506 let data = data.as_ref();
507 let (content_len, _) = strip_padding_info(data, alphabet.is_padded())?;
508 let output_len = decoded_length(content_len)?;
509 let mut output = alloc::vec![0u8; output_len];
510 decode_into(&mut output, data, alphabet)?;
511 Ok(output)
512}
513
514pub const fn decode_array<const OUT: usize>(encoded_data: &[u8], alphabet: Alphabet) -> Result<[u8; OUT], DecodeError> {
527 let mut result = [0u8; OUT];
528 match decode_into_constant_time(&mut result, encoded_data, alphabet) {
529 Ok(_) => Ok(result),
530 Err(err) => Err(err),
531 }
532}
533
534pub fn decode_into(output: &mut [u8], encoded_data: &[u8], alphabet: Alphabet) -> Result<usize, DecodeError> {
555 let (content_len, _) = strip_padding_info(encoded_data, alphabet.is_padded())?;
556 let computed_output = decoded_length(content_len)?;
557 if output.len() < computed_output {
558 return Err(DecodeError::InvalidOutputLength);
559 }
560
561 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
562 if content_len >= 32 {
563 let content = &encoded_data[..content_len];
564 unsafe { base64_neon::decode_into(output, content, alphabet)? };
565 return Ok(computed_output);
566 }
567
568 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))]
569 if content_len >= 32 {
570 let content = &encoded_data[..content_len];
571 unsafe { base64_avx2::decode_into(output, content, alphabet)? };
572 return Ok(computed_output);
573 }
574
575 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
576 if content_len >= 16 {
577 let content = &encoded_data[..content_len];
578 base64_wasm_simd128::decode_into(output, content, alphabet)?;
579 return Ok(computed_output);
580 }
581
582 decode_into_constant_time(output, encoded_data, alphabet)
583}
584
585pub const fn decode_into_constant_time(
600 output: &mut [u8],
601 encoded_data: &[u8],
602 alphabet: Alphabet,
603) -> Result<usize, DecodeError> {
604 let in_len = encoded_data.len();
605 let padding = alphabet.is_padded();
606
607 if in_len == 0 {
608 return Ok(0);
609 }
610
611 let (content_len, _padding_len) = match strip_padding_info(encoded_data, padding) {
612 Ok(info) => info,
613 Err(e) => return Err(e),
614 };
615
616 if content_len == 0 {
617 return Ok(0);
618 }
619
620 let computed_output = match decoded_length(content_len) {
621 Ok(len) => len,
622 Err(e) => return Err(e),
623 };
624
625 if output.len() < computed_output {
626 return Err(DecodeError::InvalidOutputLength);
627 }
628
629 let mut err: u8 = 0;
630 let mut i = 0;
631 let mut o = 0;
632
633 while i + 32 <= content_len {
634 let v0 = base64_char_to_sextet(encoded_data[i], alphabet);
635 let v1 = base64_char_to_sextet(encoded_data[i + 1], alphabet);
636 let v2 = base64_char_to_sextet(encoded_data[i + 2], alphabet);
637 let v3 = base64_char_to_sextet(encoded_data[i + 3], alphabet);
638 err |= v0 | v1 | v2 | v3;
639 output[o] = (v0 << 2) | (v1 >> 4);
640 output[o + 1] = (v1 << 4) | (v2 >> 2);
641 output[o + 2] = (v2 << 6) | v3;
642
643 let v4 = base64_char_to_sextet(encoded_data[i + 4], alphabet);
644 let v5 = base64_char_to_sextet(encoded_data[i + 5], alphabet);
645 let v6 = base64_char_to_sextet(encoded_data[i + 6], alphabet);
646 let v7 = base64_char_to_sextet(encoded_data[i + 7], alphabet);
647 err |= v4 | v5 | v6 | v7;
648 output[o + 3] = (v4 << 2) | (v5 >> 4);
649 output[o + 4] = (v5 << 4) | (v6 >> 2);
650 output[o + 5] = (v6 << 6) | v7;
651
652 let v8 = base64_char_to_sextet(encoded_data[i + 8], alphabet);
653 let v9 = base64_char_to_sextet(encoded_data[i + 9], alphabet);
654 let v10 = base64_char_to_sextet(encoded_data[i + 10], alphabet);
655 let v11 = base64_char_to_sextet(encoded_data[i + 11], alphabet);
656 err |= v8 | v9 | v10 | v11;
657 output[o + 6] = (v8 << 2) | (v9 >> 4);
658 output[o + 7] = (v9 << 4) | (v10 >> 2);
659 output[o + 8] = (v10 << 6) | v11;
660
661 let v12 = base64_char_to_sextet(encoded_data[i + 12], alphabet);
662 let v13 = base64_char_to_sextet(encoded_data[i + 13], alphabet);
663 let v14 = base64_char_to_sextet(encoded_data[i + 14], alphabet);
664 let v15 = base64_char_to_sextet(encoded_data[i + 15], alphabet);
665 err |= v12 | v13 | v14 | v15;
666 output[o + 9] = (v12 << 2) | (v13 >> 4);
667 output[o + 10] = (v13 << 4) | (v14 >> 2);
668 output[o + 11] = (v14 << 6) | v15;
669
670 let v16 = base64_char_to_sextet(encoded_data[i + 16], alphabet);
671 let v17 = base64_char_to_sextet(encoded_data[i + 17], alphabet);
672 let v18 = base64_char_to_sextet(encoded_data[i + 18], alphabet);
673 let v19 = base64_char_to_sextet(encoded_data[i + 19], alphabet);
674 err |= v16 | v17 | v18 | v19;
675 output[o + 12] = (v16 << 2) | (v17 >> 4);
676 output[o + 13] = (v17 << 4) | (v18 >> 2);
677 output[o + 14] = (v18 << 6) | v19;
678
679 let v20 = base64_char_to_sextet(encoded_data[i + 20], alphabet);
680 let v21 = base64_char_to_sextet(encoded_data[i + 21], alphabet);
681 let v22 = base64_char_to_sextet(encoded_data[i + 22], alphabet);
682 let v23 = base64_char_to_sextet(encoded_data[i + 23], alphabet);
683 err |= v20 | v21 | v22 | v23;
684 output[o + 15] = (v20 << 2) | (v21 >> 4);
685 output[o + 16] = (v21 << 4) | (v22 >> 2);
686 output[o + 17] = (v22 << 6) | v23;
687
688 let v24 = base64_char_to_sextet(encoded_data[i + 24], alphabet);
689 let v25 = base64_char_to_sextet(encoded_data[i + 25], alphabet);
690 let v26 = base64_char_to_sextet(encoded_data[i + 26], alphabet);
691 let v27 = base64_char_to_sextet(encoded_data[i + 27], alphabet);
692 err |= v24 | v25 | v26 | v27;
693 output[o + 18] = (v24 << 2) | (v25 >> 4);
694 output[o + 19] = (v25 << 4) | (v26 >> 2);
695 output[o + 20] = (v26 << 6) | v27;
696
697 let v28 = base64_char_to_sextet(encoded_data[i + 28], alphabet);
698 let v29 = base64_char_to_sextet(encoded_data[i + 29], alphabet);
699 let v30 = base64_char_to_sextet(encoded_data[i + 30], alphabet);
700 let v31 = base64_char_to_sextet(encoded_data[i + 31], alphabet);
701 err |= v28 | v29 | v30 | v31;
702 output[o + 21] = (v28 << 2) | (v29 >> 4);
703 output[o + 22] = (v29 << 4) | (v30 >> 2);
704 output[o + 23] = (v30 << 6) | v31;
705
706 i += 32;
707 o += 24;
708 }
709
710 while i + 4 <= content_len {
711 let v0 = base64_char_to_sextet(encoded_data[i], alphabet);
712 let v1 = base64_char_to_sextet(encoded_data[i + 1], alphabet);
713 let v2 = base64_char_to_sextet(encoded_data[i + 2], alphabet);
714 let v3 = base64_char_to_sextet(encoded_data[i + 3], alphabet);
715 err |= v0 | v1 | v2 | v3;
716 output[o] = (v0 << 2) | (v1 >> 4);
717 output[o + 1] = (v1 << 4) | (v2 >> 2);
718 output[o + 2] = (v2 << 6) | v3;
719 i += 4;
720 o += 3;
721 }
722
723 let remaining = content_len - i;
724 let rem2 = (remaining == 2) as u8;
725 let rem3 = (remaining == 3) as u8;
726 let rem0 = (remaining == 0) as u8;
727 let valid_rem = rem0 | rem2 | rem3;
728 err |= (1 - valid_rem) << 6;
729
730 let rem2_mask = 0u8.wrapping_sub(rem2);
731 let rem3_mask = 0u8.wrapping_sub(rem3);
732
733 if remaining > 0 {
734 let c0 = encoded_data[i];
735 let c1 = if i + 1 < content_len { encoded_data[i + 1] } else { b'A' };
736 let c2 = if i + 2 < content_len { encoded_data[i + 2] } else { b'A' };
737
738 let v0 = base64_char_to_sextet(c0, alphabet);
739 let v1 = base64_char_to_sextet(c1, alphabet);
740 let v2 = base64_char_to_sextet(c2, alphabet);
741
742 let m0 = 0u8.wrapping_sub((i < content_len) as u8);
743 let m1 = 0u8.wrapping_sub((i + 1 < content_len) as u8);
744 let m2 = 0u8.wrapping_sub((i + 2 < content_len) as u8);
745 err |= (v0 & m0) | (v1 & m1) | (v2 & m2);
746
747 let v1_trailing = v1 & 0x0F;
751 let v2_trailing = v2 & 0x03;
752 let trailing = (v1_trailing & rem2_mask) | (v2_trailing & rem3_mask);
753 err |= ((trailing != 0) as u8) << 6;
754
755 let out0 = (v0 << 2) | (v1 >> 4);
756 let out1 = (v1 << 4) | (v2 >> 2);
757
758 output[o] = out0;
759 if remaining == 3 {
760 output[o + 1] = out1;
761 }
762 }
763
764 if err >= 64 {
765 return Err(DecodeError::InvalidInput);
766 }
767
768 Ok(computed_output)
769}
770
771#[inline]
774const fn base64_char_to_sextet(c: u8, alphabet: Alphabet) -> u8 {
775 let not_upper = not_in_range(c, b'A', b'Z');
776 let not_lower = not_in_range(c, b'a', b'z');
777 let not_digit = not_in_range(c, b'0', b'9');
778
779 let upper_val = c.wrapping_sub(b'A');
780 let lower_val = c.wrapping_sub(b'a').wrapping_add(26);
781 let digit_val = c.wrapping_sub(b'0').wrapping_add(52);
782
783 let (ch_62, ch_63) = match alphabet {
784 Alphabet::Standard | Alphabet::StandardNoPadding => (b'+', b'/'),
785 Alphabet::Url | Alphabet::UrlNoPadding => (b'-', b'_'),
786 };
787 let not_62 = not_in_range(c, ch_62, ch_62);
788 let not_63 = not_in_range(c, ch_63, ch_63);
789
790 let value = (upper_val & !not_upper)
791 | (lower_val & !not_lower)
792 | (digit_val & !not_digit)
793 | (62 & !not_62)
794 | (63 & !not_63);
795
796 let invalid = not_upper & not_lower & not_digit & not_62 & not_63;
797 value | (invalid & 0x40)
798}
799
800pub(crate) const fn strip_padding_info(data: &[u8], expect_padding: bool) -> Result<(usize, usize), DecodeError> {
801 let in_len = data.len();
802
803 if !expect_padding {
804 let last_is_pad = if in_len > 0 { (data[in_len - 1] == PAD) as u8 } else { 0 };
805 if last_is_pad != 0 {
806 return Err(DecodeError::InvalidPadding);
807 }
808 return Ok((in_len, 0));
809 }
810
811 let b0 = if in_len > 0 { data[in_len - 1] } else { 0 };
814 let b1 = if in_len > 1 { data[in_len - 2] } else { 0 };
815 let b2 = if in_len > 2 { data[in_len - 3] } else { 0 };
816
817 let p0 = (b0 == PAD) as usize;
818 let p1 = (b1 == PAD) as usize;
819 let p2 = (b2 == PAD) as usize;
820
821 let pad_count = p0 + (p0 & p1) + (p0 & p1 & p2);
823 let content_len = in_len - pad_count;
824
825 let mut err_len: u8 = 0;
826 let mut err_pad: u8 = 0;
827
828 let has_pads = (pad_count != 0) as u8;
830 let len_mod4_ok = ((in_len & 3) == 0) as u8;
831 err_len |= has_pads & (1 - len_mod4_ok);
832
833 err_pad |= (pad_count > 2) as u8;
835
836 if err_len != 0 {
837 return Err(DecodeError::InvalidInputLength);
838 }
839
840 let content_mod4 = content_len & 3;
844 let expected_mod4 = 4usize.wrapping_sub(pad_count);
845 let mod4_ok = (content_mod4 == expected_mod4) as u8;
846 err_pad |= has_pads & (1 - mod4_ok);
847
848 if err_pad != 0 {
849 return Err(DecodeError::InvalidPadding);
850 }
851
852 Ok((content_len, pad_count))
853}
854
855pub(crate) const fn decoded_length(encoded_data_length: usize) -> Result<usize, DecodeError> {
858 let full_blocks = encoded_data_length / 4;
859 let rem = encoded_data_length % 4;
860
861 let base = match full_blocks.checked_mul(3) {
862 Some(v) => v,
863 None => return Err(DecodeError::InvalidInputLength),
864 };
865
866 match rem {
867 0 => Ok(base),
868 2 => match base.checked_add(1) {
869 Some(v) => Ok(v),
870 None => Err(DecodeError::InvalidInputLength),
871 },
872 3 => match base.checked_add(2) {
873 Some(v) => Ok(v),
874 None => Err(DecodeError::InvalidInputLength),
875 },
876 _ => Err(DecodeError::InvalidInputLength),
877 }
878}
879
880#[cfg(test)]
881mod tests {
882 use super::*;
883
884 const ENCODE_VECTORS: &[(&[u8], Alphabet, &str, &str)] = &[
886 (b"", Alphabet::Standard, "", "RFC4648: empty"),
888 (b"f", Alphabet::Standard, "Zg==", "RFC4648: 'f'"),
889 (b"fo", Alphabet::Standard, "Zm8=", "RFC4648: 'fo'"),
890 (b"foo", Alphabet::Standard, "Zm9v", "RFC4648: 'foo'"),
891 (b"foob", Alphabet::Standard, "Zm9vYg==", "RFC4648: 'foob'"),
892 (b"fooba", Alphabet::Standard, "Zm9vYmE=", "RFC4648: 'fooba'"),
893 (b"foobar", Alphabet::Standard, "Zm9vYmFy", "RFC4648: 'foobar'"),
894 (
896 &[0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e],
897 Alphabet::Standard,
898 "FPucA9l+",
899 "illustration: 6 bytes",
900 ),
901 (
902 &[0x14, 0xfb, 0x9c, 0x03, 0xd9],
903 Alphabet::Standard,
904 "FPucA9k=",
905 "illustration: 5 bytes",
906 ),
907 (
908 &[0x14, 0xfb, 0x9c, 0x03],
909 Alphabet::Standard,
910 "FPucAw==",
911 "illustration: 4 bytes",
912 ),
913 (b"Man", Alphabet::Standard, "TWFu", "illustration: 'Man'"),
914 (b"Ma", Alphabet::Standard, "TWE=", "illustration: 'Ma'"),
915 (b"M", Alphabet::Standard, "TQ==", "illustration: 'M'"),
916 (b"\x00", Alphabet::Standard, "AA==", "single byte 0x00"),
918 (b"\xFF", Alphabet::Standard, "/w==", "single byte 0xFF"),
919 (b"\xAB", Alphabet::Standard, "qw==", "single byte 0xAB"),
920 (b"\xFF", Alphabet::Url, "_w==", "single byte 0xFF URL"),
921 (b"\x00\x00", Alphabet::Standard, "AAA=", "two bytes 0x00"),
923 (b"\xFF\xFF", Alphabet::Standard, "//8=", "two bytes 0xFF"),
924 (b"bar", Alphabet::Standard, "YmFy", "three bytes 'bar'"),
926 (b"f", Alphabet::StandardNoPadding, "Zg", "no-pad: 'f'"),
928 (b"fo", Alphabet::StandardNoPadding, "Zm8", "no-pad: 'fo'"),
929 (b"foo", Alphabet::StandardNoPadding, "Zm9v", "no-pad: 'foo'"),
930 (b"\xFF\xEC\x20\x55\x00", Alphabet::Url, "_-wgVQA=", "URL padded"),
932 (b"\xFF\xEC\x20\x55\x00", Alphabet::UrlNoPadding, "_-wgVQA", "URL no-pad"),
933 ];
934
935 const DECODE_VECTORS: &[(&[u8], Alphabet, &[u8], &str)] = &[
937 (b"", Alphabet::Standard, b"", "RFC4648: empty"),
938 (b"Zg==", Alphabet::Standard, b"f", "RFC4648: 'f'"),
939 (b"Zm8=", Alphabet::Standard, b"fo", "RFC4648: 'fo'"),
940 (b"Zm9v", Alphabet::Standard, b"foo", "RFC4648: 'foo'"),
941 (b"Zm9vYg==", Alphabet::Standard, b"foob", "RFC4648: 'foob'"),
942 (b"Zm9vYmE=", Alphabet::Standard, b"fooba", "RFC4648: 'fooba'"),
943 (b"Zm9vYmFy", Alphabet::Standard, b"foobar", "RFC4648: 'foobar'"),
944 (b"AA==", Alphabet::Standard, b"\x00", "single byte 0x00"),
945 (b"/w==", Alphabet::Standard, b"\xFF", "single byte 0xFF"),
946 (b"qw==", Alphabet::Standard, b"\xAB", "single byte 0xAB"),
947 (b"AAA=", Alphabet::Standard, b"\x00\x00", "two bytes 0x00"),
948 (b"//8=", Alphabet::Standard, b"\xFF\xFF", "two bytes 0xFF"),
949 (b"Zg", Alphabet::StandardNoPadding, b"f", "no-pad: 'f'"),
950 (b"Zm8", Alphabet::StandardNoPadding, b"fo", "no-pad: 'fo'"),
951 (b"Zm9v", Alphabet::StandardNoPadding, b"foo", "no-pad: 'foo'"),
952 (b"_-wgVQA=", Alphabet::Url, b"\xFF\xEC\x20\x55\x00", "URL padded"),
953 (b"_-wgVQA", Alphabet::UrlNoPadding, b"\xFF\xEC\x20\x55\x00", "URL no-pad"),
954 ];
955
956 const DECODE_ERROR_VECTORS: &[(&[u8], Alphabet, DecodeError, &str)] = &[
958 (b"!!", Alphabet::Standard, DecodeError::InvalidInput, "two invalid chars"),
959 (
960 b"Zg!!",
961 Alphabet::Standard,
962 DecodeError::InvalidInput,
963 "valid prefix + invalid suffix",
964 ),
965 (b"!A==", Alphabet::Standard, DecodeError::InvalidInput, "invalid first char"),
966 (b"A", Alphabet::Standard, DecodeError::InvalidInputLength, "single char"),
967 (b"AAAAA", Alphabet::Standard, DecodeError::InvalidInputLength, "5 chars"),
968 (b"Z===", Alphabet::Standard, DecodeError::InvalidPadding, "1 content + 3 pads"),
969 (
970 b"Zg=A",
971 Alphabet::Standard,
972 DecodeError::InvalidInput,
973 "interior '=' before valid char",
974 ),
975 (
976 b"Zg===",
977 Alphabet::Standard,
978 DecodeError::InvalidInputLength,
979 "valid + 3 pads (invalid length)",
980 ),
981 (
982 b"Zg==",
983 Alphabet::StandardNoPadding,
984 DecodeError::InvalidPadding,
985 "no-pad rejects padding",
986 ),
987 (
988 b"Zm8=",
989 Alphabet::StandardNoPadding,
990 DecodeError::InvalidPadding,
991 "no-pad rejects padding 2 bytes",
992 ),
993 (b"=", Alphabet::Standard, DecodeError::InvalidInputLength, "single pad only"),
994 (b"==", Alphabet::Standard, DecodeError::InvalidInputLength, "double pad only"),
995 (b"A===", Alphabet::Standard, DecodeError::InvalidPadding, "1 content + 3 pads"),
996 ];
997
998 const ENCODED_LENGTH_VECTORS: &[(usize, bool, Option<usize>, &str)] = &[
1000 (0, true, Some(0), "empty padded"),
1001 (1, true, Some(4), "1 byte padded"),
1002 (2, true, Some(4), "2 bytes padded"),
1003 (3, true, Some(4), "3 bytes padded"),
1004 (4, true, Some(8), "4 bytes padded"),
1005 (5, true, Some(8), "5 bytes padded"),
1006 (0, false, Some(0), "empty unpadded"),
1007 (1, false, Some(2), "1 byte unpadded"),
1008 (2, false, Some(3), "2 bytes unpadded"),
1009 (3, false, Some(4), "3 bytes unpadded"),
1010 (4, false, Some(6), "4 bytes unpadded"),
1011 (5, false, Some(7), "5 bytes unpadded"),
1012 (usize::MAX, true, None, "overflow padded"),
1013 (usize::MAX, false, None, "overflow unpadded"),
1014 (usize::MAX / 4 * 3 + 3, true, None, "overflow at boundary"),
1015 ];
1016
1017 const ENCODE_INTO_STRING_VECTORS: &[(&str, &[u8], Alphabet, &str, &str)] = &[
1019 ("", b"", Alphabet::Standard, "", "empty"),
1020 ("prefix", b"", Alphabet::Standard, "prefix", "empty data with prefix"),
1021 ("", b"\x00", Alphabet::Standard, "AA==", "single null byte"),
1022 ("", b"fo", Alphabet::Standard, "Zm8=", "two bytes 'fo'"),
1023 ("", b"foo", Alphabet::Standard, "Zm9v", "three bytes 'foo'"),
1024 ("", b"f", Alphabet::StandardNoPadding, "Zg", "no-pad: 'f'"),
1025 ("", b"fo", Alphabet::StandardNoPadding, "Zm8", "no-pad: 'fo'"),
1026 ("", b"foo", Alphabet::StandardNoPadding, "Zm9v", "no-pad: 'foo'"),
1027 ("", b"\xFF\xEC\x20\x55\x00", Alphabet::Url, "_-wgVQA=", "URL padded"),
1028 ("", b"\xFF\xEC\x20\x55\x00", Alphabet::UrlNoPadding, "_-wgVQA", "URL no-pad"),
1029 ];
1030
1031 const ALL_ALPHABETS: &[Alphabet] = &[
1032 Alphabet::Standard,
1033 Alphabet::StandardNoPadding,
1034 Alphabet::Url,
1035 Alphabet::UrlNoPadding,
1036 ];
1037
1038 const ROUNDTRIP_SIZES: &[usize] = &[
1039 0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 23, 24, 25, 31, 32, 33, 47, 48, 49, 63, 64, 65, 127, 128, 129,
1040 ];
1041
1042 const SIMD_BOUNDARY_SIZES: &[usize] = &[
1043 22, 23, 24, 25, 26, 30, 31, 32, 33, 34, 46, 47, 48, 49, 50, 62, 63, 64, 65, 66,
1044 ];
1045
1046 #[test]
1047 fn test_encode() {
1048 for &(input, alphabet, expected, desc) in ENCODE_VECTORS {
1049 let result = encode(input, alphabet);
1050 assert_eq!(result, expected, "encode: {desc}");
1051 }
1052 }
1053
1054 #[test]
1055 fn test_decode() {
1056 for &(encoded, alphabet, expected, desc) in DECODE_VECTORS {
1057 let result = decode(encoded, alphabet).unwrap();
1058 assert_eq!(&result, expected, "decode: {desc}");
1059 }
1060 }
1061
1062 #[test]
1063 fn test_decode_error() {
1064 for &(encoded, alphabet, expected_err, desc) in DECODE_ERROR_VECTORS {
1065 let result = decode(encoded, alphabet);
1066 assert_eq!(result, Err(expected_err), "decode error: {desc}");
1067 }
1068
1069 let mut input = alloc::vec![b'A'; 32];
1071 input[31] = b'!';
1072 assert_eq!(decode(&input, Alphabet::Standard), Err(DecodeError::InvalidInput));
1073 }
1074
1075 #[test]
1076 fn test_encoded_length() {
1077 for &(data_len, padding, expected, desc) in ENCODED_LENGTH_VECTORS {
1078 let result = encoded_length(data_len, padding);
1079 assert_eq!(result, expected, "encoded_length: {desc}");
1080 }
1081 }
1082
1083 #[test]
1084 fn test_encode_into_string() {
1085 for &(initial, input, alphabet, expected, desc) in ENCODE_INTO_STRING_VECTORS {
1086 let mut s = alloc::string::String::from(initial);
1087 encode_into_string(&mut s, input, alphabet);
1088 assert_eq!(s, expected, "encode_into_string: {desc}");
1089 }
1090
1091 let mut s = alloc::string::String::from("~~");
1093 encode_into_string(&mut s, b"foo", Alphabet::Standard);
1094 assert_eq!(s, "~~Zm9v");
1095 encode_into_string(&mut s, b"bar", Alphabet::Standard);
1096 assert_eq!(s, "~~Zm9vYmFy");
1097
1098 for alphabet in ALL_ALPHABETS {
1099 let expected = encode(b"hello world", *alphabet);
1100 let mut s = alloc::string::String::new();
1101 encode_into_string(&mut s, b"hello world", *alphabet);
1102 assert_eq!(s, expected, "encode_into_string alphabet {alphabet:?}");
1103 }
1104 }
1105
1106 #[test]
1107 fn test_roundtrip() {
1108 for &len in ROUNDTRIP_SIZES {
1109 let data: Vec<u8> = (0..len as u8).collect();
1110 for alphabet in ALL_ALPHABETS {
1111 let encoded = encode(&data, *alphabet);
1112 let decoded = decode(encoded.as_bytes(), *alphabet).unwrap();
1113 assert_eq!(decoded, data, "roundtrip len={len} alphabet={alphabet:?}");
1114 }
1115 }
1116 }
1117
1118 #[test]
1119 fn test_roundtrip_large() {
1120 let size = 4096;
1121
1122 let data = alloc::vec![0x00u8; size];
1123 let elen = encoded_length(size, true).expect("encoded_len overflow");
1124 let mut encoded = alloc::vec![0u8; elen];
1125 encode_into_constant_time(&mut encoded, &data, Alphabet::Standard).unwrap();
1126 let mut decoded = alloc::vec![0u8; size];
1127 decode_into_constant_time(&mut decoded, &encoded, Alphabet::Standard).unwrap();
1128 assert_eq!(decoded, data, "4096 zeroes constant-time");
1129
1130 let data = alloc::vec![0xFFu8; size];
1131 let mut encoded = alloc::vec![0u8; elen];
1132 encode_into_constant_time(&mut encoded, &data, Alphabet::Standard).unwrap();
1133 decode_into_constant_time(&mut decoded, &encoded, Alphabet::Standard).unwrap();
1134 assert_eq!(decoded, data, "4096 0xFF constant-time");
1135
1136 let data: Vec<u8> = (0..=255).cycle().take(size).collect();
1137 let encoded = encode(&data, Alphabet::Standard);
1138 let decoded = decode(encoded.as_bytes(), Alphabet::Standard).unwrap();
1139 assert_eq!(decoded, data, "4096 cycle dispatch");
1140
1141 let data: Vec<u8> = (0..=255).collect();
1142 let mut s = alloc::string::String::new();
1143 encode_into_string(&mut s, &data, Alphabet::Standard);
1144 let decoded = decode(s.as_bytes(), Alphabet::Standard).unwrap();
1145 assert_eq!(decoded, data, "256-byte encode_into_string roundtrip");
1146
1147 let data: Vec<u8> = (0..255).cycle().take(4096).collect();
1148 let expected = encode(&data, Alphabet::Standard);
1149 let mut s = alloc::string::String::new();
1150 encode_into_string(&mut s, &data, Alphabet::Standard);
1151 assert_eq!(s, expected, "4096-byte encode_into_string");
1152 }
1153
1154 #[test]
1155 fn test_encode_all_single_bytes() {
1156 for byte in 0..=255u8 {
1157 for alphabet in &[Alphabet::Standard, Alphabet::Url] {
1158 let padding = alphabet.is_padded();
1159 let elen = encoded_length(1, padding).unwrap();
1160 let mut encoded = alloc::vec![0u8; elen];
1161 encode_into_constant_time(&mut encoded, &[byte], *alphabet).unwrap();
1162 let mut decoded = [0u8; 1];
1163 decode_into_constant_time(&mut decoded, &encoded, *alphabet).unwrap();
1164 assert_eq!(decoded[0], byte, "single byte roundtrip {byte:#04x} alphabet={alphabet:?}");
1165 }
1166 }
1167 }
1168
1169 #[test]
1170 fn test_decode_invalid_char_every_position() {
1171 let mut out = [0u8; 32];
1172
1173 for pos in 0..32 {
1174 let mut input = [b'A'; 32];
1175 input[pos] = b'!';
1176 assert_eq!(
1177 decode_into_constant_time(&mut out, &input, Alphabet::Standard),
1178 Err(DecodeError::InvalidInput),
1179 "invalid char at position {pos} in 32-byte block"
1180 );
1181 }
1182
1183 for pos in 0..36 {
1184 let mut input = [b'A'; 36];
1185 input[pos] = b'!';
1186 assert_eq!(
1187 decode_into_constant_time(&mut out, &input, Alphabet::Standard),
1188 Err(DecodeError::InvalidInput),
1189 "invalid char at position {pos} in 36-byte block"
1190 );
1191 }
1192 }
1193
1194 #[test]
1195 fn test_decode_non_canonical_trailing_bits() {
1196 let mut out = [0u8; 2];
1197
1198 for &(input, expected) in &[
1200 (b"/w==" as &[u8], Ok(())),
1201 (b"/x==" as &[u8], Err(DecodeError::InvalidInput)),
1202 (b"/y==" as &[u8], Err(DecodeError::InvalidInput)),
1203 (b"/z==" as &[u8], Err(DecodeError::InvalidInput)),
1204 ] {
1205 assert_eq!(
1206 decode_into_constant_time(&mut out, input, Alphabet::Standard).map(|_| ()),
1207 expected,
1208 "non-canonical (rem=2): {:?}",
1209 core::str::from_utf8(input)
1210 );
1211 }
1212
1213 for &(input, expected) in &[
1215 (b"iYU=" as &[u8], Ok(())),
1216 (b"iYV=" as &[u8], Err(DecodeError::InvalidInput)),
1217 (b"iYW=" as &[u8], Err(DecodeError::InvalidInput)),
1218 (b"iYX=" as &[u8], Err(DecodeError::InvalidInput)),
1219 ] {
1220 assert_eq!(
1221 decode_into_constant_time(&mut out, input, Alphabet::Standard).map(|_| ()),
1222 expected,
1223 "non-canonical (rem=3): {:?}",
1224 core::str::from_utf8(input)
1225 );
1226 }
1227 }
1228
1229 #[test]
1230 fn test_decode_rejects_interior_padding() {
1231 let mut out = [0u8; 4];
1232 assert_eq!(
1233 decode_into_constant_time(&mut out, b"A=AA", Alphabet::Standard),
1234 Err(DecodeError::InvalidInput)
1235 );
1236 assert_eq!(
1237 decode_into_constant_time(&mut out, b"AA=A", Alphabet::Standard),
1238 Err(DecodeError::InvalidInput)
1239 );
1240 assert_eq!(
1241 decode_into_constant_time(&mut out, b"AA==", Alphabet::StandardNoPadding),
1242 Err(DecodeError::InvalidPadding)
1243 );
1244 }
1245
1246 #[test]
1247 fn test_roundtrip_simd_boundary_sizes() {
1248 let mut data_buf = Vec::new();
1249 let mut enc_buf = Vec::new();
1250
1251 for &input_len in SIMD_BOUNDARY_SIZES {
1252 data_buf.clear();
1253 for b in 0..input_len {
1254 data_buf.push(b as u8);
1255 }
1256
1257 for alphabet in ALL_ALPHABETS {
1258 let padding = alphabet.is_padded();
1259 let encoded_length = encoded_length(input_len, padding).expect("encoded_len overflow");
1260 enc_buf.resize(encoded_length, 0);
1261 encode_into_constant_time(&mut enc_buf, &data_buf, *alphabet).unwrap();
1262
1263 let mut decoded = alloc::vec![0u8; input_len];
1264 assert_eq!(
1265 decode_into_constant_time(&mut decoded, &enc_buf, *alphabet),
1266 Ok(input_len),
1267 "decode failed len={input_len} alphabet={alphabet:?}"
1268 );
1269 assert_eq!(&decoded, &data_buf, "roundtrip mismatch len={input_len} alphabet={alphabet:?}");
1270 }
1271 }
1272 }
1273
1274 #[test]
1275 fn test_const_encode() {
1276 const DATA: [u8; 3] = [0x66, 0x6F, 0x6F];
1277 const B64: [u8; 4] = encode_array::<4>(&DATA, Alphabet::Standard);
1278 assert_eq!(&B64, b"Zm9v");
1279
1280 const B64_URL: [u8; 4] = encode_array::<4>(&DATA, Alphabet::Url);
1281 assert_eq!(&B64_URL, b"Zm9v");
1282
1283 const B64_EMPTY: [u8; 0] = encode_array::<0>(b"", Alphabet::Standard);
1284 assert_eq!(B64_EMPTY.len(), 0);
1285
1286 const NOPAD_DATA: [u8; 2] = [0x66, 0x6F];
1287 const B64_NOPAD: [u8; 3] = encode_array::<3>(&NOPAD_DATA, Alphabet::StandardNoPadding);
1288 assert_eq!(&B64_NOPAD, b"Zm8");
1289 }
1290
1291 #[test]
1292 fn test_const_decode() {
1293 const RESULT: Result<[u8; 3], DecodeError> = decode_array::<3>(b"Zm9v", Alphabet::Standard);
1294 assert_eq!(RESULT.unwrap(), [0x66, 0x6F, 0x6F]);
1295
1296 const RESULT_EMPTY: Result<[u8; 0], DecodeError> = decode_array::<0>(b"", Alphabet::Standard);
1297 assert_eq!(RESULT_EMPTY.unwrap().len(), 0);
1298
1299 const RESULT_NOPAD: Result<[u8; 2], DecodeError> = decode_array::<2>(b"Zm8", Alphabet::StandardNoPadding);
1300 assert_eq!(RESULT_NOPAD.unwrap(), [0x66, 0x6F]);
1301 }
1302
1303 #[test]
1304 fn test_const_decode_error() {
1305 const ERR_INVALID: Result<[u8; 1], DecodeError> = decode_array::<1>(b"!!", Alphabet::Standard);
1306 assert_eq!(ERR_INVALID, Err(DecodeError::InvalidInput));
1307
1308 const ERR_SIZE: Result<[u8; 0], DecodeError> = decode_array::<0>(b"Zg==", Alphabet::Standard);
1309 assert_eq!(ERR_SIZE, Err(DecodeError::InvalidOutputLength));
1310 }
1311
1312 #[test]
1313 fn test_buffer_management() {
1314 let mut out = [0u8; 1];
1315 assert_eq!(
1316 encode_into(&mut out, b"hello", Alphabet::Standard),
1317 Err(EncodeError::InvalidOutputLength)
1318 );
1319
1320 let mut out = [0u8; 3];
1321 decode_into(&mut out, b"Zm9v", Alphabet::Standard).unwrap();
1322 assert_eq!(&out, b"foo");
1323
1324 let mut out = [0u8; 2];
1325 assert_eq!(
1326 decode_into(&mut out, b"Zm9v", Alphabet::Standard),
1327 Err(DecodeError::InvalidOutputLength)
1328 );
1329 }
1330
1331 #[test]
1332 fn test_display_error() {
1333 assert_eq!(format!("{}", DecodeError::InvalidInput), "invalid base64 character");
1334 assert_eq!(format!("{}", DecodeError::InvalidInputLength), "invalid base64 length");
1335 assert_eq!(format!("{}", DecodeError::InvalidPadding), "invalid base64 padding");
1336 assert_eq!(
1337 format!("{}", EncodeError::InvalidOutputLength),
1338 "output buffer size must be exactly equal to decoded_len(input)"
1339 );
1340 assert_eq!(format!("{}", EncodeError::OutputOverflow), "output length overflows usize::MAX");
1341 }
1342
1343 #[cfg(feature = "serde")]
1344 #[test]
1345 fn test_serde() {
1346 #[derive(::serde::Serialize, ::serde::Deserialize)]
1347 struct Data(#[serde(with = "crate::serde")] Vec<u8>);
1348
1349 let data = Data(b"hello world".to_vec());
1350 let json = ::serde_json::to_string(&data).unwrap();
1351 let deserialized: Data = ::serde_json::from_str(&json).unwrap();
1352 assert_eq!(deserialized.0, b"hello world");
1353 }
1354}