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