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