1#![cfg_attr(not(feature = "std"), no_std)]
2#![deny(unsafe_code)]
3#![deny(missing_docs)]
4#![deny(clippy::all)]
5#![deny(clippy::pedantic)]
6#![allow(clippy::missing_errors_doc)]
7
8#[cfg(feature = "alloc")]
46extern crate alloc;
47
48#[cfg(feature = "simd")]
49mod simd;
50
51pub mod runtime {
57 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
59 #[non_exhaustive]
60 pub enum Backend {
61 Scalar,
63 Avx512Vbmi,
65 Avx2,
67 Neon,
69 }
70
71 impl Backend {
72 #[must_use]
78 pub const fn as_str(self) -> &'static str {
79 match self {
80 Self::Scalar => "scalar",
81 Self::Avx512Vbmi => "avx512-vbmi",
82 Self::Avx2 => "avx2",
83 Self::Neon => "neon",
84 }
85 }
86
87 #[must_use]
100 pub const fn required_cpu_features(self) -> &'static [&'static str] {
101 match self {
102 Self::Scalar => &[],
103 Self::Avx512Vbmi => &["avx512f", "avx512bw", "avx512vl", "avx512vbmi"],
104 Self::Avx2 => &["avx2"],
105 Self::Neon => &["neon"],
106 }
107 }
108 }
109
110 impl core::fmt::Display for Backend {
111 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112 formatter.write_str(self.as_str())
113 }
114 }
115
116 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
118 #[non_exhaustive]
119 pub enum SecurityPosture {
120 ScalarOnly,
122 SimdCandidateScalarActive,
124 Accelerated,
126 }
127
128 impl SecurityPosture {
129 #[must_use]
138 pub const fn as_str(self) -> &'static str {
139 match self {
140 Self::ScalarOnly => "scalar-only",
141 Self::SimdCandidateScalarActive => "simd-candidate-scalar-active",
142 Self::Accelerated => "accelerated",
143 }
144 }
145 }
146
147 impl core::fmt::Display for SecurityPosture {
148 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
149 formatter.write_str(self.as_str())
150 }
151 }
152
153 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
155 #[non_exhaustive]
156 pub enum BackendPolicy {
157 ScalarExecutionOnly,
159 SimdFeatureDisabled,
161 NoDetectedSimdCandidate,
163 HighAssuranceScalarOnly,
166 }
167
168 impl BackendPolicy {
169 #[must_use]
178 pub const fn as_str(self) -> &'static str {
179 match self {
180 Self::ScalarExecutionOnly => "scalar-execution-only",
181 Self::SimdFeatureDisabled => "simd-feature-disabled",
182 Self::NoDetectedSimdCandidate => "no-detected-simd-candidate",
183 Self::HighAssuranceScalarOnly => "high-assurance-scalar-only",
184 }
185 }
186 }
187
188 impl core::fmt::Display for BackendPolicy {
189 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
190 formatter.write_str(self.as_str())
191 }
192 }
193
194 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
196 pub struct BackendPolicyError {
197 pub policy: BackendPolicy,
199 pub report: BackendReport,
201 }
202
203 impl core::fmt::Display for BackendPolicyError {
204 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
205 write!(
206 formatter,
207 "runtime backend policy `{}` was not satisfied ({})",
208 self.policy, self.report,
209 )
210 }
211 }
212
213 #[cfg(feature = "std")]
214 impl std::error::Error for BackendPolicyError {}
215
216 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
218 pub struct BackendReport {
219 pub active: Backend,
221 pub candidate: Backend,
223 pub simd_feature_enabled: bool,
225 pub accelerated_backend_active: bool,
227 pub unsafe_boundary_enforced: bool,
229 pub security_posture: SecurityPosture,
231 }
232
233 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
235 pub struct BackendSnapshot {
236 pub active: &'static str,
238 pub candidate: &'static str,
240 pub candidate_required_cpu_features: &'static [&'static str],
242 pub simd_feature_enabled: bool,
244 pub accelerated_backend_active: bool,
246 pub unsafe_boundary_enforced: bool,
248 pub security_posture: &'static str,
250 }
251
252 impl core::fmt::Display for BackendReport {
253 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
254 write!(
255 formatter,
256 "active={} candidate={} candidate_required_cpu_features=",
257 self.active, self.candidate,
258 )?;
259 write_feature_list(formatter, self.candidate_required_cpu_features())?;
260 write!(
261 formatter,
262 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={}",
263 self.simd_feature_enabled,
264 self.accelerated_backend_active,
265 self.unsafe_boundary_enforced,
266 self.security_posture,
267 )
268 }
269 }
270
271 impl BackendReport {
272 #[must_use]
282 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
283 match policy {
284 BackendPolicy::ScalarExecutionOnly => {
285 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
286 }
287 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
288 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
289 BackendPolicy::HighAssuranceScalarOnly => {
290 matches!(self.active, Backend::Scalar)
291 && matches!(self.candidate, Backend::Scalar)
292 && !self.simd_feature_enabled
293 && !self.accelerated_backend_active
294 && self.unsafe_boundary_enforced
295 }
296 }
297 }
298
299 #[must_use]
310 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
311 self.candidate.required_cpu_features()
312 }
313
314 #[must_use]
323 pub const fn snapshot(self) -> BackendSnapshot {
324 BackendSnapshot {
325 active: self.active.as_str(),
326 candidate: self.candidate.as_str(),
327 candidate_required_cpu_features: self.candidate_required_cpu_features(),
328 simd_feature_enabled: self.simd_feature_enabled,
329 accelerated_backend_active: self.accelerated_backend_active,
330 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
331 security_posture: self.security_posture.as_str(),
332 }
333 }
334 }
335
336 #[must_use]
345 pub fn backend_report() -> BackendReport {
346 let active = active_backend();
347 let candidate = detected_candidate();
348 let accelerated_backend_active = active != Backend::Scalar;
349 let security_posture = if accelerated_backend_active {
350 SecurityPosture::Accelerated
351 } else if candidate != Backend::Scalar {
352 SecurityPosture::SimdCandidateScalarActive
353 } else {
354 SecurityPosture::ScalarOnly
355 };
356
357 BackendReport {
358 active,
359 candidate,
360 simd_feature_enabled: cfg!(feature = "simd"),
361 accelerated_backend_active,
362 unsafe_boundary_enforced: true,
363 security_posture,
364 }
365 }
366
367 pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
376 let report = backend_report();
377 if report.satisfies(policy) {
378 Ok(())
379 } else {
380 Err(BackendPolicyError { policy, report })
381 }
382 }
383
384 fn write_feature_list(
385 formatter: &mut core::fmt::Formatter<'_>,
386 features: &[&str],
387 ) -> core::fmt::Result {
388 formatter.write_str("[")?;
389 let mut index = 0;
390 while index < features.len() {
391 if index != 0 {
392 formatter.write_str(",")?;
393 }
394 formatter.write_str(features[index])?;
395 index += 1;
396 }
397 formatter.write_str("]")
398 }
399
400 #[cfg(feature = "simd")]
401 fn active_backend() -> Backend {
402 match super::simd::active_backend() {
403 super::simd::ActiveBackend::Scalar => Backend::Scalar,
404 }
405 }
406
407 #[cfg(not(feature = "simd"))]
408 const fn active_backend() -> Backend {
409 Backend::Scalar
410 }
411
412 #[cfg(feature = "simd")]
413 fn detected_candidate() -> Backend {
414 match super::simd::detected_candidate() {
415 super::simd::Candidate::Scalar => Backend::Scalar,
416 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
417 super::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
418 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
419 super::simd::Candidate::Avx2 => Backend::Avx2,
420 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
421 super::simd::Candidate::Neon => Backend::Neon,
422 }
423 }
424
425 #[cfg(not(feature = "simd"))]
426 const fn detected_candidate() -> Backend {
427 Backend::Scalar
428 }
429}
430
431#[cfg(feature = "stream")]
432pub mod stream {
433 use super::{Alphabet, DecodeError, EncodeError, Engine};
463 use std::io::{self, Read, Write};
464
465 struct OutputQueue<const CAP: usize> {
466 buffer: [u8; CAP],
467 start: usize,
468 len: usize,
469 }
470
471 impl<const CAP: usize> OutputQueue<CAP> {
472 const fn new() -> Self {
473 Self {
474 buffer: [0; CAP],
475 start: 0,
476 len: 0,
477 }
478 }
479
480 const fn is_empty(&self) -> bool {
481 self.len == 0
482 }
483
484 fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
485 if input.len() > self.available_capacity() {
486 return Err(io::Error::new(
487 io::ErrorKind::InvalidInput,
488 "base64 stream output queue capacity exceeded",
489 ));
490 }
491
492 let mut read = 0;
493 while read < input.len() {
494 let write = (self.start + self.len) % CAP;
495 self.buffer[write] = input[read];
496 self.len += 1;
497 read += 1;
498 }
499
500 Ok(())
501 }
502
503 fn pop_front(&mut self) -> Option<u8> {
504 if self.len == 0 {
505 return None;
506 }
507
508 let byte = self.buffer[self.start];
509 self.buffer[self.start] = 0;
510 self.start = (self.start + 1) % CAP;
511 self.len -= 1;
512 if self.len == 0 {
513 self.start = 0;
514 }
515 Some(byte)
516 }
517
518 fn clear_all(&mut self) {
519 self.buffer.fill(0);
520 self.start = 0;
521 self.len = 0;
522 }
523
524 const fn available_capacity(&self) -> usize {
525 CAP - self.len
526 }
527 }
528
529 pub struct Encoder<W, A, const PAD: bool>
531 where
532 A: Alphabet,
533 {
534 inner: Option<W>,
535 engine: Engine<A, PAD>,
536 pending: [u8; 2],
537 pending_len: usize,
538 }
539
540 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
541 where
542 A: Alphabet,
543 {
544 #[must_use]
546 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
547 Self {
548 inner: Some(inner),
549 engine,
550 pending: [0; 2],
551 pending_len: 0,
552 }
553 }
554
555 #[must_use]
557 pub fn get_ref(&self) -> &W {
558 self.inner_ref()
559 }
560
561 pub fn get_mut(&mut self) -> &mut W {
563 self.inner_mut()
564 }
565
566 #[must_use]
570 pub fn into_inner(mut self) -> W {
571 self.take_inner()
572 }
573
574 fn inner_ref(&self) -> &W {
575 match &self.inner {
576 Some(inner) => inner,
577 None => unreachable!("stream encoder inner writer was already taken"),
578 }
579 }
580
581 fn inner_mut(&mut self) -> &mut W {
582 match &mut self.inner {
583 Some(inner) => inner,
584 None => unreachable!("stream encoder inner writer was already taken"),
585 }
586 }
587
588 fn take_inner(&mut self) -> W {
589 match self.inner.take() {
590 Some(inner) => inner,
591 None => unreachable!("stream encoder inner writer was already taken"),
592 }
593 }
594
595 fn clear_pending(&mut self) {
596 self.pending.fill(0);
597 self.pending_len = 0;
598 }
599 }
600
601 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
602 where
603 A: Alphabet,
604 {
605 fn drop(&mut self) {
606 self.clear_pending();
607 }
608 }
609
610 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
611 where
612 W: Write,
613 A: Alphabet,
614 {
615 pub fn finish(mut self) -> io::Result<W> {
617 self.write_pending_final()?;
618 self.inner_mut().flush()?;
619 Ok(self.take_inner())
620 }
621
622 fn write_pending_final(&mut self) -> io::Result<()> {
623 if self.pending_len == 0 {
624 return Ok(());
625 }
626
627 let mut encoded = [0u8; 4];
628 let written = self
629 .engine
630 .encode_slice(&self.pending[..self.pending_len], &mut encoded)
631 .map_err(encode_error_to_io)?;
632 self.inner_mut().write_all(&encoded[..written])?;
633 self.clear_pending();
634 Ok(())
635 }
636 }
637
638 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
639 where
640 W: Write,
641 A: Alphabet,
642 {
643 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
644 if input.is_empty() {
645 return Ok(0);
646 }
647
648 let mut consumed = 0;
649 if self.pending_len > 0 {
650 let needed = 3 - self.pending_len;
651 if input.len() < needed {
652 self.pending[self.pending_len..self.pending_len + input.len()]
653 .copy_from_slice(input);
654 self.pending_len += input.len();
655 return Ok(input.len());
656 }
657
658 let mut chunk = [0u8; 3];
659 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
660 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
661
662 let mut encoded = [0u8; 4];
663 let written = self
664 .engine
665 .encode_slice(&chunk, &mut encoded)
666 .map_err(encode_error_to_io)?;
667 self.inner_mut().write_all(&encoded[..written])?;
668 self.clear_pending();
669 consumed += needed;
670 }
671
672 let remaining = &input[consumed..];
673 let full_len = remaining.len() / 3 * 3;
674 let mut offset = 0;
675 let mut encoded = [0u8; 1024];
676 while offset < full_len {
677 let mut take = core::cmp::min(full_len - offset, 768);
678 take -= take % 3;
679 debug_assert!(take > 0);
680
681 let written = self
682 .engine
683 .encode_slice(&remaining[offset..offset + take], &mut encoded)
684 .map_err(encode_error_to_io)?;
685 self.inner_mut().write_all(&encoded[..written])?;
686 offset += take;
687 }
688
689 let tail = &remaining[full_len..];
690 self.pending[..tail.len()].copy_from_slice(tail);
691 self.pending_len = tail.len();
692
693 Ok(input.len())
694 }
695
696 fn flush(&mut self) -> io::Result<()> {
697 self.inner_mut().flush()
698 }
699 }
700
701 fn encode_error_to_io(err: EncodeError) -> io::Error {
702 io::Error::new(io::ErrorKind::InvalidInput, err)
703 }
704
705 pub struct Decoder<W, A, const PAD: bool>
707 where
708 A: Alphabet,
709 {
710 inner: Option<W>,
711 engine: Engine<A, PAD>,
712 pending: [u8; 4],
713 pending_len: usize,
714 finished: bool,
715 }
716
717 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
718 where
719 A: Alphabet,
720 {
721 #[must_use]
723 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
724 Self {
725 inner: Some(inner),
726 engine,
727 pending: [0; 4],
728 pending_len: 0,
729 finished: false,
730 }
731 }
732
733 #[must_use]
735 pub fn get_ref(&self) -> &W {
736 self.inner_ref()
737 }
738
739 pub fn get_mut(&mut self) -> &mut W {
741 self.inner_mut()
742 }
743
744 #[must_use]
748 pub fn into_inner(mut self) -> W {
749 self.take_inner()
750 }
751
752 fn inner_ref(&self) -> &W {
753 match &self.inner {
754 Some(inner) => inner,
755 None => unreachable!("stream decoder inner writer was already taken"),
756 }
757 }
758
759 fn inner_mut(&mut self) -> &mut W {
760 match &mut self.inner {
761 Some(inner) => inner,
762 None => unreachable!("stream decoder inner writer was already taken"),
763 }
764 }
765
766 fn take_inner(&mut self) -> W {
767 match self.inner.take() {
768 Some(inner) => inner,
769 None => unreachable!("stream decoder inner writer was already taken"),
770 }
771 }
772
773 fn clear_pending(&mut self) {
774 self.pending.fill(0);
775 self.pending_len = 0;
776 }
777 }
778
779 impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
780 where
781 A: Alphabet,
782 {
783 fn drop(&mut self) {
784 self.clear_pending();
785 }
786 }
787
788 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
789 where
790 W: Write,
791 A: Alphabet,
792 {
793 pub fn finish(mut self) -> io::Result<W> {
795 self.write_pending_final()?;
796 self.inner_mut().flush()?;
797 Ok(self.take_inner())
798 }
799
800 fn write_pending_final(&mut self) -> io::Result<()> {
801 if self.pending_len == 0 {
802 return Ok(());
803 }
804
805 let mut decoded = [0u8; 3];
806 let written = self
807 .engine
808 .decode_slice(&self.pending[..self.pending_len], &mut decoded)
809 .map_err(decode_error_to_io)?;
810 self.inner_mut().write_all(&decoded[..written])?;
811 self.clear_pending();
812 Ok(())
813 }
814
815 fn write_full_quad(&mut self, input: [u8; 4]) -> io::Result<()> {
816 let mut decoded = [0u8; 3];
817 let written = self
818 .engine
819 .decode_slice(&input, &mut decoded)
820 .map_err(decode_error_to_io)?;
821 self.inner_mut().write_all(&decoded[..written])?;
822 if written < 3 {
823 self.finished = true;
824 }
825 Ok(())
826 }
827 }
828
829 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
830 where
831 W: Write,
832 A: Alphabet,
833 {
834 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
835 if input.is_empty() {
836 return Ok(0);
837 }
838 if self.finished {
839 return Err(trailing_input_after_padding_error());
840 }
841
842 let mut consumed = 0;
843 if self.pending_len > 0 {
844 let needed = 4 - self.pending_len;
845 if input.len() < needed {
846 self.pending[self.pending_len..self.pending_len + input.len()]
847 .copy_from_slice(input);
848 self.pending_len += input.len();
849 return Ok(input.len());
850 }
851
852 let mut quad = [0u8; 4];
853 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
854 quad[self.pending_len..].copy_from_slice(&input[..needed]);
855 self.write_full_quad(quad)?;
856 self.clear_pending();
857 consumed += needed;
858 if self.finished && consumed < input.len() {
859 return Err(trailing_input_after_padding_error());
860 }
861 }
862
863 let remaining = &input[consumed..];
864 let full_len = remaining.len() / 4 * 4;
865 let mut offset = 0;
866 while offset < full_len {
867 let quad = [
868 remaining[offset],
869 remaining[offset + 1],
870 remaining[offset + 2],
871 remaining[offset + 3],
872 ];
873 self.write_full_quad(quad)?;
874 offset += 4;
875 if self.finished && offset < remaining.len() {
876 return Err(trailing_input_after_padding_error());
877 }
878 }
879
880 let tail = &remaining[full_len..];
881 self.pending[..tail.len()].copy_from_slice(tail);
882 self.pending_len = tail.len();
883
884 Ok(input.len())
885 }
886
887 fn flush(&mut self) -> io::Result<()> {
888 self.inner_mut().flush()
889 }
890 }
891
892 fn decode_error_to_io(err: DecodeError) -> io::Error {
893 io::Error::new(io::ErrorKind::InvalidInput, err)
894 }
895
896 fn trailing_input_after_padding_error() -> io::Error {
897 io::Error::new(
898 io::ErrorKind::InvalidInput,
899 "base64 decoder received trailing input after padding",
900 )
901 }
902
903 pub struct DecoderReader<R, A, const PAD: bool>
910 where
911 A: Alphabet,
912 {
913 inner: Option<R>,
914 engine: Engine<A, PAD>,
915 pending: [u8; 4],
916 pending_len: usize,
917 output: OutputQueue<3>,
918 finished: bool,
919 terminal_seen: bool,
920 }
921
922 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
923 where
924 A: Alphabet,
925 {
926 #[must_use]
928 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
929 Self {
930 inner: Some(inner),
931 engine,
932 pending: [0; 4],
933 pending_len: 0,
934 output: OutputQueue::new(),
935 finished: false,
936 terminal_seen: false,
937 }
938 }
939
940 #[must_use]
942 pub fn get_ref(&self) -> &R {
943 self.inner_ref()
944 }
945
946 pub fn get_mut(&mut self) -> &mut R {
948 self.inner_mut()
949 }
950
951 #[must_use]
953 pub fn into_inner(mut self) -> R {
954 self.take_inner()
955 }
956
957 fn inner_ref(&self) -> &R {
958 match &self.inner {
959 Some(inner) => inner,
960 None => unreachable!("stream decoder reader inner reader was already taken"),
961 }
962 }
963
964 fn inner_mut(&mut self) -> &mut R {
965 match &mut self.inner {
966 Some(inner) => inner,
967 None => unreachable!("stream decoder reader inner reader was already taken"),
968 }
969 }
970
971 fn take_inner(&mut self) -> R {
972 match self.inner.take() {
973 Some(inner) => inner,
974 None => unreachable!("stream decoder reader inner reader was already taken"),
975 }
976 }
977
978 fn clear_pending(&mut self) {
979 self.pending.fill(0);
980 self.pending_len = 0;
981 }
982 }
983
984 impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
985 where
986 A: Alphabet,
987 {
988 fn drop(&mut self) {
989 self.clear_pending();
990 self.output.clear_all();
991 }
992 }
993
994 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
995 where
996 R: Read,
997 A: Alphabet,
998 {
999 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1000 if output.is_empty() {
1001 return Ok(0);
1002 }
1003
1004 while self.output.is_empty() && !self.finished {
1005 self.fill_output()?;
1006 }
1007
1008 let mut written = 0;
1009 while written < output.len() {
1010 let Some(byte) = self.output.pop_front() else {
1011 break;
1012 };
1013 output[written] = byte;
1014 written += 1;
1015 }
1016
1017 Ok(written)
1018 }
1019 }
1020
1021 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1022 where
1023 R: Read,
1024 A: Alphabet,
1025 {
1026 fn fill_output(&mut self) -> io::Result<()> {
1027 if self.terminal_seen {
1028 self.finished = true;
1029 return Ok(());
1030 }
1031
1032 let mut input = [0u8; 4];
1033 let available = 4 - self.pending_len;
1034 let read = self.inner_mut().read(&mut input[..available])?;
1035 if read == 0 {
1036 self.finished = true;
1037 self.push_final_pending()?;
1038 return Ok(());
1039 }
1040
1041 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1042 self.pending_len += read;
1043 if self.pending_len < 4 {
1044 return Ok(());
1045 }
1046
1047 let quad = self.pending;
1048 self.clear_pending();
1049 self.push_decoded(&quad)?;
1050 if self.terminal_seen {
1051 self.finished = true;
1052 }
1053 Ok(())
1054 }
1055
1056 fn push_final_pending(&mut self) -> io::Result<()> {
1057 if self.pending_len == 0 {
1058 return Ok(());
1059 }
1060
1061 let mut pending = [0u8; 4];
1062 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1063 let pending_len = self.pending_len;
1064 self.clear_pending();
1065 self.push_decoded(&pending[..pending_len])
1066 }
1067
1068 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1069 let mut decoded = [0u8; 3];
1070 let written = self
1071 .engine
1072 .decode_slice(input, &mut decoded)
1073 .map_err(decode_error_to_io)?;
1074 self.output.push_slice(&decoded[..written])?;
1075 if input.len() == 4 && written < 3 {
1076 self.terminal_seen = true;
1077 }
1078 Ok(())
1079 }
1080 }
1081
1082 pub struct EncoderReader<R, A, const PAD: bool>
1084 where
1085 A: Alphabet,
1086 {
1087 inner: Option<R>,
1088 engine: Engine<A, PAD>,
1089 pending: [u8; 2],
1090 pending_len: usize,
1091 output: OutputQueue<1024>,
1092 finished: bool,
1093 }
1094
1095 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1096 where
1097 A: Alphabet,
1098 {
1099 #[must_use]
1101 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1102 Self {
1103 inner: Some(inner),
1104 engine,
1105 pending: [0; 2],
1106 pending_len: 0,
1107 output: OutputQueue::new(),
1108 finished: false,
1109 }
1110 }
1111
1112 #[must_use]
1114 pub fn get_ref(&self) -> &R {
1115 self.inner_ref()
1116 }
1117
1118 pub fn get_mut(&mut self) -> &mut R {
1120 self.inner_mut()
1121 }
1122
1123 #[must_use]
1125 pub fn into_inner(mut self) -> R {
1126 self.take_inner()
1127 }
1128
1129 fn inner_ref(&self) -> &R {
1130 match &self.inner {
1131 Some(inner) => inner,
1132 None => unreachable!("stream encoder reader inner reader was already taken"),
1133 }
1134 }
1135
1136 fn inner_mut(&mut self) -> &mut R {
1137 match &mut self.inner {
1138 Some(inner) => inner,
1139 None => unreachable!("stream encoder reader inner reader was already taken"),
1140 }
1141 }
1142
1143 fn take_inner(&mut self) -> R {
1144 match self.inner.take() {
1145 Some(inner) => inner,
1146 None => unreachable!("stream encoder reader inner reader was already taken"),
1147 }
1148 }
1149
1150 fn clear_pending(&mut self) {
1151 self.pending.fill(0);
1152 self.pending_len = 0;
1153 }
1154 }
1155
1156 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1157 where
1158 A: Alphabet,
1159 {
1160 fn drop(&mut self) {
1161 self.clear_pending();
1162 self.output.clear_all();
1163 }
1164 }
1165
1166 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1167 where
1168 R: Read,
1169 A: Alphabet,
1170 {
1171 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1172 if output.is_empty() {
1173 return Ok(0);
1174 }
1175
1176 while self.output.is_empty() && !self.finished {
1177 self.fill_output()?;
1178 }
1179
1180 let mut written = 0;
1181 while written < output.len() {
1182 let Some(byte) = self.output.pop_front() else {
1183 break;
1184 };
1185 output[written] = byte;
1186 written += 1;
1187 }
1188
1189 Ok(written)
1190 }
1191 }
1192
1193 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1194 where
1195 R: Read,
1196 A: Alphabet,
1197 {
1198 fn fill_output(&mut self) -> io::Result<()> {
1199 let mut input = [0u8; 768];
1200 let read = self.inner_mut().read(&mut input)?;
1201 if read == 0 {
1202 self.finished = true;
1203 self.push_final_pending()?;
1204 return Ok(());
1205 }
1206
1207 let mut consumed = 0;
1208 if self.pending_len > 0 {
1209 let needed = 3 - self.pending_len;
1210 if read < needed {
1211 self.pending[self.pending_len..self.pending_len + read]
1212 .copy_from_slice(&input[..read]);
1213 self.pending_len += read;
1214 return Ok(());
1215 }
1216
1217 let mut chunk = [0u8; 3];
1218 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1219 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1220 self.push_encoded(&chunk)?;
1221 self.clear_pending();
1222 consumed += needed;
1223 }
1224
1225 let remaining = &input[consumed..read];
1226 let full_len = remaining.len() / 3 * 3;
1227 if full_len > 0 {
1228 self.push_encoded(&remaining[..full_len])?;
1229 }
1230
1231 let tail = &remaining[full_len..];
1232 self.pending[..tail.len()].copy_from_slice(tail);
1233 self.pending_len = tail.len();
1234 Ok(())
1235 }
1236
1237 fn push_final_pending(&mut self) -> io::Result<()> {
1238 if self.pending_len == 0 {
1239 return Ok(());
1240 }
1241
1242 let mut pending = [0u8; 2];
1243 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1244 let pending_len = self.pending_len;
1245 self.clear_pending();
1246 self.push_encoded(&pending[..pending_len])
1247 }
1248
1249 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1250 let mut encoded = [0u8; 1024];
1251 let written = self
1252 .engine
1253 .encode_slice(input, &mut encoded)
1254 .map_err(encode_error_to_io)?;
1255 self.output.push_slice(&encoded[..written])?;
1256 Ok(())
1257 }
1258 }
1259}
1260
1261pub mod ct {
1269 use super::{Alphabet, DecodeError, Standard, UrlSafe, ct_decode_in_place, ct_decode_slice};
1270 use core::marker::PhantomData;
1271
1272 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
1274
1275 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
1277
1278 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
1280
1281 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
1283
1284 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1286 pub struct CtEngine<A, const PAD: bool> {
1287 alphabet: PhantomData<A>,
1288 }
1289
1290 impl<A, const PAD: bool> CtEngine<A, PAD>
1291 where
1292 A: Alphabet,
1293 {
1294 #[must_use]
1296 pub const fn new() -> Self {
1297 Self {
1298 alphabet: PhantomData,
1299 }
1300 }
1301
1302 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1325 ct_decode_slice::<A, PAD>(input, output)
1326 }
1327
1328 pub fn decode_slice_clear_tail(
1350 &self,
1351 input: &[u8],
1352 output: &mut [u8],
1353 ) -> Result<usize, DecodeError> {
1354 let written = match self.decode_slice(input, output) {
1355 Ok(written) => written,
1356 Err(err) => {
1357 output.fill(0);
1358 return Err(err);
1359 }
1360 };
1361 output[written..].fill(0);
1362 Ok(written)
1363 }
1364
1365 pub fn decode_in_place<'a>(
1382 &self,
1383 buffer: &'a mut [u8],
1384 ) -> Result<&'a mut [u8], DecodeError> {
1385 let len = ct_decode_in_place::<A, PAD>(buffer)?;
1386 Ok(&mut buffer[..len])
1387 }
1388
1389 pub fn decode_in_place_clear_tail<'a>(
1406 &self,
1407 buffer: &'a mut [u8],
1408 ) -> Result<&'a mut [u8], DecodeError> {
1409 let len = match ct_decode_in_place::<A, PAD>(buffer) {
1410 Ok(len) => len,
1411 Err(err) => {
1412 buffer.fill(0);
1413 return Err(err);
1414 }
1415 };
1416 buffer[len..].fill(0);
1417 Ok(&mut buffer[..len])
1418 }
1419 }
1420}
1421
1422pub const STANDARD: Engine<Standard, true> = Engine::new();
1424
1425pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
1427
1428pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
1430
1431pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
1433
1434pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
1449 match checked_encoded_len(input_len, padded) {
1450 Some(len) => Ok(len),
1451 None => Err(EncodeError::LengthOverflow),
1452 }
1453}
1454
1455#[must_use]
1466pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
1467 let groups = input_len / 3;
1468 if groups > usize::MAX / 4 {
1469 return None;
1470 }
1471 let full = groups * 4;
1472 let rem = input_len % 3;
1473 if rem == 0 {
1474 Some(full)
1475 } else if padded {
1476 full.checked_add(4)
1477 } else {
1478 full.checked_add(rem + 1)
1479 }
1480}
1481
1482#[must_use]
1493pub const fn decoded_capacity(encoded_len: usize) -> usize {
1494 let rem = encoded_len % 4;
1495 encoded_len / 4 * 3
1496 + if rem == 2 {
1497 1
1498 } else if rem == 3 {
1499 2
1500 } else {
1501 0
1502 }
1503}
1504
1505pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
1519 if padded {
1520 decoded_len_padded(input)
1521 } else {
1522 decoded_len_unpadded(input)
1523 }
1524}
1525
1526pub trait Alphabet {
1528 const ENCODE: [u8; 64];
1530
1531 fn decode(byte: u8) -> Option<u8>;
1533}
1534
1535#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1537pub struct Standard;
1538
1539impl Alphabet for Standard {
1540 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1541
1542 #[inline]
1543 fn decode(byte: u8) -> Option<u8> {
1544 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
1545 }
1546}
1547
1548#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1550pub struct UrlSafe;
1551
1552impl Alphabet for UrlSafe {
1553 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1554
1555 #[inline]
1556 fn decode(byte: u8) -> Option<u8> {
1557 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
1558 }
1559}
1560
1561#[inline]
1562const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
1563 encode_ascii_base64(value, A::ENCODE[62], A::ENCODE[63])
1564}
1565
1566#[inline]
1567const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
1568 let upper = ct_mask_lt_u8(value, 26);
1569 let lower = ct_mask_lt_u8(value.wrapping_sub(26), 26);
1570 let digit = ct_mask_lt_u8(value.wrapping_sub(52), 10);
1571 let value_62 = ct_mask_eq_u8(value, 0x3e);
1572 let value_63 = ct_mask_eq_u8(value, 0x3f);
1573
1574 (value.wrapping_add(b'A') & upper)
1575 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
1576 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
1577 | (value_62_byte & value_62)
1578 | (value_63_byte & value_63)
1579}
1580
1581#[inline]
1582fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
1583 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
1584 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
1585 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
1586 let value_62 = ct_mask_eq_u8(byte, value_62_byte);
1587 let value_63 = ct_mask_eq_u8(byte, value_63_byte);
1588 let valid = upper | lower | digit | value_62 | value_63;
1589
1590 let decoded = (byte.wrapping_sub(b'A') & upper)
1591 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
1592 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
1593 | (0x3e & value_62)
1594 | (0x3f & value_63);
1595
1596 if valid == 0 { None } else { Some(decoded) }
1597}
1598
1599#[inline]
1600const fn ct_mask_bit(bit: u8) -> u8 {
1601 0u8.wrapping_sub(bit & 1)
1602}
1603
1604#[inline]
1605const fn ct_mask_nonzero_u8(value: u8) -> u8 {
1606 let wide = value as u16;
1607 let negative = 0u16.wrapping_sub(wide);
1608 let nonzero = ((wide | negative) >> 8) as u8;
1609 ct_mask_bit(nonzero)
1610}
1611
1612#[inline]
1613const fn ct_mask_eq_u8(left: u8, right: u8) -> u8 {
1614 !ct_mask_nonzero_u8(left ^ right)
1615}
1616
1617#[inline]
1618const fn ct_mask_lt_u8(left: u8, right: u8) -> u8 {
1619 let diff = (left as u16).wrapping_sub(right as u16);
1620 ct_mask_bit((diff >> 8) as u8)
1621}
1622
1623mod backend {
1624 use super::{
1625 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
1626 encode_base64_value,
1627 };
1628
1629 pub(super) fn encode_slice<A, const PAD: bool>(
1630 input: &[u8],
1631 output: &mut [u8],
1632 ) -> Result<usize, EncodeError>
1633 where
1634 A: Alphabet,
1635 {
1636 #[cfg(feature = "simd")]
1637 match super::simd::active_backend() {
1638 super::simd::ActiveBackend::Scalar => {}
1639 }
1640
1641 scalar_encode_slice::<A, PAD>(input, output)
1642 }
1643
1644 pub(super) fn decode_slice<A, const PAD: bool>(
1645 input: &[u8],
1646 output: &mut [u8],
1647 ) -> Result<usize, DecodeError>
1648 where
1649 A: Alphabet,
1650 {
1651 #[cfg(feature = "simd")]
1652 match super::simd::active_backend() {
1653 super::simd::ActiveBackend::Scalar => {}
1654 }
1655
1656 scalar_decode_slice::<A, PAD>(input, output)
1657 }
1658
1659 #[cfg(test)]
1660 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
1661 input: &[u8],
1662 output: &mut [u8],
1663 ) -> Result<usize, EncodeError>
1664 where
1665 A: Alphabet,
1666 {
1667 scalar_encode_slice::<A, PAD>(input, output)
1668 }
1669
1670 #[cfg(test)]
1671 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
1672 input: &[u8],
1673 output: &mut [u8],
1674 ) -> Result<usize, DecodeError>
1675 where
1676 A: Alphabet,
1677 {
1678 scalar_decode_slice::<A, PAD>(input, output)
1679 }
1680
1681 fn scalar_encode_slice<A, const PAD: bool>(
1682 input: &[u8],
1683 output: &mut [u8],
1684 ) -> Result<usize, EncodeError>
1685 where
1686 A: Alphabet,
1687 {
1688 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1689 if output.len() < required {
1690 return Err(EncodeError::OutputTooSmall {
1691 required,
1692 available: output.len(),
1693 });
1694 }
1695
1696 let mut read = 0;
1697 let mut write = 0;
1698 while read + 3 <= input.len() {
1699 let b0 = input[read];
1700 let b1 = input[read + 1];
1701 let b2 = input[read + 2];
1702
1703 output[write] = encode_base64_value::<A>(b0 >> 2);
1704 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1705 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1706 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1707
1708 read += 3;
1709 write += 4;
1710 }
1711
1712 match input.len() - read {
1713 0 => {}
1714 1 => {
1715 let b0 = input[read];
1716 output[write] = encode_base64_value::<A>(b0 >> 2);
1717 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1718 write += 2;
1719 if PAD {
1720 output[write] = b'=';
1721 output[write + 1] = b'=';
1722 write += 2;
1723 }
1724 }
1725 2 => {
1726 let b0 = input[read];
1727 let b1 = input[read + 1];
1728 output[write] = encode_base64_value::<A>(b0 >> 2);
1729 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1730 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1731 write += 3;
1732 if PAD {
1733 output[write] = b'=';
1734 write += 1;
1735 }
1736 }
1737 _ => unreachable!(),
1738 }
1739
1740 Ok(write)
1741 }
1742
1743 fn scalar_decode_slice<A, const PAD: bool>(
1744 input: &[u8],
1745 output: &mut [u8],
1746 ) -> Result<usize, DecodeError>
1747 where
1748 A: Alphabet,
1749 {
1750 if input.is_empty() {
1751 return Ok(0);
1752 }
1753
1754 if PAD {
1755 decode_padded::<A>(input, output)
1756 } else {
1757 decode_unpadded::<A>(input, output)
1758 }
1759 }
1760}
1761
1762#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1764pub struct Engine<A, const PAD: bool> {
1765 alphabet: core::marker::PhantomData<A>,
1766}
1767
1768impl<A, const PAD: bool> Engine<A, PAD>
1769where
1770 A: Alphabet,
1771{
1772 #[must_use]
1774 pub const fn new() -> Self {
1775 Self {
1776 alphabet: core::marker::PhantomData,
1777 }
1778 }
1779
1780 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
1782 encoded_len(input_len, PAD)
1783 }
1784
1785 #[must_use]
1787 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
1788 checked_encoded_len(input_len, PAD)
1789 }
1790
1791 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
1796 decoded_len(input, PAD)
1797 }
1798
1799 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
1805 validate_legacy_decode::<A, PAD>(input)
1806 }
1807
1808 #[must_use]
1840 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
1841 &self,
1842 input: &[u8; INPUT_LEN],
1843 ) -> [u8; OUTPUT_LEN] {
1844 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
1845 panic!("encoded base64 length overflows usize");
1846 };
1847 assert!(
1848 required == OUTPUT_LEN,
1849 "base64 output array has incorrect length"
1850 );
1851
1852 let mut output = [0u8; OUTPUT_LEN];
1853 let mut read = 0;
1854 let mut write = 0;
1855 while INPUT_LEN - read >= 3 {
1856 let b0 = input[read];
1857 let b1 = input[read + 1];
1858 let b2 = input[read + 2];
1859
1860 output[write] = encode_base64_value::<A>(b0 >> 2);
1861 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1862 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1863 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1864
1865 read += 3;
1866 write += 4;
1867 }
1868
1869 match INPUT_LEN - read {
1870 0 => {}
1871 1 => {
1872 let b0 = input[read];
1873 output[write] = encode_base64_value::<A>(b0 >> 2);
1874 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1875 write += 2;
1876 if PAD {
1877 output[write] = b'=';
1878 output[write + 1] = b'=';
1879 }
1880 }
1881 2 => {
1882 let b0 = input[read];
1883 let b1 = input[read + 1];
1884 output[write] = encode_base64_value::<A>(b0 >> 2);
1885 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1886 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1887 if PAD {
1888 output[write + 3] = b'=';
1889 }
1890 }
1891 _ => unreachable!(),
1892 }
1893
1894 output
1895 }
1896
1897 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
1899 backend::encode_slice::<A, PAD>(input, output)
1900 }
1901
1902 pub fn encode_slice_clear_tail(
1922 &self,
1923 input: &[u8],
1924 output: &mut [u8],
1925 ) -> Result<usize, EncodeError> {
1926 let written = match self.encode_slice(input, output) {
1927 Ok(written) => written,
1928 Err(err) => {
1929 output.fill(0);
1930 return Err(err);
1931 }
1932 };
1933 output[written..].fill(0);
1934 Ok(written)
1935 }
1936
1937 #[cfg(feature = "alloc")]
1939 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
1940 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1941 let mut output = alloc::vec![0; required];
1942 let written = self.encode_slice(input, &mut output)?;
1943 output.truncate(written);
1944 Ok(output)
1945 }
1946
1947 #[cfg(feature = "alloc")]
1962 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
1963 let output = self.encode_vec(input)?;
1964 match alloc::string::String::from_utf8(output) {
1965 Ok(output) => Ok(output),
1966 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1967 }
1968 }
1969
1970 pub fn encode_in_place<'a>(
1987 &self,
1988 buffer: &'a mut [u8],
1989 input_len: usize,
1990 ) -> Result<&'a mut [u8], EncodeError> {
1991 if input_len > buffer.len() {
1992 return Err(EncodeError::InputTooLarge {
1993 input_len,
1994 buffer_len: buffer.len(),
1995 });
1996 }
1997
1998 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
1999 if buffer.len() < required {
2000 return Err(EncodeError::OutputTooSmall {
2001 required,
2002 available: buffer.len(),
2003 });
2004 }
2005
2006 let mut read = input_len;
2007 let mut write = required;
2008
2009 match input_len % 3 {
2010 0 => {}
2011 1 => {
2012 read -= 1;
2013 let b0 = buffer[read];
2014 if PAD {
2015 write -= 4;
2016 buffer[write] = encode_base64_value::<A>(b0 >> 2);
2017 buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
2018 buffer[write + 2] = b'=';
2019 buffer[write + 3] = b'=';
2020 } else {
2021 write -= 2;
2022 buffer[write] = encode_base64_value::<A>(b0 >> 2);
2023 buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
2024 }
2025 }
2026 2 => {
2027 read -= 2;
2028 let b0 = buffer[read];
2029 let b1 = buffer[read + 1];
2030 if PAD {
2031 write -= 4;
2032 buffer[write] = encode_base64_value::<A>(b0 >> 2);
2033 buffer[write + 1] =
2034 encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2035 buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
2036 buffer[write + 3] = b'=';
2037 } else {
2038 write -= 3;
2039 buffer[write] = encode_base64_value::<A>(b0 >> 2);
2040 buffer[write + 1] =
2041 encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2042 buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
2043 }
2044 }
2045 _ => unreachable!(),
2046 }
2047
2048 while read > 0 {
2049 read -= 3;
2050 write -= 4;
2051 let b0 = buffer[read];
2052 let b1 = buffer[read + 1];
2053 let b2 = buffer[read + 2];
2054
2055 buffer[write] = encode_base64_value::<A>(b0 >> 2);
2056 buffer[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2057 buffer[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
2058 buffer[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
2059 }
2060
2061 debug_assert_eq!(write, 0);
2062 Ok(&mut buffer[..required])
2063 }
2064
2065 pub fn encode_in_place_clear_tail<'a>(
2083 &self,
2084 buffer: &'a mut [u8],
2085 input_len: usize,
2086 ) -> Result<&'a mut [u8], EncodeError> {
2087 let len = match self.encode_in_place(buffer, input_len) {
2088 Ok(encoded) => encoded.len(),
2089 Err(err) => {
2090 buffer.fill(0);
2091 return Err(err);
2092 }
2093 };
2094 buffer[len..].fill(0);
2095 Ok(&mut buffer[..len])
2096 }
2097
2098 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2103 backend::decode_slice::<A, PAD>(input, output)
2104 }
2105
2106 pub fn decode_slice_clear_tail(
2126 &self,
2127 input: &[u8],
2128 output: &mut [u8],
2129 ) -> Result<usize, DecodeError> {
2130 let written = match self.decode_slice(input, output) {
2131 Ok(written) => written,
2132 Err(err) => {
2133 output.fill(0);
2134 return Err(err);
2135 }
2136 };
2137 output[written..].fill(0);
2138 Ok(written)
2139 }
2140
2141 pub fn decode_slice_legacy(
2147 &self,
2148 input: &[u8],
2149 output: &mut [u8],
2150 ) -> Result<usize, DecodeError> {
2151 let required = validate_legacy_decode::<A, PAD>(input)?;
2152 if output.len() < required {
2153 return Err(DecodeError::OutputTooSmall {
2154 required,
2155 available: output.len(),
2156 });
2157 }
2158 decode_legacy_to_slice::<A, PAD>(input, output)
2159 }
2160
2161 pub fn decode_slice_legacy_clear_tail(
2181 &self,
2182 input: &[u8],
2183 output: &mut [u8],
2184 ) -> Result<usize, DecodeError> {
2185 let written = match self.decode_slice_legacy(input, output) {
2186 Ok(written) => written,
2187 Err(err) => {
2188 output.fill(0);
2189 return Err(err);
2190 }
2191 };
2192 output[written..].fill(0);
2193 Ok(written)
2194 }
2195
2196 #[cfg(feature = "alloc")]
2200 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
2201 let required = validate_decode::<A, PAD>(input)?;
2202 let mut output = alloc::vec![0; required];
2203 let written = match self.decode_slice(input, &mut output) {
2204 Ok(written) => written,
2205 Err(err) => {
2206 output.fill(0);
2207 return Err(err);
2208 }
2209 };
2210 output.truncate(written);
2211 Ok(output)
2212 }
2213
2214 #[cfg(feature = "alloc")]
2217 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
2218 let required = validate_legacy_decode::<A, PAD>(input)?;
2219 let mut output = alloc::vec![0; required];
2220 let written = match self.decode_slice_legacy(input, &mut output) {
2221 Ok(written) => written,
2222 Err(err) => {
2223 output.fill(0);
2224 return Err(err);
2225 }
2226 };
2227 output.truncate(written);
2228 Ok(output)
2229 }
2230
2231 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
2243 let len = Self::decode_slice_to_start(buffer)?;
2244 Ok(&mut buffer[..len])
2245 }
2246
2247 pub fn decode_in_place_clear_tail<'a>(
2264 &self,
2265 buffer: &'a mut [u8],
2266 ) -> Result<&'a mut [u8], DecodeError> {
2267 let len = match Self::decode_slice_to_start(buffer) {
2268 Ok(len) => len,
2269 Err(err) => {
2270 buffer.fill(0);
2271 return Err(err);
2272 }
2273 };
2274 buffer[len..].fill(0);
2275 Ok(&mut buffer[..len])
2276 }
2277
2278 pub fn decode_in_place_legacy<'a>(
2283 &self,
2284 buffer: &'a mut [u8],
2285 ) -> Result<&'a mut [u8], DecodeError> {
2286 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
2287 let mut write = 0;
2288 let mut read = 0;
2289 while read < buffer.len() {
2290 let byte = buffer[read];
2291 if !is_legacy_whitespace(byte) {
2292 buffer[write] = byte;
2293 write += 1;
2294 }
2295 read += 1;
2296 }
2297 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
2298 Ok(&mut buffer[..len])
2299 }
2300
2301 pub fn decode_in_place_legacy_clear_tail<'a>(
2307 &self,
2308 buffer: &'a mut [u8],
2309 ) -> Result<&'a mut [u8], DecodeError> {
2310 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
2311 buffer.fill(0);
2312 return Err(err);
2313 }
2314
2315 let mut write = 0;
2316 let mut read = 0;
2317 while read < buffer.len() {
2318 let byte = buffer[read];
2319 if !is_legacy_whitespace(byte) {
2320 buffer[write] = byte;
2321 write += 1;
2322 }
2323 read += 1;
2324 }
2325
2326 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
2327 Ok(len) => len,
2328 Err(err) => {
2329 buffer.fill(0);
2330 return Err(err);
2331 }
2332 };
2333 buffer[len..].fill(0);
2334 Ok(&mut buffer[..len])
2335 }
2336
2337 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
2338 let input_len = buffer.len();
2339 let mut read = 0;
2340 let mut write = 0;
2341 while read + 4 <= input_len {
2342 let chunk = [
2343 buffer[read],
2344 buffer[read + 1],
2345 buffer[read + 2],
2346 buffer[read + 3],
2347 ];
2348 let written = decode_chunk::<A, PAD>(&chunk, &mut buffer[write..])
2349 .map_err(|err| err.with_index_offset(read))?;
2350 read += 4;
2351 write += written;
2352 if written < 3 {
2353 if read != input_len {
2354 return Err(DecodeError::InvalidPadding { index: read - 4 });
2355 }
2356 return Ok(write);
2357 }
2358 }
2359
2360 let rem = input_len - read;
2361 if rem == 0 {
2362 return Ok(write);
2363 }
2364 if PAD {
2365 return Err(DecodeError::InvalidLength);
2366 }
2367 let mut tail = [0u8; 3];
2368 tail[..rem].copy_from_slice(&buffer[read..input_len]);
2369 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
2370 .map_err(|err| err.with_index_offset(read))
2371 .map(|n| write + n)
2372 }
2373}
2374
2375#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2377pub enum EncodeError {
2378 LengthOverflow,
2380 InputTooLarge {
2382 input_len: usize,
2384 buffer_len: usize,
2386 },
2387 OutputTooSmall {
2389 required: usize,
2391 available: usize,
2393 },
2394}
2395
2396impl core::fmt::Display for EncodeError {
2397 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2398 match self {
2399 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
2400 Self::InputTooLarge {
2401 input_len,
2402 buffer_len,
2403 } => write!(
2404 f,
2405 "base64 input length {input_len} exceeds buffer length {buffer_len}"
2406 ),
2407 Self::OutputTooSmall {
2408 required,
2409 available,
2410 } => write!(
2411 f,
2412 "base64 output buffer too small: required {required}, available {available}"
2413 ),
2414 }
2415 }
2416}
2417
2418#[cfg(feature = "std")]
2419impl std::error::Error for EncodeError {}
2420
2421#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2423pub enum DecodeError {
2424 InvalidInput,
2427 InvalidLength,
2429 InvalidByte {
2431 index: usize,
2433 byte: u8,
2435 },
2436 InvalidPadding {
2438 index: usize,
2440 },
2441 OutputTooSmall {
2443 required: usize,
2445 available: usize,
2447 },
2448}
2449
2450impl core::fmt::Display for DecodeError {
2451 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2452 match self {
2453 Self::InvalidInput => f.write_str("malformed base64 input"),
2454 Self::InvalidLength => f.write_str("invalid base64 input length"),
2455 Self::InvalidByte { index, byte } => {
2456 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
2457 }
2458 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
2459 Self::OutputTooSmall {
2460 required,
2461 available,
2462 } => write!(
2463 f,
2464 "base64 decode output buffer too small: required {required}, available {available}"
2465 ),
2466 }
2467 }
2468}
2469
2470impl DecodeError {
2471 fn with_index_offset(self, offset: usize) -> Self {
2472 match self {
2473 Self::InvalidByte { index, byte } => Self::InvalidByte {
2474 index: index + offset,
2475 byte,
2476 },
2477 Self::InvalidPadding { index } => Self::InvalidPadding {
2478 index: index + offset,
2479 },
2480 Self::InvalidInput | Self::InvalidLength | Self::OutputTooSmall { .. } => self,
2481 }
2482 }
2483}
2484
2485#[cfg(feature = "std")]
2486impl std::error::Error for DecodeError {}
2487
2488fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
2489 input: &[u8],
2490) -> Result<usize, DecodeError> {
2491 let mut chunk = [0u8; 4];
2492 let mut indexes = [0usize; 4];
2493 let mut chunk_len = 0;
2494 let mut required = 0;
2495 let mut terminal_seen = false;
2496
2497 for (index, byte) in input.iter().copied().enumerate() {
2498 if is_legacy_whitespace(byte) {
2499 continue;
2500 }
2501 if terminal_seen {
2502 return Err(DecodeError::InvalidPadding { index });
2503 }
2504
2505 chunk[chunk_len] = byte;
2506 indexes[chunk_len] = index;
2507 chunk_len += 1;
2508
2509 if chunk_len == 4 {
2510 let written =
2511 validate_chunk::<A, PAD>(&chunk).map_err(|err| map_chunk_error(err, &indexes))?;
2512 required += written;
2513 terminal_seen = written < 3;
2514 chunk_len = 0;
2515 }
2516 }
2517
2518 if chunk_len == 0 {
2519 return Ok(required);
2520 }
2521 if PAD {
2522 return Err(DecodeError::InvalidLength);
2523 }
2524
2525 validate_tail_unpadded::<A>(&chunk[..chunk_len])
2526 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
2527 Ok(required + decoded_capacity(chunk_len))
2528}
2529
2530fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
2531 input: &[u8],
2532 output: &mut [u8],
2533) -> Result<usize, DecodeError> {
2534 let mut chunk = [0u8; 4];
2535 let mut indexes = [0usize; 4];
2536 let mut chunk_len = 0;
2537 let mut write = 0;
2538 let mut terminal_seen = false;
2539
2540 for (index, byte) in input.iter().copied().enumerate() {
2541 if is_legacy_whitespace(byte) {
2542 continue;
2543 }
2544 if terminal_seen {
2545 return Err(DecodeError::InvalidPadding { index });
2546 }
2547
2548 chunk[chunk_len] = byte;
2549 indexes[chunk_len] = index;
2550 chunk_len += 1;
2551
2552 if chunk_len == 4 {
2553 let written = decode_chunk::<A, PAD>(&chunk, &mut output[write..])
2554 .map_err(|err| map_chunk_error(err, &indexes))?;
2555 write += written;
2556 terminal_seen = written < 3;
2557 chunk_len = 0;
2558 }
2559 }
2560
2561 if chunk_len == 0 {
2562 return Ok(write);
2563 }
2564 if PAD {
2565 return Err(DecodeError::InvalidLength);
2566 }
2567
2568 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
2569 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
2570 .map(|n| write + n)
2571}
2572
2573#[inline]
2574const fn is_legacy_whitespace(byte: u8) -> bool {
2575 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
2576}
2577
2578fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
2579 match err {
2580 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
2581 index: indexes[index],
2582 byte,
2583 },
2584 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
2585 index: indexes[index],
2586 },
2587 DecodeError::InvalidInput
2588 | DecodeError::InvalidLength
2589 | DecodeError::OutputTooSmall { .. } => err,
2590 }
2591}
2592
2593fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
2594 match err {
2595 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
2596 index: indexes[index],
2597 byte,
2598 },
2599 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
2600 index: indexes[index],
2601 },
2602 DecodeError::InvalidByte { .. }
2603 | DecodeError::InvalidPadding { .. }
2604 | DecodeError::InvalidInput
2605 | DecodeError::InvalidLength
2606 | DecodeError::OutputTooSmall { .. } => err,
2607 }
2608}
2609
2610fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2611 if !input.len().is_multiple_of(4) {
2612 return Err(DecodeError::InvalidLength);
2613 }
2614 let required = decoded_len_padded(input)?;
2615 if output.len() < required {
2616 return Err(DecodeError::OutputTooSmall {
2617 required,
2618 available: output.len(),
2619 });
2620 }
2621
2622 let mut read = 0;
2623 let mut write = 0;
2624 while read < input.len() {
2625 let written = decode_chunk::<A, true>(&input[read..read + 4], &mut output[write..])
2626 .map_err(|err| err.with_index_offset(read))?;
2627 read += 4;
2628 write += written;
2629 if written < 3 && read != input.len() {
2630 return Err(DecodeError::InvalidPadding { index: read - 4 });
2631 }
2632 }
2633 Ok(write)
2634}
2635
2636#[cfg(feature = "alloc")]
2637fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
2638 if input.is_empty() {
2639 return Ok(0);
2640 }
2641
2642 if PAD {
2643 validate_padded::<A>(input)
2644 } else {
2645 validate_unpadded::<A>(input)
2646 }
2647}
2648
2649#[cfg(feature = "alloc")]
2650fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
2651 if !input.len().is_multiple_of(4) {
2652 return Err(DecodeError::InvalidLength);
2653 }
2654 let required = decoded_len_padded(input)?;
2655
2656 let mut read = 0;
2657 while read < input.len() {
2658 let written = validate_chunk::<A, true>(&input[read..read + 4])
2659 .map_err(|err| err.with_index_offset(read))?;
2660 read += 4;
2661 if written < 3 && read != input.len() {
2662 return Err(DecodeError::InvalidPadding { index: read - 4 });
2663 }
2664 }
2665
2666 Ok(required)
2667}
2668
2669#[cfg(feature = "alloc")]
2670fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
2671 let required = decoded_len_unpadded(input)?;
2672
2673 let mut read = 0;
2674 while read + 4 <= input.len() {
2675 validate_chunk::<A, false>(&input[read..read + 4])
2676 .map_err(|err| err.with_index_offset(read))?;
2677 read += 4;
2678 }
2679 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
2680
2681 Ok(required)
2682}
2683
2684fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2685 let required = decoded_len_unpadded(input)?;
2686 if output.len() < required {
2687 return Err(DecodeError::OutputTooSmall {
2688 required,
2689 available: output.len(),
2690 });
2691 }
2692
2693 let mut read = 0;
2694 let mut write = 0;
2695 while read + 4 <= input.len() {
2696 let written = decode_chunk::<A, false>(&input[read..read + 4], &mut output[write..])
2697 .map_err(|err| err.with_index_offset(read))?;
2698 read += 4;
2699 write += written;
2700 }
2701 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
2702 .map_err(|err| err.with_index_offset(read))
2703 .map(|n| write + n)
2704}
2705
2706fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
2707 if input.is_empty() {
2708 return Ok(0);
2709 }
2710 if !input.len().is_multiple_of(4) {
2711 return Err(DecodeError::InvalidLength);
2712 }
2713 let mut padding = 0;
2714 if input[input.len() - 1] == b'=' {
2715 padding += 1;
2716 }
2717 if input[input.len() - 2] == b'=' {
2718 padding += 1;
2719 }
2720 if padding == 0
2721 && let Some(index) = input.iter().position(|byte| *byte == b'=')
2722 {
2723 return Err(DecodeError::InvalidPadding { index });
2724 }
2725 if padding > 0 {
2726 let first_pad = input.len() - padding;
2727 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
2728 return Err(DecodeError::InvalidPadding { index });
2729 }
2730 }
2731 Ok(input.len() / 4 * 3 - padding)
2732}
2733
2734fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
2735 if input.len() % 4 == 1 {
2736 return Err(DecodeError::InvalidLength);
2737 }
2738 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
2739 return Err(DecodeError::InvalidPadding { index });
2740 }
2741 Ok(decoded_capacity(input.len()))
2742}
2743
2744fn validate_chunk<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
2745 debug_assert_eq!(input.len(), 4);
2746 let _v0 = decode_byte::<A>(input[0], 0)?;
2747 let v1 = decode_byte::<A>(input[1], 1)?;
2748
2749 match (input[2], input[3]) {
2750 (b'=', b'=') if PAD => {
2751 if v1 & 0b0000_1111 != 0 {
2752 return Err(DecodeError::InvalidPadding { index: 1 });
2753 }
2754 Ok(1)
2755 }
2756 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
2757 (_, b'=') if PAD => {
2758 let v2 = decode_byte::<A>(input[2], 2)?;
2759 if v2 & 0b0000_0011 != 0 {
2760 return Err(DecodeError::InvalidPadding { index: 2 });
2761 }
2762 Ok(2)
2763 }
2764 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
2765 index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
2766 }),
2767 _ => {
2768 decode_byte::<A>(input[2], 2)?;
2769 decode_byte::<A>(input[3], 3)?;
2770 Ok(3)
2771 }
2772 }
2773}
2774
2775fn decode_chunk<A: Alphabet, const PAD: bool>(
2776 input: &[u8],
2777 output: &mut [u8],
2778) -> Result<usize, DecodeError> {
2779 debug_assert_eq!(input.len(), 4);
2780 let v0 = decode_byte::<A>(input[0], 0)?;
2781 let v1 = decode_byte::<A>(input[1], 1)?;
2782
2783 match (input[2], input[3]) {
2784 (b'=', b'=') if PAD => {
2785 if output.is_empty() {
2786 return Err(DecodeError::OutputTooSmall {
2787 required: 1,
2788 available: output.len(),
2789 });
2790 }
2791 if v1 & 0b0000_1111 != 0 {
2792 return Err(DecodeError::InvalidPadding { index: 1 });
2793 }
2794 output[0] = (v0 << 2) | (v1 >> 4);
2795 Ok(1)
2796 }
2797 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
2798 (_, b'=') if PAD => {
2799 if output.len() < 2 {
2800 return Err(DecodeError::OutputTooSmall {
2801 required: 2,
2802 available: output.len(),
2803 });
2804 }
2805 let v2 = decode_byte::<A>(input[2], 2)?;
2806 if v2 & 0b0000_0011 != 0 {
2807 return Err(DecodeError::InvalidPadding { index: 2 });
2808 }
2809 output[0] = (v0 << 2) | (v1 >> 4);
2810 output[1] = (v1 << 4) | (v2 >> 2);
2811 Ok(2)
2812 }
2813 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
2814 index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
2815 }),
2816 _ => {
2817 if output.len() < 3 {
2818 return Err(DecodeError::OutputTooSmall {
2819 required: 3,
2820 available: output.len(),
2821 });
2822 }
2823 let v2 = decode_byte::<A>(input[2], 2)?;
2824 let v3 = decode_byte::<A>(input[3], 3)?;
2825 output[0] = (v0 << 2) | (v1 >> 4);
2826 output[1] = (v1 << 4) | (v2 >> 2);
2827 output[2] = (v2 << 6) | v3;
2828 Ok(3)
2829 }
2830 }
2831}
2832
2833fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
2834 match input.len() {
2835 0 => Ok(()),
2836 2 => {
2837 decode_byte::<A>(input[0], 0)?;
2838 let v1 = decode_byte::<A>(input[1], 1)?;
2839 if v1 & 0b0000_1111 != 0 {
2840 return Err(DecodeError::InvalidPadding { index: 1 });
2841 }
2842 Ok(())
2843 }
2844 3 => {
2845 decode_byte::<A>(input[0], 0)?;
2846 decode_byte::<A>(input[1], 1)?;
2847 let v2 = decode_byte::<A>(input[2], 2)?;
2848 if v2 & 0b0000_0011 != 0 {
2849 return Err(DecodeError::InvalidPadding { index: 2 });
2850 }
2851 Ok(())
2852 }
2853 _ => Err(DecodeError::InvalidLength),
2854 }
2855}
2856
2857fn decode_tail_unpadded<A: Alphabet>(
2858 input: &[u8],
2859 output: &mut [u8],
2860) -> Result<usize, DecodeError> {
2861 match input.len() {
2862 0 => Ok(0),
2863 2 => {
2864 if output.is_empty() {
2865 return Err(DecodeError::OutputTooSmall {
2866 required: 1,
2867 available: output.len(),
2868 });
2869 }
2870 let v0 = decode_byte::<A>(input[0], 0)?;
2871 let v1 = decode_byte::<A>(input[1], 1)?;
2872 if v1 & 0b0000_1111 != 0 {
2873 return Err(DecodeError::InvalidPadding { index: 1 });
2874 }
2875 output[0] = (v0 << 2) | (v1 >> 4);
2876 Ok(1)
2877 }
2878 3 => {
2879 if output.len() < 2 {
2880 return Err(DecodeError::OutputTooSmall {
2881 required: 2,
2882 available: output.len(),
2883 });
2884 }
2885 let v0 = decode_byte::<A>(input[0], 0)?;
2886 let v1 = decode_byte::<A>(input[1], 1)?;
2887 let v2 = decode_byte::<A>(input[2], 2)?;
2888 if v2 & 0b0000_0011 != 0 {
2889 return Err(DecodeError::InvalidPadding { index: 2 });
2890 }
2891 output[0] = (v0 << 2) | (v1 >> 4);
2892 output[1] = (v1 << 4) | (v2 >> 2);
2893 Ok(2)
2894 }
2895 _ => Err(DecodeError::InvalidLength),
2896 }
2897}
2898
2899fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
2900 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
2901}
2902
2903fn ct_decode_slice<A: Alphabet, const PAD: bool>(
2904 input: &[u8],
2905 output: &mut [u8],
2906) -> Result<usize, DecodeError> {
2907 if input.is_empty() {
2908 return Ok(0);
2909 }
2910
2911 if PAD {
2912 ct_decode_padded::<A>(input, output)
2913 } else {
2914 ct_decode_unpadded::<A>(input, output)
2915 }
2916}
2917
2918fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
2919 buffer: &mut [u8],
2920) -> Result<usize, DecodeError> {
2921 if buffer.is_empty() {
2922 return Ok(0);
2923 }
2924
2925 if PAD {
2926 ct_decode_padded_in_place::<A>(buffer)
2927 } else {
2928 ct_decode_unpadded_in_place::<A>(buffer)
2929 }
2930}
2931
2932fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2933 if !input.len().is_multiple_of(4) {
2934 return Err(DecodeError::InvalidLength);
2935 }
2936
2937 let padding = ct_padding_len(input);
2938 let required = input.len() / 4 * 3 - padding;
2939 if output.len() < required {
2940 return Err(DecodeError::OutputTooSmall {
2941 required,
2942 available: output.len(),
2943 });
2944 }
2945
2946 let mut invalid_byte = 0u8;
2947 let mut invalid_padding = 0u8;
2948 let mut write = 0;
2949 let mut read = 0;
2950
2951 while read < input.len() {
2952 let is_last = read + 4 == input.len();
2953 let b0 = input[read];
2954 let b1 = input[read + 1];
2955 let b2 = input[read + 2];
2956 let b3 = input[read + 3];
2957 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2958 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2959 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2960 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
2961
2962 invalid_byte |= !valid0;
2963 invalid_byte |= !valid1;
2964
2965 if is_last && padding == 2 {
2966 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
2967 output[write] = (v0 << 2) | (v1 >> 4);
2968 write += 1;
2969 } else if is_last && padding == 1 {
2970 invalid_byte |= !valid2;
2971 invalid_padding |= ct_mask_eq_u8(b2, b'=');
2972 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
2973 output[write] = (v0 << 2) | (v1 >> 4);
2974 output[write + 1] = (v1 << 4) | (v2 >> 2);
2975 write += 2;
2976 } else {
2977 invalid_byte |= !valid2;
2978 invalid_byte |= !valid3;
2979 invalid_padding |= ct_mask_eq_u8(b2, b'=');
2980 invalid_padding |= ct_mask_eq_u8(b3, b'=');
2981 output[write] = (v0 << 2) | (v1 >> 4);
2982 output[write + 1] = (v1 << 4) | (v2 >> 2);
2983 output[write + 2] = (v2 << 6) | v3;
2984 write += 3;
2985 }
2986
2987 read += 4;
2988 }
2989
2990 report_ct_error(invalid_byte, invalid_padding)?;
2991 Ok(write)
2992}
2993
2994fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
2995 if !buffer.len().is_multiple_of(4) {
2996 return Err(DecodeError::InvalidLength);
2997 }
2998
2999 let padding = ct_padding_len(buffer);
3000 let required = buffer.len() / 4 * 3 - padding;
3001 debug_assert!(required <= buffer.len());
3002
3003 let mut invalid_byte = 0u8;
3004 let mut invalid_padding = 0u8;
3005 let mut write = 0;
3006 let mut read = 0;
3007
3008 while read < buffer.len() {
3009 let is_last = read + 4 == buffer.len();
3010 let b0 = buffer[read];
3011 let b1 = buffer[read + 1];
3012 let b2 = buffer[read + 2];
3013 let b3 = buffer[read + 3];
3014 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
3015 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
3016 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
3017 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
3018
3019 invalid_byte |= !valid0;
3020 invalid_byte |= !valid1;
3021
3022 if is_last && padding == 2 {
3023 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
3024 buffer[write] = (v0 << 2) | (v1 >> 4);
3025 write += 1;
3026 } else if is_last && padding == 1 {
3027 invalid_byte |= !valid2;
3028 invalid_padding |= ct_mask_eq_u8(b2, b'=');
3029 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
3030 buffer[write] = (v0 << 2) | (v1 >> 4);
3031 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
3032 write += 2;
3033 } else {
3034 invalid_byte |= !valid2;
3035 invalid_byte |= !valid3;
3036 invalid_padding |= ct_mask_eq_u8(b2, b'=');
3037 invalid_padding |= ct_mask_eq_u8(b3, b'=');
3038 buffer[write] = (v0 << 2) | (v1 >> 4);
3039 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
3040 buffer[write + 2] = (v2 << 6) | v3;
3041 write += 3;
3042 }
3043
3044 read += 4;
3045 }
3046
3047 debug_assert_eq!(write, required);
3048 report_ct_error(invalid_byte, invalid_padding)?;
3049 Ok(write)
3050}
3051
3052fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
3053 if input.len() % 4 == 1 {
3054 return Err(DecodeError::InvalidLength);
3055 }
3056
3057 let required = decoded_capacity(input.len());
3058 if output.len() < required {
3059 return Err(DecodeError::OutputTooSmall {
3060 required,
3061 available: output.len(),
3062 });
3063 }
3064
3065 let mut invalid_byte = 0u8;
3066 let mut invalid_padding = 0u8;
3067 let mut write = 0;
3068 let mut read = 0;
3069
3070 while read + 4 <= input.len() {
3071 let b0 = input[read];
3072 let b1 = input[read + 1];
3073 let b2 = input[read + 2];
3074 let b3 = input[read + 3];
3075 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
3076 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
3077 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
3078 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
3079
3080 invalid_byte |= !valid0;
3081 invalid_byte |= !valid1;
3082 invalid_byte |= !valid2;
3083 invalid_byte |= !valid3;
3084 invalid_padding |= ct_mask_eq_u8(b0, b'=');
3085 invalid_padding |= ct_mask_eq_u8(b1, b'=');
3086 invalid_padding |= ct_mask_eq_u8(b2, b'=');
3087 invalid_padding |= ct_mask_eq_u8(b3, b'=');
3088
3089 output[write] = (v0 << 2) | (v1 >> 4);
3090 output[write + 1] = (v1 << 4) | (v2 >> 2);
3091 output[write + 2] = (v2 << 6) | v3;
3092 read += 4;
3093 write += 3;
3094 }
3095
3096 match input.len() - read {
3097 0 => {}
3098 2 => {
3099 let b0 = input[read];
3100 let b1 = input[read + 1];
3101 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
3102 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
3103 invalid_byte |= !valid0;
3104 invalid_byte |= !valid1;
3105 invalid_padding |= ct_mask_eq_u8(b0, b'=');
3106 invalid_padding |= ct_mask_eq_u8(b1, b'=');
3107 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
3108 output[write] = (v0 << 2) | (v1 >> 4);
3109 write += 1;
3110 }
3111 3 => {
3112 let b0 = input[read];
3113 let b1 = input[read + 1];
3114 let b2 = input[read + 2];
3115 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
3116 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
3117 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
3118 invalid_byte |= !valid0;
3119 invalid_byte |= !valid1;
3120 invalid_byte |= !valid2;
3121 invalid_padding |= ct_mask_eq_u8(b0, b'=');
3122 invalid_padding |= ct_mask_eq_u8(b1, b'=');
3123 invalid_padding |= ct_mask_eq_u8(b2, b'=');
3124 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
3125 output[write] = (v0 << 2) | (v1 >> 4);
3126 output[write + 1] = (v1 << 4) | (v2 >> 2);
3127 write += 2;
3128 }
3129 _ => return Err(DecodeError::InvalidLength),
3130 }
3131
3132 report_ct_error(invalid_byte, invalid_padding)?;
3133 Ok(write)
3134}
3135
3136fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
3137 if buffer.len() % 4 == 1 {
3138 return Err(DecodeError::InvalidLength);
3139 }
3140
3141 let required = decoded_capacity(buffer.len());
3142 debug_assert!(required <= buffer.len());
3143
3144 let mut invalid_byte = 0u8;
3145 let mut invalid_padding = 0u8;
3146 let mut write = 0;
3147 let mut read = 0;
3148
3149 while read + 4 <= buffer.len() {
3150 let b0 = buffer[read];
3151 let b1 = buffer[read + 1];
3152 let b2 = buffer[read + 2];
3153 let b3 = buffer[read + 3];
3154 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
3155 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
3156 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
3157 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
3158
3159 invalid_byte |= !valid0;
3160 invalid_byte |= !valid1;
3161 invalid_byte |= !valid2;
3162 invalid_byte |= !valid3;
3163 invalid_padding |= ct_mask_eq_u8(b0, b'=');
3164 invalid_padding |= ct_mask_eq_u8(b1, b'=');
3165 invalid_padding |= ct_mask_eq_u8(b2, b'=');
3166 invalid_padding |= ct_mask_eq_u8(b3, b'=');
3167
3168 buffer[write] = (v0 << 2) | (v1 >> 4);
3169 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
3170 buffer[write + 2] = (v2 << 6) | v3;
3171 read += 4;
3172 write += 3;
3173 }
3174
3175 match buffer.len() - read {
3176 0 => {}
3177 2 => {
3178 let b0 = buffer[read];
3179 let b1 = buffer[read + 1];
3180 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
3181 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
3182 invalid_byte |= !valid0;
3183 invalid_byte |= !valid1;
3184 invalid_padding |= ct_mask_eq_u8(b0, b'=');
3185 invalid_padding |= ct_mask_eq_u8(b1, b'=');
3186 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
3187 buffer[write] = (v0 << 2) | (v1 >> 4);
3188 write += 1;
3189 }
3190 3 => {
3191 let b0 = buffer[read];
3192 let b1 = buffer[read + 1];
3193 let b2 = buffer[read + 2];
3194 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
3195 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
3196 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
3197 invalid_byte |= !valid0;
3198 invalid_byte |= !valid1;
3199 invalid_byte |= !valid2;
3200 invalid_padding |= ct_mask_eq_u8(b0, b'=');
3201 invalid_padding |= ct_mask_eq_u8(b1, b'=');
3202 invalid_padding |= ct_mask_eq_u8(b2, b'=');
3203 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
3204 buffer[write] = (v0 << 2) | (v1 >> 4);
3205 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
3206 write += 2;
3207 }
3208 _ => return Err(DecodeError::InvalidLength),
3209 }
3210
3211 debug_assert_eq!(write, required);
3212 report_ct_error(invalid_byte, invalid_padding)?;
3213 Ok(write)
3214}
3215
3216#[inline]
3217fn ct_decode_ascii_base64<A: Alphabet>(byte: u8) -> (u8, u8) {
3218 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
3219 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
3220 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
3221 let value_62 = ct_mask_eq_u8(byte, A::ENCODE[62]);
3222 let value_63 = ct_mask_eq_u8(byte, A::ENCODE[63]);
3223 let valid = upper | lower | digit | value_62 | value_63;
3224
3225 let decoded = (byte.wrapping_sub(b'A') & upper)
3226 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
3227 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
3228 | (0x3e & value_62)
3229 | (0x3f & value_63);
3230
3231 (decoded, valid)
3232}
3233
3234fn ct_padding_len(input: &[u8]) -> usize {
3235 let last = input[input.len() - 1];
3236 let before_last = input[input.len() - 2];
3237 usize::from(ct_mask_eq_u8(last, b'=') & 1) + usize::from(ct_mask_eq_u8(before_last, b'=') & 1)
3238}
3239
3240fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
3241 if (invalid_byte | invalid_padding) != 0 {
3242 Err(DecodeError::InvalidInput)
3243 } else {
3244 Ok(())
3245 }
3246}
3247
3248#[cfg(kani)]
3249mod kani_proofs {
3250 use super::{STANDARD, checked_encoded_len, decoded_capacity};
3251
3252 #[kani::proof]
3253 fn checked_encoded_len_is_bounded_for_small_inputs() {
3254 let len = usize::from(kani::any::<u8>());
3255 let padded = kani::any::<bool>();
3256 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
3257
3258 assert!(encoded >= len);
3259 assert!(encoded <= len / 3 * 4 + 4);
3260 }
3261
3262 #[kani::proof]
3263 fn decoded_capacity_is_bounded_for_small_inputs() {
3264 let len = usize::from(kani::any::<u8>());
3265 let capacity = decoded_capacity(len);
3266
3267 assert!(capacity <= len / 4 * 3 + 2);
3268 }
3269
3270 #[kani::proof]
3271 #[kani::unwind(3)]
3272 fn standard_in_place_decode_returns_prefix_within_buffer() {
3273 let mut buffer = kani::any::<[u8; 8]>();
3274 let result = STANDARD.decode_in_place(&mut buffer);
3275
3276 if let Ok(decoded) = result {
3277 assert!(decoded.len() <= 8);
3278 }
3279 }
3280
3281 #[kani::proof]
3282 #[kani::unwind(3)]
3283 fn standard_clear_tail_decode_clears_buffer_on_error() {
3284 let mut buffer = kani::any::<[u8; 4]>();
3285 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
3286
3287 if result.is_err() {
3288 assert!(buffer.iter().all(|byte| *byte == 0));
3289 }
3290 }
3291}
3292
3293#[cfg(test)]
3294mod tests {
3295 use super::*;
3296
3297 fn fill_pattern(output: &mut [u8], seed: usize) {
3298 for (index, byte) in output.iter_mut().enumerate() {
3299 let value = (index * 73 + seed * 19) % 256;
3300 *byte = u8::try_from(value).unwrap();
3301 }
3302 }
3303
3304 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
3305 where
3306 A: Alphabet,
3307 {
3308 let engine = Engine::<A, PAD>::new();
3309 let mut dispatched = [0x55; 256];
3310 let mut scalar = [0xaa; 256];
3311
3312 let dispatched_result = engine.encode_slice(input, &mut dispatched);
3313 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
3314
3315 assert_eq!(dispatched_result, scalar_result);
3316 if let Ok(written) = dispatched_result {
3317 assert_eq!(&dispatched[..written], &scalar[..written]);
3318 }
3319
3320 let required = checked_encoded_len(input.len(), PAD).unwrap();
3321 if required > 0 {
3322 let mut dispatched_short = [0x55; 256];
3323 let mut scalar_short = [0xaa; 256];
3324 let available = required - 1;
3325
3326 assert_eq!(
3327 engine.encode_slice(input, &mut dispatched_short[..available]),
3328 backend::scalar_reference_encode_slice::<A, PAD>(
3329 input,
3330 &mut scalar_short[..available],
3331 )
3332 );
3333 }
3334 }
3335
3336 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
3337 where
3338 A: Alphabet,
3339 {
3340 let engine = Engine::<A, PAD>::new();
3341 let mut dispatched = [0x55; 128];
3342 let mut scalar = [0xaa; 128];
3343
3344 let dispatched_result = engine.decode_slice(input, &mut dispatched);
3345 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
3346
3347 assert_eq!(dispatched_result, scalar_result);
3348 if let Ok(written) = dispatched_result {
3349 assert_eq!(&dispatched[..written], &scalar[..written]);
3350
3351 if written > 0 {
3352 let mut dispatched_short = [0x55; 128];
3353 let mut scalar_short = [0xaa; 128];
3354 let available = written - 1;
3355
3356 assert_eq!(
3357 engine.decode_slice(input, &mut dispatched_short[..available]),
3358 backend::scalar_reference_decode_slice::<A, PAD>(
3359 input,
3360 &mut scalar_short[..available],
3361 )
3362 );
3363 }
3364 }
3365 }
3366
3367 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
3368 where
3369 A: Alphabet,
3370 {
3371 assert_encode_backend_matches_scalar::<A, PAD>(input);
3372
3373 let mut encoded = [0; 256];
3374 let encoded_len =
3375 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
3376 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
3377 }
3378
3379 #[test]
3380 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
3381 let mut input = [0; 128];
3382
3383 for input_len in 0..=input.len() {
3384 fill_pattern(&mut input[..input_len], input_len);
3385 let input = &input[..input_len];
3386
3387 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
3388 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
3389 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
3390 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
3391 }
3392 }
3393
3394 #[test]
3395 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
3396 for input in [
3397 &b"Z"[..],
3398 b"====",
3399 b"AA=A",
3400 b"Zh==",
3401 b"Zm9=",
3402 b"Zm9v$g==",
3403 b"Zm9vZh==",
3404 ] {
3405 assert_decode_backend_matches_scalar::<Standard, true>(input);
3406 }
3407
3408 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
3409 assert_decode_backend_matches_scalar::<Standard, false>(input);
3410 }
3411
3412 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
3413 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
3414 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
3415 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
3416 }
3417
3418 #[cfg(feature = "simd")]
3419 #[test]
3420 fn simd_dispatch_scaffold_keeps_scalar_active() {
3421 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
3422 let _candidate = simd::detected_candidate();
3423 }
3424
3425 #[test]
3426 fn encodes_standard_vectors() {
3427 let vectors = [
3428 (&b""[..], &b""[..]),
3429 (&b"f"[..], &b"Zg=="[..]),
3430 (&b"fo"[..], &b"Zm8="[..]),
3431 (&b"foo"[..], &b"Zm9v"[..]),
3432 (&b"foob"[..], &b"Zm9vYg=="[..]),
3433 (&b"fooba"[..], &b"Zm9vYmE="[..]),
3434 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
3435 ];
3436 for (input, expected) in vectors {
3437 let mut output = [0u8; 16];
3438 let written = STANDARD.encode_slice(input, &mut output).unwrap();
3439 assert_eq!(&output[..written], expected);
3440 }
3441 }
3442
3443 #[test]
3444 fn decodes_standard_vectors() {
3445 let vectors = [
3446 (&b""[..], &b""[..]),
3447 (&b"Zg=="[..], &b"f"[..]),
3448 (&b"Zm8="[..], &b"fo"[..]),
3449 (&b"Zm9v"[..], &b"foo"[..]),
3450 (&b"Zm9vYg=="[..], &b"foob"[..]),
3451 (&b"Zm9vYmE="[..], &b"fooba"[..]),
3452 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
3453 ];
3454 for (input, expected) in vectors {
3455 let mut output = [0u8; 16];
3456 let written = STANDARD.decode_slice(input, &mut output).unwrap();
3457 assert_eq!(&output[..written], expected);
3458 }
3459 }
3460
3461 #[test]
3462 fn supports_unpadded_url_safe() {
3463 let mut encoded = [0u8; 16];
3464 let written = URL_SAFE_NO_PAD
3465 .encode_slice(b"\xfb\xff", &mut encoded)
3466 .unwrap();
3467 assert_eq!(&encoded[..written], b"-_8");
3468
3469 let mut decoded = [0u8; 2];
3470 let written = URL_SAFE_NO_PAD
3471 .decode_slice(&encoded[..written], &mut decoded)
3472 .unwrap();
3473 assert_eq!(&decoded[..written], b"\xfb\xff");
3474 }
3475
3476 #[test]
3477 fn decodes_in_place() {
3478 let mut buffer = *b"Zm9vYmFy";
3479 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
3480 assert_eq!(decoded, b"foobar");
3481 }
3482
3483 #[test]
3484 fn rejects_non_canonical_padding_bits() {
3485 let mut output = [0u8; 4];
3486 assert_eq!(
3487 STANDARD.decode_slice(b"Zh==", &mut output),
3488 Err(DecodeError::InvalidPadding { index: 1 })
3489 );
3490 assert_eq!(
3491 STANDARD.decode_slice(b"Zm9=", &mut output),
3492 Err(DecodeError::InvalidPadding { index: 2 })
3493 );
3494 }
3495}