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 Avx2,
65 Neon,
67 }
68
69 impl Backend {
70 #[must_use]
76 pub const fn as_str(self) -> &'static str {
77 match self {
78 Self::Scalar => "scalar",
79 Self::Avx2 => "avx2",
80 Self::Neon => "neon",
81 }
82 }
83 }
84
85 impl core::fmt::Display for Backend {
86 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 formatter.write_str(self.as_str())
88 }
89 }
90
91 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
93 #[non_exhaustive]
94 pub enum SecurityPosture {
95 ScalarOnly,
97 SimdCandidateScalarActive,
99 Accelerated,
101 }
102
103 impl SecurityPosture {
104 #[must_use]
113 pub const fn as_str(self) -> &'static str {
114 match self {
115 Self::ScalarOnly => "scalar-only",
116 Self::SimdCandidateScalarActive => "simd-candidate-scalar-active",
117 Self::Accelerated => "accelerated",
118 }
119 }
120 }
121
122 impl core::fmt::Display for SecurityPosture {
123 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124 formatter.write_str(self.as_str())
125 }
126 }
127
128 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
130 #[non_exhaustive]
131 pub enum BackendPolicy {
132 ScalarExecutionOnly,
134 SimdFeatureDisabled,
136 NoDetectedSimdCandidate,
138 HighAssuranceScalarOnly,
141 }
142
143 impl BackendPolicy {
144 #[must_use]
153 pub const fn as_str(self) -> &'static str {
154 match self {
155 Self::ScalarExecutionOnly => "scalar-execution-only",
156 Self::SimdFeatureDisabled => "simd-feature-disabled",
157 Self::NoDetectedSimdCandidate => "no-detected-simd-candidate",
158 Self::HighAssuranceScalarOnly => "high-assurance-scalar-only",
159 }
160 }
161 }
162
163 impl core::fmt::Display for BackendPolicy {
164 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165 formatter.write_str(self.as_str())
166 }
167 }
168
169 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
171 pub struct BackendPolicyError {
172 pub policy: BackendPolicy,
174 pub report: BackendReport,
176 }
177
178 impl core::fmt::Display for BackendPolicyError {
179 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
180 write!(
181 formatter,
182 "runtime backend policy `{}` was not satisfied ({})",
183 self.policy, self.report,
184 )
185 }
186 }
187
188 #[cfg(feature = "std")]
189 impl std::error::Error for BackendPolicyError {}
190
191 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
193 pub struct BackendReport {
194 pub active: Backend,
196 pub candidate: Backend,
198 pub simd_feature_enabled: bool,
200 pub accelerated_backend_active: bool,
202 pub unsafe_boundary_enforced: bool,
204 pub security_posture: SecurityPosture,
206 }
207
208 impl core::fmt::Display for BackendReport {
209 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
210 write!(
211 formatter,
212 "active={} candidate={} simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={}",
213 self.active,
214 self.candidate,
215 self.simd_feature_enabled,
216 self.accelerated_backend_active,
217 self.unsafe_boundary_enforced,
218 self.security_posture,
219 )
220 }
221 }
222
223 impl BackendReport {
224 #[must_use]
234 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
235 match policy {
236 BackendPolicy::ScalarExecutionOnly => {
237 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
238 }
239 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
240 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
241 BackendPolicy::HighAssuranceScalarOnly => {
242 matches!(self.active, Backend::Scalar)
243 && matches!(self.candidate, Backend::Scalar)
244 && !self.simd_feature_enabled
245 && !self.accelerated_backend_active
246 && self.unsafe_boundary_enforced
247 }
248 }
249 }
250 }
251
252 #[must_use]
261 pub fn backend_report() -> BackendReport {
262 let active = active_backend();
263 let candidate = detected_candidate();
264 let accelerated_backend_active = active != Backend::Scalar;
265 let security_posture = if accelerated_backend_active {
266 SecurityPosture::Accelerated
267 } else if candidate != Backend::Scalar {
268 SecurityPosture::SimdCandidateScalarActive
269 } else {
270 SecurityPosture::ScalarOnly
271 };
272
273 BackendReport {
274 active,
275 candidate,
276 simd_feature_enabled: cfg!(feature = "simd"),
277 accelerated_backend_active,
278 unsafe_boundary_enforced: true,
279 security_posture,
280 }
281 }
282
283 pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
292 let report = backend_report();
293 if report.satisfies(policy) {
294 Ok(())
295 } else {
296 Err(BackendPolicyError { policy, report })
297 }
298 }
299
300 #[cfg(feature = "simd")]
301 fn active_backend() -> Backend {
302 match super::simd::active_backend() {
303 super::simd::ActiveBackend::Scalar => Backend::Scalar,
304 }
305 }
306
307 #[cfg(not(feature = "simd"))]
308 const fn active_backend() -> Backend {
309 Backend::Scalar
310 }
311
312 #[cfg(feature = "simd")]
313 fn detected_candidate() -> Backend {
314 match super::simd::detected_candidate() {
315 super::simd::Candidate::Scalar => Backend::Scalar,
316 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
317 super::simd::Candidate::Avx2 => Backend::Avx2,
318 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
319 super::simd::Candidate::Neon => Backend::Neon,
320 }
321 }
322
323 #[cfg(not(feature = "simd"))]
324 const fn detected_candidate() -> Backend {
325 Backend::Scalar
326 }
327}
328
329#[cfg(feature = "stream")]
330pub mod stream {
331 use super::{Alphabet, DecodeError, EncodeError, Engine};
361 use std::collections::VecDeque;
362 use std::io::{self, Read, Write};
363
364 pub struct Encoder<W, A, const PAD: bool>
366 where
367 A: Alphabet,
368 {
369 inner: Option<W>,
370 engine: Engine<A, PAD>,
371 pending: [u8; 2],
372 pending_len: usize,
373 }
374
375 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
376 where
377 A: Alphabet,
378 {
379 #[must_use]
381 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
382 Self {
383 inner: Some(inner),
384 engine,
385 pending: [0; 2],
386 pending_len: 0,
387 }
388 }
389
390 #[must_use]
392 pub fn get_ref(&self) -> &W {
393 self.inner_ref()
394 }
395
396 pub fn get_mut(&mut self) -> &mut W {
398 self.inner_mut()
399 }
400
401 #[must_use]
405 pub fn into_inner(mut self) -> W {
406 self.take_inner()
407 }
408
409 fn inner_ref(&self) -> &W {
410 match &self.inner {
411 Some(inner) => inner,
412 None => unreachable!("stream encoder inner writer was already taken"),
413 }
414 }
415
416 fn inner_mut(&mut self) -> &mut W {
417 match &mut self.inner {
418 Some(inner) => inner,
419 None => unreachable!("stream encoder inner writer was already taken"),
420 }
421 }
422
423 fn take_inner(&mut self) -> W {
424 match self.inner.take() {
425 Some(inner) => inner,
426 None => unreachable!("stream encoder inner writer was already taken"),
427 }
428 }
429
430 fn clear_pending(&mut self) {
431 self.pending.fill(0);
432 self.pending_len = 0;
433 }
434 }
435
436 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
437 where
438 A: Alphabet,
439 {
440 fn drop(&mut self) {
441 self.clear_pending();
442 }
443 }
444
445 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
446 where
447 W: Write,
448 A: Alphabet,
449 {
450 pub fn finish(mut self) -> io::Result<W> {
452 self.write_pending_final()?;
453 self.inner_mut().flush()?;
454 Ok(self.take_inner())
455 }
456
457 fn write_pending_final(&mut self) -> io::Result<()> {
458 if self.pending_len == 0 {
459 return Ok(());
460 }
461
462 let mut encoded = [0u8; 4];
463 let written = self
464 .engine
465 .encode_slice(&self.pending[..self.pending_len], &mut encoded)
466 .map_err(encode_error_to_io)?;
467 self.inner_mut().write_all(&encoded[..written])?;
468 self.clear_pending();
469 Ok(())
470 }
471 }
472
473 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
474 where
475 W: Write,
476 A: Alphabet,
477 {
478 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
479 if input.is_empty() {
480 return Ok(0);
481 }
482
483 let mut consumed = 0;
484 if self.pending_len > 0 {
485 let needed = 3 - self.pending_len;
486 if input.len() < needed {
487 self.pending[self.pending_len..self.pending_len + input.len()]
488 .copy_from_slice(input);
489 self.pending_len += input.len();
490 return Ok(input.len());
491 }
492
493 let mut chunk = [0u8; 3];
494 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
495 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
496
497 let mut encoded = [0u8; 4];
498 let written = self
499 .engine
500 .encode_slice(&chunk, &mut encoded)
501 .map_err(encode_error_to_io)?;
502 self.inner_mut().write_all(&encoded[..written])?;
503 self.clear_pending();
504 consumed += needed;
505 }
506
507 let remaining = &input[consumed..];
508 let full_len = remaining.len() / 3 * 3;
509 let mut offset = 0;
510 let mut encoded = [0u8; 1024];
511 while offset < full_len {
512 let mut take = core::cmp::min(full_len - offset, 768);
513 take -= take % 3;
514 debug_assert!(take > 0);
515
516 let written = self
517 .engine
518 .encode_slice(&remaining[offset..offset + take], &mut encoded)
519 .map_err(encode_error_to_io)?;
520 self.inner_mut().write_all(&encoded[..written])?;
521 offset += take;
522 }
523
524 let tail = &remaining[full_len..];
525 self.pending[..tail.len()].copy_from_slice(tail);
526 self.pending_len = tail.len();
527
528 Ok(input.len())
529 }
530
531 fn flush(&mut self) -> io::Result<()> {
532 self.inner_mut().flush()
533 }
534 }
535
536 fn encode_error_to_io(err: EncodeError) -> io::Error {
537 io::Error::new(io::ErrorKind::InvalidInput, err)
538 }
539
540 pub struct Decoder<W, A, const PAD: bool>
542 where
543 A: Alphabet,
544 {
545 inner: W,
546 engine: Engine<A, PAD>,
547 pending: [u8; 4],
548 pending_len: usize,
549 finished: bool,
550 }
551
552 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
553 where
554 A: Alphabet,
555 {
556 #[must_use]
558 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
559 Self {
560 inner,
561 engine,
562 pending: [0; 4],
563 pending_len: 0,
564 finished: false,
565 }
566 }
567
568 #[must_use]
570 pub const fn get_ref(&self) -> &W {
571 &self.inner
572 }
573
574 pub fn get_mut(&mut self) -> &mut W {
576 &mut self.inner
577 }
578
579 #[must_use]
583 pub fn into_inner(self) -> W {
584 self.inner
585 }
586 }
587
588 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
589 where
590 W: Write,
591 A: Alphabet,
592 {
593 pub fn finish(mut self) -> io::Result<W> {
595 self.write_pending_final()?;
596 self.inner.flush()?;
597 Ok(self.inner)
598 }
599
600 fn write_pending_final(&mut self) -> io::Result<()> {
601 if self.pending_len == 0 {
602 return Ok(());
603 }
604
605 let mut decoded = [0u8; 3];
606 let written = self
607 .engine
608 .decode_slice(&self.pending[..self.pending_len], &mut decoded)
609 .map_err(decode_error_to_io)?;
610 self.inner.write_all(&decoded[..written])?;
611 self.pending_len = 0;
612 Ok(())
613 }
614
615 fn write_full_quad(&mut self, input: [u8; 4]) -> io::Result<()> {
616 let mut decoded = [0u8; 3];
617 let written = self
618 .engine
619 .decode_slice(&input, &mut decoded)
620 .map_err(decode_error_to_io)?;
621 self.inner.write_all(&decoded[..written])?;
622 if written < 3 {
623 self.finished = true;
624 }
625 Ok(())
626 }
627 }
628
629 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
630 where
631 W: Write,
632 A: Alphabet,
633 {
634 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
635 if input.is_empty() {
636 return Ok(0);
637 }
638 if self.finished {
639 return Err(trailing_input_after_padding_error());
640 }
641
642 let mut consumed = 0;
643 if self.pending_len > 0 {
644 let needed = 4 - self.pending_len;
645 if input.len() < needed {
646 self.pending[self.pending_len..self.pending_len + input.len()]
647 .copy_from_slice(input);
648 self.pending_len += input.len();
649 return Ok(input.len());
650 }
651
652 let mut quad = [0u8; 4];
653 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
654 quad[self.pending_len..].copy_from_slice(&input[..needed]);
655 self.write_full_quad(quad)?;
656 self.pending_len = 0;
657 consumed += needed;
658 if self.finished && consumed < input.len() {
659 return Err(trailing_input_after_padding_error());
660 }
661 }
662
663 let remaining = &input[consumed..];
664 let full_len = remaining.len() / 4 * 4;
665 let mut offset = 0;
666 while offset < full_len {
667 let quad = [
668 remaining[offset],
669 remaining[offset + 1],
670 remaining[offset + 2],
671 remaining[offset + 3],
672 ];
673 self.write_full_quad(quad)?;
674 offset += 4;
675 if self.finished && offset < remaining.len() {
676 return Err(trailing_input_after_padding_error());
677 }
678 }
679
680 let tail = &remaining[full_len..];
681 self.pending[..tail.len()].copy_from_slice(tail);
682 self.pending_len = tail.len();
683
684 Ok(input.len())
685 }
686
687 fn flush(&mut self) -> io::Result<()> {
688 self.inner.flush()
689 }
690 }
691
692 fn decode_error_to_io(err: DecodeError) -> io::Error {
693 io::Error::new(io::ErrorKind::InvalidInput, err)
694 }
695
696 fn trailing_input_after_padding_error() -> io::Error {
697 io::Error::new(
698 io::ErrorKind::InvalidInput,
699 "base64 decoder received trailing input after padding",
700 )
701 }
702
703 pub struct DecoderReader<R, A, const PAD: bool>
710 where
711 A: Alphabet,
712 {
713 inner: R,
714 engine: Engine<A, PAD>,
715 pending: [u8; 4],
716 pending_len: usize,
717 output: VecDeque<u8>,
718 finished: bool,
719 terminal_seen: bool,
720 }
721
722 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
723 where
724 A: Alphabet,
725 {
726 #[must_use]
728 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
729 Self {
730 inner,
731 engine,
732 pending: [0; 4],
733 pending_len: 0,
734 output: VecDeque::new(),
735 finished: false,
736 terminal_seen: false,
737 }
738 }
739
740 #[must_use]
742 pub const fn get_ref(&self) -> &R {
743 &self.inner
744 }
745
746 pub fn get_mut(&mut self) -> &mut R {
748 &mut self.inner
749 }
750
751 #[must_use]
753 pub fn into_inner(self) -> R {
754 self.inner
755 }
756 }
757
758 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
759 where
760 R: Read,
761 A: Alphabet,
762 {
763 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
764 if output.is_empty() {
765 return Ok(0);
766 }
767
768 while self.output.is_empty() && !self.finished {
769 self.fill_output()?;
770 }
771
772 let mut written = 0;
773 while written < output.len() {
774 let Some(byte) = self.output.pop_front() else {
775 break;
776 };
777 output[written] = byte;
778 written += 1;
779 }
780
781 Ok(written)
782 }
783 }
784
785 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
786 where
787 R: Read,
788 A: Alphabet,
789 {
790 fn fill_output(&mut self) -> io::Result<()> {
791 if self.terminal_seen {
792 self.finished = true;
793 return Ok(());
794 }
795
796 let mut input = [0u8; 4];
797 let read = self.inner.read(&mut input[..4 - self.pending_len])?;
798 if read == 0 {
799 self.finished = true;
800 self.push_final_pending()?;
801 return Ok(());
802 }
803
804 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
805 self.pending_len += read;
806 if self.pending_len < 4 {
807 return Ok(());
808 }
809
810 let quad = self.pending;
811 self.pending_len = 0;
812 self.push_decoded(&quad)?;
813 if self.terminal_seen {
814 self.finished = true;
815 }
816 Ok(())
817 }
818
819 fn push_final_pending(&mut self) -> io::Result<()> {
820 if self.pending_len == 0 {
821 return Ok(());
822 }
823
824 let mut pending = [0u8; 4];
825 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
826 let pending_len = self.pending_len;
827 self.pending_len = 0;
828 self.push_decoded(&pending[..pending_len])
829 }
830
831 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
832 let mut decoded = [0u8; 3];
833 let written = self
834 .engine
835 .decode_slice(input, &mut decoded)
836 .map_err(decode_error_to_io)?;
837 self.output.extend(&decoded[..written]);
838 if input.len() == 4 && written < 3 {
839 self.terminal_seen = true;
840 }
841 Ok(())
842 }
843 }
844
845 pub struct EncoderReader<R, A, const PAD: bool>
847 where
848 A: Alphabet,
849 {
850 inner: Option<R>,
851 engine: Engine<A, PAD>,
852 pending: [u8; 2],
853 pending_len: usize,
854 output: VecDeque<u8>,
855 finished: bool,
856 }
857
858 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
859 where
860 A: Alphabet,
861 {
862 #[must_use]
864 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
865 Self {
866 inner: Some(inner),
867 engine,
868 pending: [0; 2],
869 pending_len: 0,
870 output: VecDeque::new(),
871 finished: false,
872 }
873 }
874
875 #[must_use]
877 pub fn get_ref(&self) -> &R {
878 self.inner_ref()
879 }
880
881 pub fn get_mut(&mut self) -> &mut R {
883 self.inner_mut()
884 }
885
886 #[must_use]
888 pub fn into_inner(mut self) -> R {
889 self.take_inner()
890 }
891
892 fn inner_ref(&self) -> &R {
893 match &self.inner {
894 Some(inner) => inner,
895 None => unreachable!("stream encoder reader inner reader was already taken"),
896 }
897 }
898
899 fn inner_mut(&mut self) -> &mut R {
900 match &mut self.inner {
901 Some(inner) => inner,
902 None => unreachable!("stream encoder reader inner reader was already taken"),
903 }
904 }
905
906 fn take_inner(&mut self) -> R {
907 match self.inner.take() {
908 Some(inner) => inner,
909 None => unreachable!("stream encoder reader inner reader was already taken"),
910 }
911 }
912
913 fn clear_pending(&mut self) {
914 self.pending.fill(0);
915 self.pending_len = 0;
916 }
917 }
918
919 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
920 where
921 A: Alphabet,
922 {
923 fn drop(&mut self) {
924 self.clear_pending();
925 for byte in &mut self.output {
926 *byte = 0;
927 }
928 }
929 }
930
931 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
932 where
933 R: Read,
934 A: Alphabet,
935 {
936 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
937 if output.is_empty() {
938 return Ok(0);
939 }
940
941 while self.output.is_empty() && !self.finished {
942 self.fill_output()?;
943 }
944
945 let mut written = 0;
946 while written < output.len() {
947 let Some(byte) = self.output.pop_front() else {
948 break;
949 };
950 output[written] = byte;
951 written += 1;
952 }
953
954 Ok(written)
955 }
956 }
957
958 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
959 where
960 R: Read,
961 A: Alphabet,
962 {
963 fn fill_output(&mut self) -> io::Result<()> {
964 let mut input = [0u8; 768];
965 let read = self.inner_mut().read(&mut input)?;
966 if read == 0 {
967 self.finished = true;
968 self.push_final_pending()?;
969 return Ok(());
970 }
971
972 let mut consumed = 0;
973 if self.pending_len > 0 {
974 let needed = 3 - self.pending_len;
975 if read < needed {
976 self.pending[self.pending_len..self.pending_len + read]
977 .copy_from_slice(&input[..read]);
978 self.pending_len += read;
979 return Ok(());
980 }
981
982 let mut chunk = [0u8; 3];
983 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
984 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
985 self.push_encoded(&chunk)?;
986 self.clear_pending();
987 consumed += needed;
988 }
989
990 let remaining = &input[consumed..read];
991 let full_len = remaining.len() / 3 * 3;
992 if full_len > 0 {
993 self.push_encoded(&remaining[..full_len])?;
994 }
995
996 let tail = &remaining[full_len..];
997 self.pending[..tail.len()].copy_from_slice(tail);
998 self.pending_len = tail.len();
999 Ok(())
1000 }
1001
1002 fn push_final_pending(&mut self) -> io::Result<()> {
1003 if self.pending_len == 0 {
1004 return Ok(());
1005 }
1006
1007 let mut pending = [0u8; 2];
1008 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1009 let pending_len = self.pending_len;
1010 self.clear_pending();
1011 self.push_encoded(&pending[..pending_len])
1012 }
1013
1014 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1015 let mut encoded = [0u8; 1024];
1016 let written = self
1017 .engine
1018 .encode_slice(input, &mut encoded)
1019 .map_err(encode_error_to_io)?;
1020 self.output.extend(&encoded[..written]);
1021 Ok(())
1022 }
1023 }
1024}
1025
1026pub mod ct {
1033 use super::{Alphabet, DecodeError, Standard, UrlSafe, ct_decode_in_place, ct_decode_slice};
1034 use core::marker::PhantomData;
1035
1036 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
1038
1039 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
1041
1042 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
1044
1045 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
1047
1048 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1050 pub struct CtEngine<A, const PAD: bool> {
1051 alphabet: PhantomData<A>,
1052 }
1053
1054 impl<A, const PAD: bool> CtEngine<A, PAD>
1055 where
1056 A: Alphabet,
1057 {
1058 #[must_use]
1060 pub const fn new() -> Self {
1061 Self {
1062 alphabet: PhantomData,
1063 }
1064 }
1065
1066 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1088 ct_decode_slice::<A, PAD>(input, output)
1089 }
1090
1091 pub fn decode_slice_clear_tail(
1113 &self,
1114 input: &[u8],
1115 output: &mut [u8],
1116 ) -> Result<usize, DecodeError> {
1117 let written = match self.decode_slice(input, output) {
1118 Ok(written) => written,
1119 Err(err) => {
1120 output.fill(0);
1121 return Err(err);
1122 }
1123 };
1124 output[written..].fill(0);
1125 Ok(written)
1126 }
1127
1128 pub fn decode_in_place<'a>(
1145 &self,
1146 buffer: &'a mut [u8],
1147 ) -> Result<&'a mut [u8], DecodeError> {
1148 let len = ct_decode_in_place::<A, PAD>(buffer)?;
1149 Ok(&mut buffer[..len])
1150 }
1151
1152 pub fn decode_in_place_clear_tail<'a>(
1169 &self,
1170 buffer: &'a mut [u8],
1171 ) -> Result<&'a mut [u8], DecodeError> {
1172 let len = match ct_decode_in_place::<A, PAD>(buffer) {
1173 Ok(len) => len,
1174 Err(err) => {
1175 buffer.fill(0);
1176 return Err(err);
1177 }
1178 };
1179 buffer[len..].fill(0);
1180 Ok(&mut buffer[..len])
1181 }
1182 }
1183}
1184
1185pub const STANDARD: Engine<Standard, true> = Engine::new();
1187
1188pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
1190
1191pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
1193
1194pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
1196
1197pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
1212 match checked_encoded_len(input_len, padded) {
1213 Some(len) => Ok(len),
1214 None => Err(EncodeError::LengthOverflow),
1215 }
1216}
1217
1218#[must_use]
1229pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
1230 let groups = input_len / 3;
1231 if groups > usize::MAX / 4 {
1232 return None;
1233 }
1234 let full = groups * 4;
1235 let rem = input_len % 3;
1236 if rem == 0 {
1237 Some(full)
1238 } else if padded {
1239 full.checked_add(4)
1240 } else {
1241 full.checked_add(rem + 1)
1242 }
1243}
1244
1245#[must_use]
1256pub const fn decoded_capacity(encoded_len: usize) -> usize {
1257 let rem = encoded_len % 4;
1258 encoded_len / 4 * 3
1259 + if rem == 2 {
1260 1
1261 } else if rem == 3 {
1262 2
1263 } else {
1264 0
1265 }
1266}
1267
1268pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
1282 if padded {
1283 decoded_len_padded(input)
1284 } else {
1285 decoded_len_unpadded(input)
1286 }
1287}
1288
1289pub trait Alphabet {
1291 const ENCODE: [u8; 64];
1293
1294 fn decode(byte: u8) -> Option<u8>;
1296}
1297
1298#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1300pub struct Standard;
1301
1302impl Alphabet for Standard {
1303 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1304
1305 #[inline]
1306 fn decode(byte: u8) -> Option<u8> {
1307 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
1308 }
1309}
1310
1311#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1313pub struct UrlSafe;
1314
1315impl Alphabet for UrlSafe {
1316 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1317
1318 #[inline]
1319 fn decode(byte: u8) -> Option<u8> {
1320 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
1321 }
1322}
1323
1324#[inline]
1325const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
1326 encode_ascii_base64(value, A::ENCODE[62], A::ENCODE[63])
1327}
1328
1329#[inline]
1330const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
1331 let upper = mask_if(value < 26);
1332 let lower = mask_if(value.wrapping_sub(26) < 26);
1333 let digit = mask_if(value.wrapping_sub(52) < 10);
1334 let value_62 = mask_if(value == 0x3e);
1335 let value_63 = mask_if(value == 0x3f);
1336
1337 (value.wrapping_add(b'A') & upper)
1338 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
1339 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
1340 | (value_62_byte & value_62)
1341 | (value_63_byte & value_63)
1342}
1343
1344#[inline]
1345fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
1346 let upper = mask_if(byte.wrapping_sub(b'A') <= b'Z' - b'A');
1347 let lower = mask_if(byte.wrapping_sub(b'a') <= b'z' - b'a');
1348 let digit = mask_if(byte.wrapping_sub(b'0') <= b'9' - b'0');
1349 let value_62 = mask_if(byte == value_62_byte);
1350 let value_63 = mask_if(byte == value_63_byte);
1351 let valid = upper | lower | digit | value_62 | value_63;
1352
1353 let decoded = (byte.wrapping_sub(b'A') & upper)
1354 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
1355 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
1356 | (0x3e & value_62)
1357 | (0x3f & value_63);
1358
1359 if valid == 0 { None } else { Some(decoded) }
1360}
1361
1362#[inline]
1363const fn mask_if(condition: bool) -> u8 {
1364 0u8.wrapping_sub(condition as u8)
1365}
1366
1367mod backend {
1368 use super::{
1369 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
1370 encode_base64_value,
1371 };
1372
1373 pub(super) fn encode_slice<A, const PAD: bool>(
1374 input: &[u8],
1375 output: &mut [u8],
1376 ) -> Result<usize, EncodeError>
1377 where
1378 A: Alphabet,
1379 {
1380 #[cfg(feature = "simd")]
1381 match super::simd::active_backend() {
1382 super::simd::ActiveBackend::Scalar => {}
1383 }
1384
1385 scalar_encode_slice::<A, PAD>(input, output)
1386 }
1387
1388 pub(super) fn decode_slice<A, const PAD: bool>(
1389 input: &[u8],
1390 output: &mut [u8],
1391 ) -> Result<usize, DecodeError>
1392 where
1393 A: Alphabet,
1394 {
1395 #[cfg(feature = "simd")]
1396 match super::simd::active_backend() {
1397 super::simd::ActiveBackend::Scalar => {}
1398 }
1399
1400 scalar_decode_slice::<A, PAD>(input, output)
1401 }
1402
1403 #[cfg(test)]
1404 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
1405 input: &[u8],
1406 output: &mut [u8],
1407 ) -> Result<usize, EncodeError>
1408 where
1409 A: Alphabet,
1410 {
1411 scalar_encode_slice::<A, PAD>(input, output)
1412 }
1413
1414 #[cfg(test)]
1415 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
1416 input: &[u8],
1417 output: &mut [u8],
1418 ) -> Result<usize, DecodeError>
1419 where
1420 A: Alphabet,
1421 {
1422 scalar_decode_slice::<A, PAD>(input, output)
1423 }
1424
1425 fn scalar_encode_slice<A, const PAD: bool>(
1426 input: &[u8],
1427 output: &mut [u8],
1428 ) -> Result<usize, EncodeError>
1429 where
1430 A: Alphabet,
1431 {
1432 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1433 if output.len() < required {
1434 return Err(EncodeError::OutputTooSmall {
1435 required,
1436 available: output.len(),
1437 });
1438 }
1439
1440 let mut read = 0;
1441 let mut write = 0;
1442 while read + 3 <= input.len() {
1443 let b0 = input[read];
1444 let b1 = input[read + 1];
1445 let b2 = input[read + 2];
1446
1447 output[write] = encode_base64_value::<A>(b0 >> 2);
1448 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1449 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1450 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1451
1452 read += 3;
1453 write += 4;
1454 }
1455
1456 match input.len() - read {
1457 0 => {}
1458 1 => {
1459 let b0 = input[read];
1460 output[write] = encode_base64_value::<A>(b0 >> 2);
1461 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1462 write += 2;
1463 if PAD {
1464 output[write] = b'=';
1465 output[write + 1] = b'=';
1466 write += 2;
1467 }
1468 }
1469 2 => {
1470 let b0 = input[read];
1471 let b1 = input[read + 1];
1472 output[write] = encode_base64_value::<A>(b0 >> 2);
1473 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1474 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1475 write += 3;
1476 if PAD {
1477 output[write] = b'=';
1478 write += 1;
1479 }
1480 }
1481 _ => unreachable!(),
1482 }
1483
1484 Ok(write)
1485 }
1486
1487 fn scalar_decode_slice<A, const PAD: bool>(
1488 input: &[u8],
1489 output: &mut [u8],
1490 ) -> Result<usize, DecodeError>
1491 where
1492 A: Alphabet,
1493 {
1494 if input.is_empty() {
1495 return Ok(0);
1496 }
1497
1498 if PAD {
1499 decode_padded::<A>(input, output)
1500 } else {
1501 decode_unpadded::<A>(input, output)
1502 }
1503 }
1504}
1505
1506#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1508pub struct Engine<A, const PAD: bool> {
1509 alphabet: core::marker::PhantomData<A>,
1510}
1511
1512impl<A, const PAD: bool> Engine<A, PAD>
1513where
1514 A: Alphabet,
1515{
1516 #[must_use]
1518 pub const fn new() -> Self {
1519 Self {
1520 alphabet: core::marker::PhantomData,
1521 }
1522 }
1523
1524 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
1526 encoded_len(input_len, PAD)
1527 }
1528
1529 #[must_use]
1531 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
1532 checked_encoded_len(input_len, PAD)
1533 }
1534
1535 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
1540 decoded_len(input, PAD)
1541 }
1542
1543 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
1549 validate_legacy_decode::<A, PAD>(input)
1550 }
1551
1552 #[must_use]
1584 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
1585 &self,
1586 input: &[u8; INPUT_LEN],
1587 ) -> [u8; OUTPUT_LEN] {
1588 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
1589 panic!("encoded base64 length overflows usize");
1590 };
1591 assert!(
1592 required == OUTPUT_LEN,
1593 "base64 output array has incorrect length"
1594 );
1595
1596 let mut output = [0u8; OUTPUT_LEN];
1597 let mut read = 0;
1598 let mut write = 0;
1599 while INPUT_LEN - read >= 3 {
1600 let b0 = input[read];
1601 let b1 = input[read + 1];
1602 let b2 = input[read + 2];
1603
1604 output[write] = encode_base64_value::<A>(b0 >> 2);
1605 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1606 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1607 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1608
1609 read += 3;
1610 write += 4;
1611 }
1612
1613 match INPUT_LEN - read {
1614 0 => {}
1615 1 => {
1616 let b0 = input[read];
1617 output[write] = encode_base64_value::<A>(b0 >> 2);
1618 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1619 write += 2;
1620 if PAD {
1621 output[write] = b'=';
1622 output[write + 1] = b'=';
1623 }
1624 }
1625 2 => {
1626 let b0 = input[read];
1627 let b1 = input[read + 1];
1628 output[write] = encode_base64_value::<A>(b0 >> 2);
1629 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1630 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1631 if PAD {
1632 output[write + 3] = b'=';
1633 }
1634 }
1635 _ => unreachable!(),
1636 }
1637
1638 output
1639 }
1640
1641 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
1643 backend::encode_slice::<A, PAD>(input, output)
1644 }
1645
1646 pub fn encode_slice_clear_tail(
1666 &self,
1667 input: &[u8],
1668 output: &mut [u8],
1669 ) -> Result<usize, EncodeError> {
1670 let written = match self.encode_slice(input, output) {
1671 Ok(written) => written,
1672 Err(err) => {
1673 output.fill(0);
1674 return Err(err);
1675 }
1676 };
1677 output[written..].fill(0);
1678 Ok(written)
1679 }
1680
1681 #[cfg(feature = "alloc")]
1683 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
1684 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1685 let mut output = alloc::vec![0; required];
1686 let written = self.encode_slice(input, &mut output)?;
1687 output.truncate(written);
1688 Ok(output)
1689 }
1690
1691 #[cfg(feature = "alloc")]
1706 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
1707 let output = self.encode_vec(input)?;
1708 match alloc::string::String::from_utf8(output) {
1709 Ok(output) => Ok(output),
1710 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1711 }
1712 }
1713
1714 pub fn encode_in_place<'a>(
1731 &self,
1732 buffer: &'a mut [u8],
1733 input_len: usize,
1734 ) -> Result<&'a mut [u8], EncodeError> {
1735 if input_len > buffer.len() {
1736 return Err(EncodeError::InputTooLarge {
1737 input_len,
1738 buffer_len: buffer.len(),
1739 });
1740 }
1741
1742 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
1743 if buffer.len() < required {
1744 return Err(EncodeError::OutputTooSmall {
1745 required,
1746 available: buffer.len(),
1747 });
1748 }
1749
1750 let mut read = input_len;
1751 let mut write = required;
1752
1753 match input_len % 3 {
1754 0 => {}
1755 1 => {
1756 read -= 1;
1757 let b0 = buffer[read];
1758 if PAD {
1759 write -= 4;
1760 buffer[write] = encode_base64_value::<A>(b0 >> 2);
1761 buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1762 buffer[write + 2] = b'=';
1763 buffer[write + 3] = b'=';
1764 } else {
1765 write -= 2;
1766 buffer[write] = encode_base64_value::<A>(b0 >> 2);
1767 buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1768 }
1769 }
1770 2 => {
1771 read -= 2;
1772 let b0 = buffer[read];
1773 let b1 = buffer[read + 1];
1774 if PAD {
1775 write -= 4;
1776 buffer[write] = encode_base64_value::<A>(b0 >> 2);
1777 buffer[write + 1] =
1778 encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1779 buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1780 buffer[write + 3] = b'=';
1781 } else {
1782 write -= 3;
1783 buffer[write] = encode_base64_value::<A>(b0 >> 2);
1784 buffer[write + 1] =
1785 encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1786 buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1787 }
1788 }
1789 _ => unreachable!(),
1790 }
1791
1792 while read > 0 {
1793 read -= 3;
1794 write -= 4;
1795 let b0 = buffer[read];
1796 let b1 = buffer[read + 1];
1797 let b2 = buffer[read + 2];
1798
1799 buffer[write] = encode_base64_value::<A>(b0 >> 2);
1800 buffer[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1801 buffer[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1802 buffer[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1803 }
1804
1805 debug_assert_eq!(write, 0);
1806 Ok(&mut buffer[..required])
1807 }
1808
1809 pub fn encode_in_place_clear_tail<'a>(
1827 &self,
1828 buffer: &'a mut [u8],
1829 input_len: usize,
1830 ) -> Result<&'a mut [u8], EncodeError> {
1831 let len = match self.encode_in_place(buffer, input_len) {
1832 Ok(encoded) => encoded.len(),
1833 Err(err) => {
1834 buffer.fill(0);
1835 return Err(err);
1836 }
1837 };
1838 buffer[len..].fill(0);
1839 Ok(&mut buffer[..len])
1840 }
1841
1842 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1847 backend::decode_slice::<A, PAD>(input, output)
1848 }
1849
1850 pub fn decode_slice_clear_tail(
1870 &self,
1871 input: &[u8],
1872 output: &mut [u8],
1873 ) -> Result<usize, DecodeError> {
1874 let written = match self.decode_slice(input, output) {
1875 Ok(written) => written,
1876 Err(err) => {
1877 output.fill(0);
1878 return Err(err);
1879 }
1880 };
1881 output[written..].fill(0);
1882 Ok(written)
1883 }
1884
1885 pub fn decode_slice_legacy(
1891 &self,
1892 input: &[u8],
1893 output: &mut [u8],
1894 ) -> Result<usize, DecodeError> {
1895 let required = validate_legacy_decode::<A, PAD>(input)?;
1896 if output.len() < required {
1897 return Err(DecodeError::OutputTooSmall {
1898 required,
1899 available: output.len(),
1900 });
1901 }
1902 decode_legacy_to_slice::<A, PAD>(input, output)
1903 }
1904
1905 pub fn decode_slice_legacy_clear_tail(
1925 &self,
1926 input: &[u8],
1927 output: &mut [u8],
1928 ) -> Result<usize, DecodeError> {
1929 let written = match self.decode_slice_legacy(input, output) {
1930 Ok(written) => written,
1931 Err(err) => {
1932 output.fill(0);
1933 return Err(err);
1934 }
1935 };
1936 output[written..].fill(0);
1937 Ok(written)
1938 }
1939
1940 #[cfg(feature = "alloc")]
1944 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1945 let required = validate_decode::<A, PAD>(input)?;
1946 let mut output = alloc::vec![0; required];
1947 let written = match self.decode_slice(input, &mut output) {
1948 Ok(written) => written,
1949 Err(err) => {
1950 output.fill(0);
1951 return Err(err);
1952 }
1953 };
1954 output.truncate(written);
1955 Ok(output)
1956 }
1957
1958 #[cfg(feature = "alloc")]
1961 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1962 let required = validate_legacy_decode::<A, PAD>(input)?;
1963 let mut output = alloc::vec![0; required];
1964 let written = match self.decode_slice_legacy(input, &mut output) {
1965 Ok(written) => written,
1966 Err(err) => {
1967 output.fill(0);
1968 return Err(err);
1969 }
1970 };
1971 output.truncate(written);
1972 Ok(output)
1973 }
1974
1975 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
1987 let len = Self::decode_slice_to_start(buffer)?;
1988 Ok(&mut buffer[..len])
1989 }
1990
1991 pub fn decode_in_place_clear_tail<'a>(
2008 &self,
2009 buffer: &'a mut [u8],
2010 ) -> Result<&'a mut [u8], DecodeError> {
2011 let len = match Self::decode_slice_to_start(buffer) {
2012 Ok(len) => len,
2013 Err(err) => {
2014 buffer.fill(0);
2015 return Err(err);
2016 }
2017 };
2018 buffer[len..].fill(0);
2019 Ok(&mut buffer[..len])
2020 }
2021
2022 pub fn decode_in_place_legacy<'a>(
2027 &self,
2028 buffer: &'a mut [u8],
2029 ) -> Result<&'a mut [u8], DecodeError> {
2030 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
2031 let mut write = 0;
2032 let mut read = 0;
2033 while read < buffer.len() {
2034 let byte = buffer[read];
2035 if !is_legacy_whitespace(byte) {
2036 buffer[write] = byte;
2037 write += 1;
2038 }
2039 read += 1;
2040 }
2041 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
2042 Ok(&mut buffer[..len])
2043 }
2044
2045 pub fn decode_in_place_legacy_clear_tail<'a>(
2051 &self,
2052 buffer: &'a mut [u8],
2053 ) -> Result<&'a mut [u8], DecodeError> {
2054 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
2055 buffer.fill(0);
2056 return Err(err);
2057 }
2058
2059 let mut write = 0;
2060 let mut read = 0;
2061 while read < buffer.len() {
2062 let byte = buffer[read];
2063 if !is_legacy_whitespace(byte) {
2064 buffer[write] = byte;
2065 write += 1;
2066 }
2067 read += 1;
2068 }
2069
2070 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
2071 Ok(len) => len,
2072 Err(err) => {
2073 buffer.fill(0);
2074 return Err(err);
2075 }
2076 };
2077 buffer[len..].fill(0);
2078 Ok(&mut buffer[..len])
2079 }
2080
2081 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
2082 let input_len = buffer.len();
2083 let mut read = 0;
2084 let mut write = 0;
2085 while read + 4 <= input_len {
2086 let chunk = [
2087 buffer[read],
2088 buffer[read + 1],
2089 buffer[read + 2],
2090 buffer[read + 3],
2091 ];
2092 let written = decode_chunk::<A, PAD>(&chunk, &mut buffer[write..])
2093 .map_err(|err| err.with_index_offset(read))?;
2094 read += 4;
2095 write += written;
2096 if written < 3 {
2097 if read != input_len {
2098 return Err(DecodeError::InvalidPadding { index: read - 4 });
2099 }
2100 return Ok(write);
2101 }
2102 }
2103
2104 let rem = input_len - read;
2105 if rem == 0 {
2106 return Ok(write);
2107 }
2108 if PAD {
2109 return Err(DecodeError::InvalidLength);
2110 }
2111 let mut tail = [0u8; 3];
2112 tail[..rem].copy_from_slice(&buffer[read..input_len]);
2113 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
2114 .map_err(|err| err.with_index_offset(read))
2115 .map(|n| write + n)
2116 }
2117}
2118
2119#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2121pub enum EncodeError {
2122 LengthOverflow,
2124 InputTooLarge {
2126 input_len: usize,
2128 buffer_len: usize,
2130 },
2131 OutputTooSmall {
2133 required: usize,
2135 available: usize,
2137 },
2138}
2139
2140impl core::fmt::Display for EncodeError {
2141 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2142 match self {
2143 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
2144 Self::InputTooLarge {
2145 input_len,
2146 buffer_len,
2147 } => write!(
2148 f,
2149 "base64 input length {input_len} exceeds buffer length {buffer_len}"
2150 ),
2151 Self::OutputTooSmall {
2152 required,
2153 available,
2154 } => write!(
2155 f,
2156 "base64 output buffer too small: required {required}, available {available}"
2157 ),
2158 }
2159 }
2160}
2161
2162#[cfg(feature = "std")]
2163impl std::error::Error for EncodeError {}
2164
2165#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2167pub enum DecodeError {
2168 InvalidLength,
2170 InvalidByte {
2172 index: usize,
2174 byte: u8,
2176 },
2177 InvalidPadding {
2179 index: usize,
2181 },
2182 OutputTooSmall {
2184 required: usize,
2186 available: usize,
2188 },
2189}
2190
2191impl core::fmt::Display for DecodeError {
2192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2193 match self {
2194 Self::InvalidLength => f.write_str("invalid base64 input length"),
2195 Self::InvalidByte { index, byte } => {
2196 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
2197 }
2198 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
2199 Self::OutputTooSmall {
2200 required,
2201 available,
2202 } => write!(
2203 f,
2204 "base64 decode output buffer too small: required {required}, available {available}"
2205 ),
2206 }
2207 }
2208}
2209
2210impl DecodeError {
2211 fn with_index_offset(self, offset: usize) -> Self {
2212 match self {
2213 Self::InvalidByte { index, byte } => Self::InvalidByte {
2214 index: index + offset,
2215 byte,
2216 },
2217 Self::InvalidPadding { index } => Self::InvalidPadding {
2218 index: index + offset,
2219 },
2220 Self::InvalidLength | Self::OutputTooSmall { .. } => self,
2221 }
2222 }
2223}
2224
2225#[cfg(feature = "std")]
2226impl std::error::Error for DecodeError {}
2227
2228fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
2229 input: &[u8],
2230) -> Result<usize, DecodeError> {
2231 let mut chunk = [0u8; 4];
2232 let mut indexes = [0usize; 4];
2233 let mut chunk_len = 0;
2234 let mut required = 0;
2235 let mut terminal_seen = false;
2236
2237 for (index, byte) in input.iter().copied().enumerate() {
2238 if is_legacy_whitespace(byte) {
2239 continue;
2240 }
2241 if terminal_seen {
2242 return Err(DecodeError::InvalidPadding { index });
2243 }
2244
2245 chunk[chunk_len] = byte;
2246 indexes[chunk_len] = index;
2247 chunk_len += 1;
2248
2249 if chunk_len == 4 {
2250 let written =
2251 validate_chunk::<A, PAD>(&chunk).map_err(|err| map_chunk_error(err, &indexes))?;
2252 required += written;
2253 terminal_seen = written < 3;
2254 chunk_len = 0;
2255 }
2256 }
2257
2258 if chunk_len == 0 {
2259 return Ok(required);
2260 }
2261 if PAD {
2262 return Err(DecodeError::InvalidLength);
2263 }
2264
2265 validate_tail_unpadded::<A>(&chunk[..chunk_len])
2266 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
2267 Ok(required + decoded_capacity(chunk_len))
2268}
2269
2270fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
2271 input: &[u8],
2272 output: &mut [u8],
2273) -> Result<usize, DecodeError> {
2274 let mut chunk = [0u8; 4];
2275 let mut indexes = [0usize; 4];
2276 let mut chunk_len = 0;
2277 let mut write = 0;
2278 let mut terminal_seen = false;
2279
2280 for (index, byte) in input.iter().copied().enumerate() {
2281 if is_legacy_whitespace(byte) {
2282 continue;
2283 }
2284 if terminal_seen {
2285 return Err(DecodeError::InvalidPadding { index });
2286 }
2287
2288 chunk[chunk_len] = byte;
2289 indexes[chunk_len] = index;
2290 chunk_len += 1;
2291
2292 if chunk_len == 4 {
2293 let written = decode_chunk::<A, PAD>(&chunk, &mut output[write..])
2294 .map_err(|err| map_chunk_error(err, &indexes))?;
2295 write += written;
2296 terminal_seen = written < 3;
2297 chunk_len = 0;
2298 }
2299 }
2300
2301 if chunk_len == 0 {
2302 return Ok(write);
2303 }
2304 if PAD {
2305 return Err(DecodeError::InvalidLength);
2306 }
2307
2308 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
2309 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
2310 .map(|n| write + n)
2311}
2312
2313#[inline]
2314const fn is_legacy_whitespace(byte: u8) -> bool {
2315 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
2316}
2317
2318fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
2319 match err {
2320 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
2321 index: indexes[index],
2322 byte,
2323 },
2324 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
2325 index: indexes[index],
2326 },
2327 DecodeError::InvalidLength | DecodeError::OutputTooSmall { .. } => err,
2328 }
2329}
2330
2331fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
2332 match err {
2333 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
2334 index: indexes[index],
2335 byte,
2336 },
2337 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
2338 index: indexes[index],
2339 },
2340 DecodeError::InvalidByte { .. }
2341 | DecodeError::InvalidPadding { .. }
2342 | DecodeError::InvalidLength
2343 | DecodeError::OutputTooSmall { .. } => err,
2344 }
2345}
2346
2347fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2348 if !input.len().is_multiple_of(4) {
2349 return Err(DecodeError::InvalidLength);
2350 }
2351 let required = decoded_len_padded(input)?;
2352 if output.len() < required {
2353 return Err(DecodeError::OutputTooSmall {
2354 required,
2355 available: output.len(),
2356 });
2357 }
2358
2359 let mut read = 0;
2360 let mut write = 0;
2361 while read < input.len() {
2362 let written = decode_chunk::<A, true>(&input[read..read + 4], &mut output[write..])
2363 .map_err(|err| err.with_index_offset(read))?;
2364 read += 4;
2365 write += written;
2366 if written < 3 && read != input.len() {
2367 return Err(DecodeError::InvalidPadding { index: read - 4 });
2368 }
2369 }
2370 Ok(write)
2371}
2372
2373#[cfg(feature = "alloc")]
2374fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
2375 if input.is_empty() {
2376 return Ok(0);
2377 }
2378
2379 if PAD {
2380 validate_padded::<A>(input)
2381 } else {
2382 validate_unpadded::<A>(input)
2383 }
2384}
2385
2386#[cfg(feature = "alloc")]
2387fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
2388 if !input.len().is_multiple_of(4) {
2389 return Err(DecodeError::InvalidLength);
2390 }
2391 let required = decoded_len_padded(input)?;
2392
2393 let mut read = 0;
2394 while read < input.len() {
2395 let written = validate_chunk::<A, true>(&input[read..read + 4])
2396 .map_err(|err| err.with_index_offset(read))?;
2397 read += 4;
2398 if written < 3 && read != input.len() {
2399 return Err(DecodeError::InvalidPadding { index: read - 4 });
2400 }
2401 }
2402
2403 Ok(required)
2404}
2405
2406#[cfg(feature = "alloc")]
2407fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
2408 let required = decoded_len_unpadded(input)?;
2409
2410 let mut read = 0;
2411 while read + 4 <= input.len() {
2412 validate_chunk::<A, false>(&input[read..read + 4])
2413 .map_err(|err| err.with_index_offset(read))?;
2414 read += 4;
2415 }
2416 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
2417
2418 Ok(required)
2419}
2420
2421fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2422 let required = decoded_len_unpadded(input)?;
2423 if output.len() < required {
2424 return Err(DecodeError::OutputTooSmall {
2425 required,
2426 available: output.len(),
2427 });
2428 }
2429
2430 let mut read = 0;
2431 let mut write = 0;
2432 while read + 4 <= input.len() {
2433 let written = decode_chunk::<A, false>(&input[read..read + 4], &mut output[write..])
2434 .map_err(|err| err.with_index_offset(read))?;
2435 read += 4;
2436 write += written;
2437 }
2438 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
2439 .map_err(|err| err.with_index_offset(read))
2440 .map(|n| write + n)
2441}
2442
2443fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
2444 if input.is_empty() {
2445 return Ok(0);
2446 }
2447 if !input.len().is_multiple_of(4) {
2448 return Err(DecodeError::InvalidLength);
2449 }
2450 let mut padding = 0;
2451 if input[input.len() - 1] == b'=' {
2452 padding += 1;
2453 }
2454 if input[input.len() - 2] == b'=' {
2455 padding += 1;
2456 }
2457 if padding == 0
2458 && let Some(index) = input.iter().position(|byte| *byte == b'=')
2459 {
2460 return Err(DecodeError::InvalidPadding { index });
2461 }
2462 if padding > 0 {
2463 let first_pad = input.len() - padding;
2464 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
2465 return Err(DecodeError::InvalidPadding { index });
2466 }
2467 }
2468 Ok(input.len() / 4 * 3 - padding)
2469}
2470
2471fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
2472 if input.len() % 4 == 1 {
2473 return Err(DecodeError::InvalidLength);
2474 }
2475 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
2476 return Err(DecodeError::InvalidPadding { index });
2477 }
2478 Ok(decoded_capacity(input.len()))
2479}
2480
2481fn validate_chunk<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
2482 debug_assert_eq!(input.len(), 4);
2483 let _v0 = decode_byte::<A>(input[0], 0)?;
2484 let v1 = decode_byte::<A>(input[1], 1)?;
2485
2486 match (input[2], input[3]) {
2487 (b'=', b'=') if PAD => {
2488 if v1 & 0b0000_1111 != 0 {
2489 return Err(DecodeError::InvalidPadding { index: 1 });
2490 }
2491 Ok(1)
2492 }
2493 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
2494 (_, b'=') if PAD => {
2495 let v2 = decode_byte::<A>(input[2], 2)?;
2496 if v2 & 0b0000_0011 != 0 {
2497 return Err(DecodeError::InvalidPadding { index: 2 });
2498 }
2499 Ok(2)
2500 }
2501 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
2502 index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
2503 }),
2504 _ => {
2505 decode_byte::<A>(input[2], 2)?;
2506 decode_byte::<A>(input[3], 3)?;
2507 Ok(3)
2508 }
2509 }
2510}
2511
2512fn decode_chunk<A: Alphabet, const PAD: bool>(
2513 input: &[u8],
2514 output: &mut [u8],
2515) -> Result<usize, DecodeError> {
2516 debug_assert_eq!(input.len(), 4);
2517 let v0 = decode_byte::<A>(input[0], 0)?;
2518 let v1 = decode_byte::<A>(input[1], 1)?;
2519
2520 match (input[2], input[3]) {
2521 (b'=', b'=') if PAD => {
2522 if output.is_empty() {
2523 return Err(DecodeError::OutputTooSmall {
2524 required: 1,
2525 available: output.len(),
2526 });
2527 }
2528 if v1 & 0b0000_1111 != 0 {
2529 return Err(DecodeError::InvalidPadding { index: 1 });
2530 }
2531 output[0] = (v0 << 2) | (v1 >> 4);
2532 Ok(1)
2533 }
2534 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
2535 (_, b'=') if PAD => {
2536 if output.len() < 2 {
2537 return Err(DecodeError::OutputTooSmall {
2538 required: 2,
2539 available: output.len(),
2540 });
2541 }
2542 let v2 = decode_byte::<A>(input[2], 2)?;
2543 if v2 & 0b0000_0011 != 0 {
2544 return Err(DecodeError::InvalidPadding { index: 2 });
2545 }
2546 output[0] = (v0 << 2) | (v1 >> 4);
2547 output[1] = (v1 << 4) | (v2 >> 2);
2548 Ok(2)
2549 }
2550 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
2551 index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
2552 }),
2553 _ => {
2554 if output.len() < 3 {
2555 return Err(DecodeError::OutputTooSmall {
2556 required: 3,
2557 available: output.len(),
2558 });
2559 }
2560 let v2 = decode_byte::<A>(input[2], 2)?;
2561 let v3 = decode_byte::<A>(input[3], 3)?;
2562 output[0] = (v0 << 2) | (v1 >> 4);
2563 output[1] = (v1 << 4) | (v2 >> 2);
2564 output[2] = (v2 << 6) | v3;
2565 Ok(3)
2566 }
2567 }
2568}
2569
2570fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
2571 match input.len() {
2572 0 => Ok(()),
2573 2 => {
2574 decode_byte::<A>(input[0], 0)?;
2575 let v1 = decode_byte::<A>(input[1], 1)?;
2576 if v1 & 0b0000_1111 != 0 {
2577 return Err(DecodeError::InvalidPadding { index: 1 });
2578 }
2579 Ok(())
2580 }
2581 3 => {
2582 decode_byte::<A>(input[0], 0)?;
2583 decode_byte::<A>(input[1], 1)?;
2584 let v2 = decode_byte::<A>(input[2], 2)?;
2585 if v2 & 0b0000_0011 != 0 {
2586 return Err(DecodeError::InvalidPadding { index: 2 });
2587 }
2588 Ok(())
2589 }
2590 _ => Err(DecodeError::InvalidLength),
2591 }
2592}
2593
2594fn decode_tail_unpadded<A: Alphabet>(
2595 input: &[u8],
2596 output: &mut [u8],
2597) -> Result<usize, DecodeError> {
2598 match input.len() {
2599 0 => Ok(0),
2600 2 => {
2601 if output.is_empty() {
2602 return Err(DecodeError::OutputTooSmall {
2603 required: 1,
2604 available: output.len(),
2605 });
2606 }
2607 let v0 = decode_byte::<A>(input[0], 0)?;
2608 let v1 = decode_byte::<A>(input[1], 1)?;
2609 if v1 & 0b0000_1111 != 0 {
2610 return Err(DecodeError::InvalidPadding { index: 1 });
2611 }
2612 output[0] = (v0 << 2) | (v1 >> 4);
2613 Ok(1)
2614 }
2615 3 => {
2616 if output.len() < 2 {
2617 return Err(DecodeError::OutputTooSmall {
2618 required: 2,
2619 available: output.len(),
2620 });
2621 }
2622 let v0 = decode_byte::<A>(input[0], 0)?;
2623 let v1 = decode_byte::<A>(input[1], 1)?;
2624 let v2 = decode_byte::<A>(input[2], 2)?;
2625 if v2 & 0b0000_0011 != 0 {
2626 return Err(DecodeError::InvalidPadding { index: 2 });
2627 }
2628 output[0] = (v0 << 2) | (v1 >> 4);
2629 output[1] = (v1 << 4) | (v2 >> 2);
2630 Ok(2)
2631 }
2632 _ => Err(DecodeError::InvalidLength),
2633 }
2634}
2635
2636fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
2637 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
2638}
2639
2640fn ct_decode_slice<A: Alphabet, const PAD: bool>(
2641 input: &[u8],
2642 output: &mut [u8],
2643) -> Result<usize, DecodeError> {
2644 if input.is_empty() {
2645 return Ok(0);
2646 }
2647
2648 if PAD {
2649 ct_decode_padded::<A>(input, output)
2650 } else {
2651 ct_decode_unpadded::<A>(input, output)
2652 }
2653}
2654
2655fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
2656 buffer: &mut [u8],
2657) -> Result<usize, DecodeError> {
2658 if buffer.is_empty() {
2659 return Ok(0);
2660 }
2661
2662 if PAD {
2663 ct_decode_padded_in_place::<A>(buffer)
2664 } else {
2665 ct_decode_unpadded_in_place::<A>(buffer)
2666 }
2667}
2668
2669fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2670 if !input.len().is_multiple_of(4) {
2671 return Err(DecodeError::InvalidLength);
2672 }
2673
2674 let padding = ct_padding_len(input);
2675 let required = input.len() / 4 * 3 - padding;
2676 if output.len() < required {
2677 return Err(DecodeError::OutputTooSmall {
2678 required,
2679 available: output.len(),
2680 });
2681 }
2682
2683 let mut invalid_byte = 0u8;
2684 let mut invalid_padding = 0u8;
2685 let mut write = 0;
2686 let mut read = 0;
2687
2688 while read < input.len() {
2689 let is_last = read + 4 == input.len();
2690 let b0 = input[read];
2691 let b1 = input[read + 1];
2692 let b2 = input[read + 2];
2693 let b3 = input[read + 3];
2694 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2695 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2696 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2697 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
2698
2699 invalid_byte |= u8::from(!valid0);
2700 invalid_byte |= u8::from(!valid1);
2701
2702 if is_last && padding == 2 {
2703 invalid_padding |= u8::from((v1 & 0b0000_1111) != 0);
2704 output[write] = (v0 << 2) | (v1 >> 4);
2705 write += 1;
2706 } else if is_last && padding == 1 {
2707 invalid_byte |= u8::from(!valid2);
2708 invalid_padding |= u8::from(b2 == b'=');
2709 invalid_padding |= u8::from((v2 & 0b0000_0011) != 0);
2710 output[write] = (v0 << 2) | (v1 >> 4);
2711 output[write + 1] = (v1 << 4) | (v2 >> 2);
2712 write += 2;
2713 } else {
2714 invalid_byte |= u8::from(!valid2);
2715 invalid_byte |= u8::from(!valid3);
2716 invalid_padding |= u8::from(b2 == b'=');
2717 invalid_padding |= u8::from(b3 == b'=');
2718 output[write] = (v0 << 2) | (v1 >> 4);
2719 output[write + 1] = (v1 << 4) | (v2 >> 2);
2720 output[write + 2] = (v2 << 6) | v3;
2721 write += 3;
2722 }
2723
2724 read += 4;
2725 }
2726
2727 report_ct_error(invalid_byte, invalid_padding)?;
2728 Ok(write)
2729}
2730
2731fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
2732 if !buffer.len().is_multiple_of(4) {
2733 return Err(DecodeError::InvalidLength);
2734 }
2735
2736 let padding = ct_padding_len(buffer);
2737 let required = buffer.len() / 4 * 3 - padding;
2738 debug_assert!(required <= buffer.len());
2739
2740 let mut invalid_byte = 0u8;
2741 let mut invalid_padding = 0u8;
2742 let mut write = 0;
2743 let mut read = 0;
2744
2745 while read < buffer.len() {
2746 let is_last = read + 4 == buffer.len();
2747 let b0 = buffer[read];
2748 let b1 = buffer[read + 1];
2749 let b2 = buffer[read + 2];
2750 let b3 = buffer[read + 3];
2751 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2752 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2753 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2754 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
2755
2756 invalid_byte |= u8::from(!valid0);
2757 invalid_byte |= u8::from(!valid1);
2758
2759 if is_last && padding == 2 {
2760 invalid_padding |= u8::from((v1 & 0b0000_1111) != 0);
2761 buffer[write] = (v0 << 2) | (v1 >> 4);
2762 write += 1;
2763 } else if is_last && padding == 1 {
2764 invalid_byte |= u8::from(!valid2);
2765 invalid_padding |= u8::from(b2 == b'=');
2766 invalid_padding |= u8::from((v2 & 0b0000_0011) != 0);
2767 buffer[write] = (v0 << 2) | (v1 >> 4);
2768 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
2769 write += 2;
2770 } else {
2771 invalid_byte |= u8::from(!valid2);
2772 invalid_byte |= u8::from(!valid3);
2773 invalid_padding |= u8::from(b2 == b'=');
2774 invalid_padding |= u8::from(b3 == b'=');
2775 buffer[write] = (v0 << 2) | (v1 >> 4);
2776 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
2777 buffer[write + 2] = (v2 << 6) | v3;
2778 write += 3;
2779 }
2780
2781 read += 4;
2782 }
2783
2784 debug_assert_eq!(write, required);
2785 report_ct_error(invalid_byte, invalid_padding)?;
2786 Ok(write)
2787}
2788
2789fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2790 if input.len() % 4 == 1 {
2791 return Err(DecodeError::InvalidLength);
2792 }
2793
2794 let required = decoded_capacity(input.len());
2795 if output.len() < required {
2796 return Err(DecodeError::OutputTooSmall {
2797 required,
2798 available: output.len(),
2799 });
2800 }
2801
2802 let mut invalid_byte = 0u8;
2803 let mut invalid_padding = 0u8;
2804 let mut write = 0;
2805 let mut read = 0;
2806
2807 while read + 4 <= input.len() {
2808 let b0 = input[read];
2809 let b1 = input[read + 1];
2810 let b2 = input[read + 2];
2811 let b3 = input[read + 3];
2812 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2813 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2814 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2815 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
2816
2817 invalid_byte |= u8::from(!valid0);
2818 invalid_byte |= u8::from(!valid1);
2819 invalid_byte |= u8::from(!valid2);
2820 invalid_byte |= u8::from(!valid3);
2821 invalid_padding |= u8::from(b0 == b'=');
2822 invalid_padding |= u8::from(b1 == b'=');
2823 invalid_padding |= u8::from(b2 == b'=');
2824 invalid_padding |= u8::from(b3 == b'=');
2825
2826 output[write] = (v0 << 2) | (v1 >> 4);
2827 output[write + 1] = (v1 << 4) | (v2 >> 2);
2828 output[write + 2] = (v2 << 6) | v3;
2829 read += 4;
2830 write += 3;
2831 }
2832
2833 match input.len() - read {
2834 0 => {}
2835 2 => {
2836 let b0 = input[read];
2837 let b1 = input[read + 1];
2838 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2839 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2840 invalid_byte |= u8::from(!valid0);
2841 invalid_byte |= u8::from(!valid1);
2842 invalid_padding |= u8::from(b0 == b'=');
2843 invalid_padding |= u8::from(b1 == b'=');
2844 invalid_padding |= u8::from((v1 & 0b0000_1111) != 0);
2845 output[write] = (v0 << 2) | (v1 >> 4);
2846 write += 1;
2847 }
2848 3 => {
2849 let b0 = input[read];
2850 let b1 = input[read + 1];
2851 let b2 = input[read + 2];
2852 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2853 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2854 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2855 invalid_byte |= u8::from(!valid0);
2856 invalid_byte |= u8::from(!valid1);
2857 invalid_byte |= u8::from(!valid2);
2858 invalid_padding |= u8::from(b0 == b'=');
2859 invalid_padding |= u8::from(b1 == b'=');
2860 invalid_padding |= u8::from(b2 == b'=');
2861 invalid_padding |= u8::from((v2 & 0b0000_0011) != 0);
2862 output[write] = (v0 << 2) | (v1 >> 4);
2863 output[write + 1] = (v1 << 4) | (v2 >> 2);
2864 write += 2;
2865 }
2866 _ => return Err(DecodeError::InvalidLength),
2867 }
2868
2869 report_ct_error(invalid_byte, invalid_padding)?;
2870 Ok(write)
2871}
2872
2873fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
2874 if buffer.len() % 4 == 1 {
2875 return Err(DecodeError::InvalidLength);
2876 }
2877
2878 let required = decoded_capacity(buffer.len());
2879 debug_assert!(required <= buffer.len());
2880
2881 let mut invalid_byte = 0u8;
2882 let mut invalid_padding = 0u8;
2883 let mut write = 0;
2884 let mut read = 0;
2885
2886 while read + 4 <= buffer.len() {
2887 let b0 = buffer[read];
2888 let b1 = buffer[read + 1];
2889 let b2 = buffer[read + 2];
2890 let b3 = buffer[read + 3];
2891 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2892 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2893 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2894 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
2895
2896 invalid_byte |= u8::from(!valid0);
2897 invalid_byte |= u8::from(!valid1);
2898 invalid_byte |= u8::from(!valid2);
2899 invalid_byte |= u8::from(!valid3);
2900 invalid_padding |= u8::from(b0 == b'=');
2901 invalid_padding |= u8::from(b1 == b'=');
2902 invalid_padding |= u8::from(b2 == b'=');
2903 invalid_padding |= u8::from(b3 == b'=');
2904
2905 buffer[write] = (v0 << 2) | (v1 >> 4);
2906 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
2907 buffer[write + 2] = (v2 << 6) | v3;
2908 read += 4;
2909 write += 3;
2910 }
2911
2912 match buffer.len() - read {
2913 0 => {}
2914 2 => {
2915 let b0 = buffer[read];
2916 let b1 = buffer[read + 1];
2917 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2918 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2919 invalid_byte |= u8::from(!valid0);
2920 invalid_byte |= u8::from(!valid1);
2921 invalid_padding |= u8::from(b0 == b'=');
2922 invalid_padding |= u8::from(b1 == b'=');
2923 invalid_padding |= u8::from((v1 & 0b0000_1111) != 0);
2924 buffer[write] = (v0 << 2) | (v1 >> 4);
2925 write += 1;
2926 }
2927 3 => {
2928 let b0 = buffer[read];
2929 let b1 = buffer[read + 1];
2930 let b2 = buffer[read + 2];
2931 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2932 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2933 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2934 invalid_byte |= u8::from(!valid0);
2935 invalid_byte |= u8::from(!valid1);
2936 invalid_byte |= u8::from(!valid2);
2937 invalid_padding |= u8::from(b0 == b'=');
2938 invalid_padding |= u8::from(b1 == b'=');
2939 invalid_padding |= u8::from(b2 == b'=');
2940 invalid_padding |= u8::from((v2 & 0b0000_0011) != 0);
2941 buffer[write] = (v0 << 2) | (v1 >> 4);
2942 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
2943 write += 2;
2944 }
2945 _ => return Err(DecodeError::InvalidLength),
2946 }
2947
2948 debug_assert_eq!(write, required);
2949 report_ct_error(invalid_byte, invalid_padding)?;
2950 Ok(write)
2951}
2952
2953#[inline]
2954fn ct_decode_ascii_base64<A: Alphabet>(byte: u8) -> (u8, bool) {
2955 let upper = mask_if(byte.wrapping_sub(b'A') <= b'Z' - b'A');
2956 let lower = mask_if(byte.wrapping_sub(b'a') <= b'z' - b'a');
2957 let digit = mask_if(byte.wrapping_sub(b'0') <= b'9' - b'0');
2958 let value_62 = mask_if(byte == A::ENCODE[62]);
2959 let value_63 = mask_if(byte == A::ENCODE[63]);
2960 let valid = upper | lower | digit | value_62 | value_63;
2961
2962 let decoded = (byte.wrapping_sub(b'A') & upper)
2963 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
2964 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
2965 | (0x3e & value_62)
2966 | (0x3f & value_63);
2967
2968 (decoded, valid != 0)
2969}
2970
2971fn ct_padding_len(input: &[u8]) -> usize {
2972 let last = input[input.len() - 1];
2973 let before_last = input[input.len() - 2];
2974 usize::from(mask_if(last == b'=') & 1) + usize::from(mask_if(before_last == b'=') & 1)
2975}
2976
2977fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
2978 if invalid_padding != 0 {
2979 Err(DecodeError::InvalidPadding { index: 0 })
2980 } else if invalid_byte != 0 {
2981 Err(DecodeError::InvalidByte { index: 0, byte: 0 })
2982 } else {
2983 Ok(())
2984 }
2985}
2986
2987#[cfg(kani)]
2988mod kani_proofs {
2989 use super::{STANDARD, checked_encoded_len, decoded_capacity};
2990
2991 #[kani::proof]
2992 fn checked_encoded_len_is_bounded_for_small_inputs() {
2993 let len = usize::from(kani::any::<u8>());
2994 let padded = kani::any::<bool>();
2995 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
2996
2997 assert!(encoded >= len);
2998 assert!(encoded <= len / 3 * 4 + 4);
2999 }
3000
3001 #[kani::proof]
3002 fn decoded_capacity_is_bounded_for_small_inputs() {
3003 let len = usize::from(kani::any::<u8>());
3004 let capacity = decoded_capacity(len);
3005
3006 assert!(capacity <= len / 4 * 3 + 2);
3007 }
3008
3009 #[kani::proof]
3010 #[kani::unwind(3)]
3011 fn standard_in_place_decode_returns_prefix_within_buffer() {
3012 let mut buffer = kani::any::<[u8; 8]>();
3013 let result = STANDARD.decode_in_place(&mut buffer);
3014
3015 if let Ok(decoded) = result {
3016 assert!(decoded.len() <= 8);
3017 }
3018 }
3019
3020 #[kani::proof]
3021 #[kani::unwind(3)]
3022 fn standard_clear_tail_decode_clears_buffer_on_error() {
3023 let mut buffer = kani::any::<[u8; 4]>();
3024 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
3025
3026 if result.is_err() {
3027 assert!(buffer.iter().all(|byte| *byte == 0));
3028 }
3029 }
3030}
3031
3032#[cfg(test)]
3033mod tests {
3034 use super::*;
3035
3036 fn fill_pattern(output: &mut [u8], seed: usize) {
3037 for (index, byte) in output.iter_mut().enumerate() {
3038 let value = (index * 73 + seed * 19) % 256;
3039 *byte = u8::try_from(value).unwrap();
3040 }
3041 }
3042
3043 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
3044 where
3045 A: Alphabet,
3046 {
3047 let engine = Engine::<A, PAD>::new();
3048 let mut dispatched = [0x55; 256];
3049 let mut scalar = [0xaa; 256];
3050
3051 let dispatched_result = engine.encode_slice(input, &mut dispatched);
3052 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
3053
3054 assert_eq!(dispatched_result, scalar_result);
3055 if let Ok(written) = dispatched_result {
3056 assert_eq!(&dispatched[..written], &scalar[..written]);
3057 }
3058
3059 let required = checked_encoded_len(input.len(), PAD).unwrap();
3060 if required > 0 {
3061 let mut dispatched_short = [0x55; 256];
3062 let mut scalar_short = [0xaa; 256];
3063 let available = required - 1;
3064
3065 assert_eq!(
3066 engine.encode_slice(input, &mut dispatched_short[..available]),
3067 backend::scalar_reference_encode_slice::<A, PAD>(
3068 input,
3069 &mut scalar_short[..available],
3070 )
3071 );
3072 }
3073 }
3074
3075 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
3076 where
3077 A: Alphabet,
3078 {
3079 let engine = Engine::<A, PAD>::new();
3080 let mut dispatched = [0x55; 128];
3081 let mut scalar = [0xaa; 128];
3082
3083 let dispatched_result = engine.decode_slice(input, &mut dispatched);
3084 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
3085
3086 assert_eq!(dispatched_result, scalar_result);
3087 if let Ok(written) = dispatched_result {
3088 assert_eq!(&dispatched[..written], &scalar[..written]);
3089
3090 if written > 0 {
3091 let mut dispatched_short = [0x55; 128];
3092 let mut scalar_short = [0xaa; 128];
3093 let available = written - 1;
3094
3095 assert_eq!(
3096 engine.decode_slice(input, &mut dispatched_short[..available]),
3097 backend::scalar_reference_decode_slice::<A, PAD>(
3098 input,
3099 &mut scalar_short[..available],
3100 )
3101 );
3102 }
3103 }
3104 }
3105
3106 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
3107 where
3108 A: Alphabet,
3109 {
3110 assert_encode_backend_matches_scalar::<A, PAD>(input);
3111
3112 let mut encoded = [0; 256];
3113 let encoded_len =
3114 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
3115 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
3116 }
3117
3118 #[test]
3119 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
3120 let mut input = [0; 128];
3121
3122 for input_len in 0..=input.len() {
3123 fill_pattern(&mut input[..input_len], input_len);
3124 let input = &input[..input_len];
3125
3126 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
3127 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
3128 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
3129 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
3130 }
3131 }
3132
3133 #[test]
3134 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
3135 for input in [
3136 &b"Z"[..],
3137 b"====",
3138 b"AA=A",
3139 b"Zh==",
3140 b"Zm9=",
3141 b"Zm9v$g==",
3142 b"Zm9vZh==",
3143 ] {
3144 assert_decode_backend_matches_scalar::<Standard, true>(input);
3145 }
3146
3147 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
3148 assert_decode_backend_matches_scalar::<Standard, false>(input);
3149 }
3150
3151 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
3152 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
3153 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
3154 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
3155 }
3156
3157 #[cfg(feature = "simd")]
3158 #[test]
3159 fn simd_dispatch_scaffold_keeps_scalar_active() {
3160 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
3161 let _candidate = simd::detected_candidate();
3162 }
3163
3164 #[test]
3165 fn encodes_standard_vectors() {
3166 let vectors = [
3167 (&b""[..], &b""[..]),
3168 (&b"f"[..], &b"Zg=="[..]),
3169 (&b"fo"[..], &b"Zm8="[..]),
3170 (&b"foo"[..], &b"Zm9v"[..]),
3171 (&b"foob"[..], &b"Zm9vYg=="[..]),
3172 (&b"fooba"[..], &b"Zm9vYmE="[..]),
3173 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
3174 ];
3175 for (input, expected) in vectors {
3176 let mut output = [0u8; 16];
3177 let written = STANDARD.encode_slice(input, &mut output).unwrap();
3178 assert_eq!(&output[..written], expected);
3179 }
3180 }
3181
3182 #[test]
3183 fn decodes_standard_vectors() {
3184 let vectors = [
3185 (&b""[..], &b""[..]),
3186 (&b"Zg=="[..], &b"f"[..]),
3187 (&b"Zm8="[..], &b"fo"[..]),
3188 (&b"Zm9v"[..], &b"foo"[..]),
3189 (&b"Zm9vYg=="[..], &b"foob"[..]),
3190 (&b"Zm9vYmE="[..], &b"fooba"[..]),
3191 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
3192 ];
3193 for (input, expected) in vectors {
3194 let mut output = [0u8; 16];
3195 let written = STANDARD.decode_slice(input, &mut output).unwrap();
3196 assert_eq!(&output[..written], expected);
3197 }
3198 }
3199
3200 #[test]
3201 fn supports_unpadded_url_safe() {
3202 let mut encoded = [0u8; 16];
3203 let written = URL_SAFE_NO_PAD
3204 .encode_slice(b"\xfb\xff", &mut encoded)
3205 .unwrap();
3206 assert_eq!(&encoded[..written], b"-_8");
3207
3208 let mut decoded = [0u8; 2];
3209 let written = URL_SAFE_NO_PAD
3210 .decode_slice(&encoded[..written], &mut decoded)
3211 .unwrap();
3212 assert_eq!(&decoded[..written], b"\xfb\xff");
3213 }
3214
3215 #[test]
3216 fn decodes_in_place() {
3217 let mut buffer = *b"Zm9vYmFy";
3218 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
3219 assert_eq!(decoded, b"foobar");
3220 }
3221
3222 #[test]
3223 fn rejects_non_canonical_padding_bits() {
3224 let mut output = [0u8; 4];
3225 assert_eq!(
3226 STANDARD.decode_slice(b"Zh==", &mut output),
3227 Err(DecodeError::InvalidPadding { index: 1 })
3228 );
3229 assert_eq!(
3230 STANDARD.decode_slice(b"Zm9=", &mut output),
3231 Err(DecodeError::InvalidPadding { index: 2 })
3232 );
3233 }
3234}