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