Skip to main content

base64_ng/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4#![deny(clippy::all)]
5#![deny(clippy::pedantic)]
6#![allow(clippy::missing_errors_doc)]
7
8//! `base64-ng` is a `no_std`-first Base64 encoder and decoder.
9//!
10//! This initial release provides strict scalar RFC 4648-style behavior and
11//! caller-owned output buffers. Future SIMD fast paths will be required to
12//! match this scalar module byte-for-byte.
13//!
14//! # Examples
15//!
16//! Encode and decode with caller-owned buffers:
17//!
18//! ```
19//! use base64_ng::{STANDARD, checked_encoded_len};
20//!
21//! let input = b"hello";
22//! const ENCODED_CAPACITY: usize = match checked_encoded_len(5, true) {
23//!     Some(len) => len,
24//!     None => panic!("encoded length overflow"),
25//! };
26//! let mut encoded = [0u8; ENCODED_CAPACITY];
27//! let encoded_len = STANDARD.encode_slice(input, &mut encoded).unwrap();
28//! assert_eq!(&encoded[..encoded_len], b"aGVsbG8=");
29//!
30//! let mut decoded = [0u8; 5];
31//! let decoded_len = STANDARD.decode_slice(&encoded, &mut decoded).unwrap();
32//! assert_eq!(&decoded[..decoded_len], input);
33//! ```
34//!
35//! Use the URL-safe no-padding engine:
36//!
37//! ```
38//! use base64_ng::URL_SAFE_NO_PAD;
39//!
40//! let mut encoded = [0u8; 3];
41//! let encoded_len = URL_SAFE_NO_PAD.encode_slice(b"\xfb\xff", &mut encoded).unwrap();
42//! assert_eq!(&encoded[..encoded_len], b"-_8");
43//! ```
44
45#[cfg(feature = "alloc")]
46extern crate alloc;
47
48#[cfg(feature = "stream")]
49pub mod stream {
50    //! Streaming Base64 wrappers for `std::io`.
51    //!
52    //! ```
53    //! use std::io::{Read, Write};
54    //! use base64_ng::{STANDARD, stream::{Decoder, DecoderReader, Encoder, EncoderReader}};
55    //!
56    //! let mut encoder = Encoder::new(Vec::new(), STANDARD);
57    //! encoder.write_all(b"he").unwrap();
58    //! encoder.write_all(b"llo").unwrap();
59    //! let encoded = encoder.finish().unwrap();
60    //! assert_eq!(encoded, b"aGVsbG8=");
61    //!
62    //! let mut reader = EncoderReader::new(&b"hello"[..], STANDARD);
63    //! let mut encoded = String::new();
64    //! reader.read_to_string(&mut encoded).unwrap();
65    //! assert_eq!(encoded, "aGVsbG8=");
66    //!
67    //! let mut decoder = Decoder::new(Vec::new(), STANDARD);
68    //! decoder.write_all(b"aGVs").unwrap();
69    //! decoder.write_all(b"bG8=").unwrap();
70    //! let decoded = decoder.finish().unwrap();
71    //! assert_eq!(decoded, b"hello");
72    //!
73    //! let mut reader = DecoderReader::new(&b"aGVsbG8="[..], STANDARD);
74    //! let mut decoded = Vec::new();
75    //! reader.read_to_end(&mut decoded).unwrap();
76    //! assert_eq!(decoded, b"hello");
77    //! ```
78
79    use super::{Alphabet, DecodeError, EncodeError, Engine};
80    use std::collections::VecDeque;
81    use std::io::{self, Read, Write};
82
83    /// A streaming Base64 encoder for `std::io::Write`.
84    pub struct Encoder<W, A, const PAD: bool>
85    where
86        A: Alphabet,
87    {
88        inner: W,
89        engine: Engine<A, PAD>,
90        pending: [u8; 2],
91        pending_len: usize,
92    }
93
94    impl<W, A, const PAD: bool> Encoder<W, A, PAD>
95    where
96        A: Alphabet,
97    {
98        /// Creates a new streaming encoder.
99        #[must_use]
100        pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
101            Self {
102                inner,
103                engine,
104                pending: [0; 2],
105                pending_len: 0,
106            }
107        }
108
109        /// Returns a shared reference to the wrapped writer.
110        #[must_use]
111        pub const fn get_ref(&self) -> &W {
112            &self.inner
113        }
114
115        /// Returns a mutable reference to the wrapped writer.
116        pub fn get_mut(&mut self) -> &mut W {
117            &mut self.inner
118        }
119
120        /// Consumes the encoder without flushing pending input.
121        ///
122        /// Prefer [`Self::finish`] when the encoded output must be complete.
123        #[must_use]
124        pub fn into_inner(self) -> W {
125            self.inner
126        }
127    }
128
129    impl<W, A, const PAD: bool> Encoder<W, A, PAD>
130    where
131        W: Write,
132        A: Alphabet,
133    {
134        /// Writes any pending input, flushes the wrapped writer, and returns it.
135        pub fn finish(mut self) -> io::Result<W> {
136            self.write_pending_final()?;
137            self.inner.flush()?;
138            Ok(self.inner)
139        }
140
141        fn write_pending_final(&mut self) -> io::Result<()> {
142            if self.pending_len == 0 {
143                return Ok(());
144            }
145
146            let mut encoded = [0u8; 4];
147            let written = self
148                .engine
149                .encode_slice(&self.pending[..self.pending_len], &mut encoded)
150                .map_err(encode_error_to_io)?;
151            self.inner.write_all(&encoded[..written])?;
152            self.pending_len = 0;
153            Ok(())
154        }
155    }
156
157    impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
158    where
159        W: Write,
160        A: Alphabet,
161    {
162        fn write(&mut self, input: &[u8]) -> io::Result<usize> {
163            if input.is_empty() {
164                return Ok(0);
165            }
166
167            let mut consumed = 0;
168            if self.pending_len > 0 {
169                let needed = 3 - self.pending_len;
170                if input.len() < needed {
171                    self.pending[self.pending_len..self.pending_len + input.len()]
172                        .copy_from_slice(input);
173                    self.pending_len += input.len();
174                    return Ok(input.len());
175                }
176
177                let mut chunk = [0u8; 3];
178                chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
179                chunk[self.pending_len..].copy_from_slice(&input[..needed]);
180
181                let mut encoded = [0u8; 4];
182                let written = self
183                    .engine
184                    .encode_slice(&chunk, &mut encoded)
185                    .map_err(encode_error_to_io)?;
186                self.inner.write_all(&encoded[..written])?;
187                self.pending_len = 0;
188                consumed += needed;
189            }
190
191            let remaining = &input[consumed..];
192            let full_len = remaining.len() / 3 * 3;
193            let mut offset = 0;
194            let mut encoded = [0u8; 1024];
195            while offset < full_len {
196                let mut take = core::cmp::min(full_len - offset, 768);
197                take -= take % 3;
198                debug_assert!(take > 0);
199
200                let written = self
201                    .engine
202                    .encode_slice(&remaining[offset..offset + take], &mut encoded)
203                    .map_err(encode_error_to_io)?;
204                self.inner.write_all(&encoded[..written])?;
205                offset += take;
206            }
207
208            let tail = &remaining[full_len..];
209            self.pending[..tail.len()].copy_from_slice(tail);
210            self.pending_len = tail.len();
211
212            Ok(input.len())
213        }
214
215        fn flush(&mut self) -> io::Result<()> {
216            self.inner.flush()
217        }
218    }
219
220    fn encode_error_to_io(err: EncodeError) -> io::Error {
221        io::Error::new(io::ErrorKind::InvalidInput, err)
222    }
223
224    /// A streaming Base64 decoder for `std::io::Write`.
225    pub struct Decoder<W, A, const PAD: bool>
226    where
227        A: Alphabet,
228    {
229        inner: W,
230        engine: Engine<A, PAD>,
231        pending: [u8; 4],
232        pending_len: usize,
233        finished: bool,
234    }
235
236    impl<W, A, const PAD: bool> Decoder<W, A, PAD>
237    where
238        A: Alphabet,
239    {
240        /// Creates a new streaming decoder.
241        #[must_use]
242        pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
243            Self {
244                inner,
245                engine,
246                pending: [0; 4],
247                pending_len: 0,
248                finished: false,
249            }
250        }
251
252        /// Returns a shared reference to the wrapped writer.
253        #[must_use]
254        pub const fn get_ref(&self) -> &W {
255            &self.inner
256        }
257
258        /// Returns a mutable reference to the wrapped writer.
259        pub fn get_mut(&mut self) -> &mut W {
260            &mut self.inner
261        }
262
263        /// Consumes the decoder without flushing pending input.
264        ///
265        /// Prefer [`Self::finish`] when the decoded output must be complete.
266        #[must_use]
267        pub fn into_inner(self) -> W {
268            self.inner
269        }
270    }
271
272    impl<W, A, const PAD: bool> Decoder<W, A, PAD>
273    where
274        W: Write,
275        A: Alphabet,
276    {
277        /// Validates final pending input, flushes the wrapped writer, and returns it.
278        pub fn finish(mut self) -> io::Result<W> {
279            self.write_pending_final()?;
280            self.inner.flush()?;
281            Ok(self.inner)
282        }
283
284        fn write_pending_final(&mut self) -> io::Result<()> {
285            if self.pending_len == 0 {
286                return Ok(());
287            }
288
289            let mut decoded = [0u8; 3];
290            let written = self
291                .engine
292                .decode_slice(&self.pending[..self.pending_len], &mut decoded)
293                .map_err(decode_error_to_io)?;
294            self.inner.write_all(&decoded[..written])?;
295            self.pending_len = 0;
296            Ok(())
297        }
298
299        fn write_full_quad(&mut self, input: [u8; 4]) -> io::Result<()> {
300            let mut decoded = [0u8; 3];
301            let written = self
302                .engine
303                .decode_slice(&input, &mut decoded)
304                .map_err(decode_error_to_io)?;
305            self.inner.write_all(&decoded[..written])?;
306            if written < 3 {
307                self.finished = true;
308            }
309            Ok(())
310        }
311    }
312
313    impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
314    where
315        W: Write,
316        A: Alphabet,
317    {
318        fn write(&mut self, input: &[u8]) -> io::Result<usize> {
319            if input.is_empty() {
320                return Ok(0);
321            }
322            if self.finished {
323                return Err(trailing_input_after_padding_error());
324            }
325
326            let mut consumed = 0;
327            if self.pending_len > 0 {
328                let needed = 4 - self.pending_len;
329                if input.len() < needed {
330                    self.pending[self.pending_len..self.pending_len + input.len()]
331                        .copy_from_slice(input);
332                    self.pending_len += input.len();
333                    return Ok(input.len());
334                }
335
336                let mut quad = [0u8; 4];
337                quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
338                quad[self.pending_len..].copy_from_slice(&input[..needed]);
339                self.write_full_quad(quad)?;
340                self.pending_len = 0;
341                consumed += needed;
342                if self.finished && consumed < input.len() {
343                    return Err(trailing_input_after_padding_error());
344                }
345            }
346
347            let remaining = &input[consumed..];
348            let full_len = remaining.len() / 4 * 4;
349            let mut offset = 0;
350            while offset < full_len {
351                let quad = [
352                    remaining[offset],
353                    remaining[offset + 1],
354                    remaining[offset + 2],
355                    remaining[offset + 3],
356                ];
357                self.write_full_quad(quad)?;
358                offset += 4;
359                if self.finished && offset < remaining.len() {
360                    return Err(trailing_input_after_padding_error());
361                }
362            }
363
364            let tail = &remaining[full_len..];
365            self.pending[..tail.len()].copy_from_slice(tail);
366            self.pending_len = tail.len();
367
368            Ok(input.len())
369        }
370
371        fn flush(&mut self) -> io::Result<()> {
372            self.inner.flush()
373        }
374    }
375
376    fn decode_error_to_io(err: DecodeError) -> io::Error {
377        io::Error::new(io::ErrorKind::InvalidInput, err)
378    }
379
380    fn trailing_input_after_padding_error() -> io::Error {
381        io::Error::new(
382            io::ErrorKind::InvalidInput,
383            "base64 decoder received trailing input after padding",
384        )
385    }
386
387    /// A streaming Base64 decoder for `std::io::Read`.
388    ///
389    /// For padded engines, this reader stops at the terminal padded Base64
390    /// block and leaves later bytes unread in the wrapped reader. This preserves
391    /// boundaries for callers that decode one Base64 payload from a larger
392    /// stream.
393    pub struct DecoderReader<R, A, const PAD: bool>
394    where
395        A: Alphabet,
396    {
397        inner: R,
398        engine: Engine<A, PAD>,
399        pending: [u8; 4],
400        pending_len: usize,
401        output: VecDeque<u8>,
402        finished: bool,
403        terminal_seen: bool,
404    }
405
406    impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
407    where
408        A: Alphabet,
409    {
410        /// Creates a new streaming decoder reader.
411        #[must_use]
412        pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
413            Self {
414                inner,
415                engine,
416                pending: [0; 4],
417                pending_len: 0,
418                output: VecDeque::new(),
419                finished: false,
420                terminal_seen: false,
421            }
422        }
423
424        /// Returns a shared reference to the wrapped reader.
425        #[must_use]
426        pub const fn get_ref(&self) -> &R {
427            &self.inner
428        }
429
430        /// Returns a mutable reference to the wrapped reader.
431        pub fn get_mut(&mut self) -> &mut R {
432            &mut self.inner
433        }
434
435        /// Consumes the decoder reader and returns the wrapped reader.
436        #[must_use]
437        pub fn into_inner(self) -> R {
438            self.inner
439        }
440    }
441
442    impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
443    where
444        R: Read,
445        A: Alphabet,
446    {
447        fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
448            if output.is_empty() {
449                return Ok(0);
450            }
451
452            while self.output.is_empty() && !self.finished {
453                self.fill_output()?;
454            }
455
456            let mut written = 0;
457            while written < output.len() {
458                let Some(byte) = self.output.pop_front() else {
459                    break;
460                };
461                output[written] = byte;
462                written += 1;
463            }
464
465            Ok(written)
466        }
467    }
468
469    impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
470    where
471        R: Read,
472        A: Alphabet,
473    {
474        fn fill_output(&mut self) -> io::Result<()> {
475            if self.terminal_seen {
476                self.finished = true;
477                return Ok(());
478            }
479
480            let mut input = [0u8; 4];
481            let read = self.inner.read(&mut input[..4 - self.pending_len])?;
482            if read == 0 {
483                self.finished = true;
484                self.push_final_pending()?;
485                return Ok(());
486            }
487
488            self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
489            self.pending_len += read;
490            if self.pending_len < 4 {
491                return Ok(());
492            }
493
494            let quad = self.pending;
495            self.pending_len = 0;
496            self.push_decoded(&quad)?;
497            if self.terminal_seen {
498                self.finished = true;
499            }
500            Ok(())
501        }
502
503        fn push_final_pending(&mut self) -> io::Result<()> {
504            if self.pending_len == 0 {
505                return Ok(());
506            }
507
508            let mut pending = [0u8; 4];
509            pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
510            let pending_len = self.pending_len;
511            self.pending_len = 0;
512            self.push_decoded(&pending[..pending_len])
513        }
514
515        fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
516            let mut decoded = [0u8; 3];
517            let written = self
518                .engine
519                .decode_slice(input, &mut decoded)
520                .map_err(decode_error_to_io)?;
521            self.output.extend(&decoded[..written]);
522            if input.len() == 4 && written < 3 {
523                self.terminal_seen = true;
524            }
525            Ok(())
526        }
527    }
528
529    /// A streaming Base64 encoder for `std::io::Read`.
530    pub struct EncoderReader<R, A, const PAD: bool>
531    where
532        A: Alphabet,
533    {
534        inner: R,
535        engine: Engine<A, PAD>,
536        pending: [u8; 2],
537        pending_len: usize,
538        output: VecDeque<u8>,
539        finished: bool,
540    }
541
542    impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
543    where
544        A: Alphabet,
545    {
546        /// Creates a new streaming encoder reader.
547        #[must_use]
548        pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
549            Self {
550                inner,
551                engine,
552                pending: [0; 2],
553                pending_len: 0,
554                output: VecDeque::new(),
555                finished: false,
556            }
557        }
558
559        /// Returns a shared reference to the wrapped reader.
560        #[must_use]
561        pub const fn get_ref(&self) -> &R {
562            &self.inner
563        }
564
565        /// Returns a mutable reference to the wrapped reader.
566        pub fn get_mut(&mut self) -> &mut R {
567            &mut self.inner
568        }
569
570        /// Consumes the encoder reader and returns the wrapped reader.
571        #[must_use]
572        pub fn into_inner(self) -> R {
573            self.inner
574        }
575    }
576
577    impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
578    where
579        R: Read,
580        A: Alphabet,
581    {
582        fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
583            if output.is_empty() {
584                return Ok(0);
585            }
586
587            while self.output.is_empty() && !self.finished {
588                self.fill_output()?;
589            }
590
591            let mut written = 0;
592            while written < output.len() {
593                let Some(byte) = self.output.pop_front() else {
594                    break;
595                };
596                output[written] = byte;
597                written += 1;
598            }
599
600            Ok(written)
601        }
602    }
603
604    impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
605    where
606        R: Read,
607        A: Alphabet,
608    {
609        fn fill_output(&mut self) -> io::Result<()> {
610            let mut input = [0u8; 768];
611            let read = self.inner.read(&mut input)?;
612            if read == 0 {
613                self.finished = true;
614                self.push_final_pending()?;
615                return Ok(());
616            }
617
618            let mut consumed = 0;
619            if self.pending_len > 0 {
620                let needed = 3 - self.pending_len;
621                if read < needed {
622                    self.pending[self.pending_len..self.pending_len + read]
623                        .copy_from_slice(&input[..read]);
624                    self.pending_len += read;
625                    return Ok(());
626                }
627
628                let mut chunk = [0u8; 3];
629                chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
630                chunk[self.pending_len..].copy_from_slice(&input[..needed]);
631                self.push_encoded(&chunk)?;
632                self.pending_len = 0;
633                consumed += needed;
634            }
635
636            let remaining = &input[consumed..read];
637            let full_len = remaining.len() / 3 * 3;
638            if full_len > 0 {
639                self.push_encoded(&remaining[..full_len])?;
640            }
641
642            let tail = &remaining[full_len..];
643            self.pending[..tail.len()].copy_from_slice(tail);
644            self.pending_len = tail.len();
645            Ok(())
646        }
647
648        fn push_final_pending(&mut self) -> io::Result<()> {
649            if self.pending_len == 0 {
650                return Ok(());
651            }
652
653            let mut pending = [0u8; 2];
654            pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
655            let pending_len = self.pending_len;
656            self.pending_len = 0;
657            self.push_encoded(&pending[..pending_len])
658        }
659
660        fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
661            let mut encoded = [0u8; 1024];
662            let written = self
663                .engine
664                .encode_slice(input, &mut encoded)
665                .map_err(encode_error_to_io)?;
666            self.output.extend(&encoded[..written]);
667            Ok(())
668        }
669    }
670}
671
672/// Constant-time-oriented scalar decoding APIs.
673///
674/// This module is separate from the default decoder so callers can opt into a
675/// slower path with a narrower timing target. It avoids lookup tables indexed
676/// by secret input bytes while mapping Base64 symbols, but it is not documented
677/// as a formally verified cryptographic constant-time API.
678pub mod ct {
679    use super::{Alphabet, DecodeError, Standard, UrlSafe, ct_decode_slice};
680    use core::marker::PhantomData;
681
682    /// Standard Base64 constant-time-oriented decoder with padding.
683    pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
684
685    /// Standard Base64 constant-time-oriented decoder without padding.
686    pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
687
688    /// URL-safe Base64 constant-time-oriented decoder with padding.
689    pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
690
691    /// URL-safe Base64 constant-time-oriented decoder without padding.
692    pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
693
694    /// A zero-sized constant-time-oriented Base64 decoder.
695    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
696    pub struct CtEngine<A, const PAD: bool> {
697        alphabet: PhantomData<A>,
698    }
699
700    impl<A, const PAD: bool> CtEngine<A, PAD>
701    where
702        A: Alphabet,
703    {
704        /// Creates a new constant-time-oriented decoder engine.
705        #[must_use]
706        pub const fn new() -> Self {
707            Self {
708                alphabet: PhantomData,
709            }
710        }
711
712        /// Decodes `input` into `output`, returning the number of bytes
713        /// written.
714        ///
715        /// This path uses branch-minimized arithmetic for Base64 symbol
716        /// mapping and avoids secret-indexed lookup tables. Input length,
717        /// padding length, output length, and final success or failure remain
718        /// public. Malformed input errors are intentionally non-localized; use
719        /// the normal strict decoder when exact error indexes are required.
720        ///
721        /// # Examples
722        ///
723        /// ```
724        /// use base64_ng::ct;
725        ///
726        /// let mut output = [0u8; 5];
727        /// let written = ct::STANDARD
728        ///     .decode_slice(b"aGVsbG8=", &mut output)
729        ///     .unwrap();
730        ///
731        /// assert_eq!(&output[..written], b"hello");
732        /// ```
733        pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
734            ct_decode_slice::<A, PAD>(input, output)
735        }
736    }
737}
738
739/// Standard Base64 engine with padding.
740pub const STANDARD: Engine<Standard, true> = Engine::new();
741
742/// Standard Base64 engine without padding.
743pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
744
745/// URL-safe Base64 engine with padding.
746pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
747
748/// URL-safe Base64 engine without padding.
749pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
750
751/// Returns the encoded length for an input length and padding policy.
752///
753/// This function returns [`EncodeError::LengthOverflow`] instead of panicking.
754/// Use [`checked_encoded_len`] when an `Option<usize>` is more convenient.
755///
756/// # Examples
757///
758/// ```
759/// use base64_ng::encoded_len;
760///
761/// assert_eq!(encoded_len(5, true).unwrap(), 8);
762/// assert_eq!(encoded_len(5, false).unwrap(), 7);
763/// assert!(encoded_len(usize::MAX, true).is_err());
764/// ```
765pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
766    match checked_encoded_len(input_len, padded) {
767        Some(len) => Ok(len),
768        None => Err(EncodeError::LengthOverflow),
769    }
770}
771
772/// Returns the encoded length, or `None` if it would overflow `usize`.
773///
774/// # Examples
775///
776/// ```
777/// use base64_ng::checked_encoded_len;
778///
779/// assert_eq!(checked_encoded_len(5, true), Some(8));
780/// assert_eq!(checked_encoded_len(usize::MAX, true), None);
781/// ```
782#[must_use]
783pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
784    let groups = input_len / 3;
785    if groups > usize::MAX / 4 {
786        return None;
787    }
788    let full = groups * 4;
789    let rem = input_len % 3;
790    if rem == 0 {
791        Some(full)
792    } else if padded {
793        full.checked_add(4)
794    } else {
795        full.checked_add(rem + 1)
796    }
797}
798
799/// Returns the maximum decoded length for an encoded input length.
800///
801/// # Examples
802///
803/// ```
804/// use base64_ng::decoded_capacity;
805///
806/// assert_eq!(decoded_capacity(8), 6);
807/// assert_eq!(decoded_capacity(7), 5);
808/// ```
809#[must_use]
810pub const fn decoded_capacity(encoded_len: usize) -> usize {
811    let rem = encoded_len % 4;
812    encoded_len / 4 * 3
813        + if rem == 2 {
814            1
815        } else if rem == 3 {
816            2
817        } else {
818            0
819        }
820}
821
822/// Returns the exact decoded length implied by input length and padding.
823///
824/// This validates padding placement and impossible lengths, but it does not
825/// validate alphabet membership or non-canonical trailing bits.
826///
827/// # Examples
828///
829/// ```
830/// use base64_ng::decoded_len;
831///
832/// assert_eq!(decoded_len(b"aGVsbG8=", true).unwrap(), 5);
833/// assert_eq!(decoded_len(b"aGVsbG8", false).unwrap(), 5);
834/// ```
835pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
836    if padded {
837        decoded_len_padded(input)
838    } else {
839        decoded_len_unpadded(input)
840    }
841}
842
843/// A Base64 alphabet.
844pub trait Alphabet {
845    /// Encoding table indexed by 6-bit values.
846    const ENCODE: [u8; 64];
847
848    /// Decode one byte into a 6-bit value.
849    fn decode(byte: u8) -> Option<u8>;
850}
851
852/// The RFC 4648 standard Base64 alphabet.
853#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
854pub struct Standard;
855
856impl Alphabet for Standard {
857    const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
858
859    #[inline]
860    fn decode(byte: u8) -> Option<u8> {
861        decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
862    }
863}
864
865/// The RFC 4648 URL-safe Base64 alphabet.
866#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
867pub struct UrlSafe;
868
869impl Alphabet for UrlSafe {
870    const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
871
872    #[inline]
873    fn decode(byte: u8) -> Option<u8> {
874        decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
875    }
876}
877
878#[inline]
879const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
880    encode_ascii_base64(value, A::ENCODE[62], A::ENCODE[63])
881}
882
883#[inline]
884const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
885    let upper = mask_if(value < 26);
886    let lower = mask_if(value.wrapping_sub(26) < 26);
887    let digit = mask_if(value.wrapping_sub(52) < 10);
888    let value_62 = mask_if(value == 0x3e);
889    let value_63 = mask_if(value == 0x3f);
890
891    (value.wrapping_add(b'A') & upper)
892        | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
893        | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
894        | (value_62_byte & value_62)
895        | (value_63_byte & value_63)
896}
897
898#[inline]
899fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
900    let upper = mask_if(byte.wrapping_sub(b'A') <= b'Z' - b'A');
901    let lower = mask_if(byte.wrapping_sub(b'a') <= b'z' - b'a');
902    let digit = mask_if(byte.wrapping_sub(b'0') <= b'9' - b'0');
903    let value_62 = mask_if(byte == value_62_byte);
904    let value_63 = mask_if(byte == value_63_byte);
905    let valid = upper | lower | digit | value_62 | value_63;
906
907    let decoded = (byte.wrapping_sub(b'A') & upper)
908        | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
909        | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
910        | (0x3e & value_62)
911        | (0x3f & value_63);
912
913    if valid == 0 { None } else { Some(decoded) }
914}
915
916#[inline]
917const fn mask_if(condition: bool) -> u8 {
918    0u8.wrapping_sub(condition as u8)
919}
920
921/// A zero-sized Base64 engine parameterized by alphabet and padding policy.
922#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
923pub struct Engine<A, const PAD: bool> {
924    alphabet: core::marker::PhantomData<A>,
925}
926
927impl<A, const PAD: bool> Engine<A, PAD>
928where
929    A: Alphabet,
930{
931    /// Creates a new engine value.
932    #[must_use]
933    pub const fn new() -> Self {
934        Self {
935            alphabet: core::marker::PhantomData,
936        }
937    }
938
939    /// Returns the encoded length for this engine's padding policy.
940    pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
941        encoded_len(input_len, PAD)
942    }
943
944    /// Returns the encoded length for this engine, or `None` on overflow.
945    #[must_use]
946    pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
947        checked_encoded_len(input_len, PAD)
948    }
949
950    /// Returns the exact decoded length implied by input length and padding.
951    ///
952    /// This validates padding placement and impossible lengths, but it does not
953    /// validate alphabet membership or non-canonical trailing bits.
954    pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
955        decoded_len(input, PAD)
956    }
957
958    /// Returns the exact decoded length for the explicit legacy profile.
959    ///
960    /// The legacy profile ignores ASCII space, tab, carriage return, and line
961    /// feed bytes before applying the same alphabet, padding, and canonical-bit
962    /// checks as strict decoding.
963    pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
964        validate_legacy_decode::<A, PAD>(input)
965    }
966
967    /// Encodes a fixed-size input into a fixed-size output array in const contexts.
968    ///
969    /// Stable Rust does not yet allow this API to return an array whose length
970    /// is computed from `INPUT_LEN` directly. Instead, the caller supplies the
971    /// output length through the destination type and this function panics
972    /// during const evaluation if the length is wrong.
973    ///
974    /// # Panics
975    ///
976    /// Panics if `OUTPUT_LEN` is not exactly the encoded length for `INPUT_LEN`
977    /// and this engine's padding policy, or if that length overflows `usize`.
978    ///
979    /// # Examples
980    ///
981    /// ```
982    /// use base64_ng::{STANDARD, URL_SAFE_NO_PAD};
983    ///
984    /// const HELLO: [u8; 8] = STANDARD.encode_array(b"hello");
985    /// const URL_SAFE: [u8; 3] = URL_SAFE_NO_PAD.encode_array(b"\xfb\xff");
986    ///
987    /// assert_eq!(&HELLO, b"aGVsbG8=");
988    /// assert_eq!(&URL_SAFE, b"-_8");
989    /// ```
990    ///
991    /// Incorrect output lengths fail during const evaluation:
992    ///
993    /// ```compile_fail
994    /// use base64_ng::STANDARD;
995    ///
996    /// const TOO_SHORT: [u8; 7] = STANDARD.encode_array(b"hello");
997    /// ```
998    #[must_use]
999    pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
1000        &self,
1001        input: &[u8; INPUT_LEN],
1002    ) -> [u8; OUTPUT_LEN] {
1003        let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
1004            panic!("encoded base64 length overflows usize");
1005        };
1006        assert!(
1007            required == OUTPUT_LEN,
1008            "base64 output array has incorrect length"
1009        );
1010
1011        let mut output = [0u8; OUTPUT_LEN];
1012        let mut read = 0;
1013        let mut write = 0;
1014        while INPUT_LEN - read >= 3 {
1015            let b0 = input[read];
1016            let b1 = input[read + 1];
1017            let b2 = input[read + 2];
1018
1019            output[write] = encode_base64_value::<A>(b0 >> 2);
1020            output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1021            output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1022            output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1023
1024            read += 3;
1025            write += 4;
1026        }
1027
1028        match INPUT_LEN - read {
1029            0 => {}
1030            1 => {
1031                let b0 = input[read];
1032                output[write] = encode_base64_value::<A>(b0 >> 2);
1033                output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1034                write += 2;
1035                if PAD {
1036                    output[write] = b'=';
1037                    output[write + 1] = b'=';
1038                }
1039            }
1040            2 => {
1041                let b0 = input[read];
1042                let b1 = input[read + 1];
1043                output[write] = encode_base64_value::<A>(b0 >> 2);
1044                output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1045                output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1046                if PAD {
1047                    output[write + 3] = b'=';
1048                }
1049            }
1050            _ => unreachable!(),
1051        }
1052
1053        output
1054    }
1055
1056    /// Encodes `input` into `output`, returning the number of bytes written.
1057    pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
1058        let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1059        if output.len() < required {
1060            return Err(EncodeError::OutputTooSmall {
1061                required,
1062                available: output.len(),
1063            });
1064        }
1065
1066        let mut read = 0;
1067        let mut write = 0;
1068        while read + 3 <= input.len() {
1069            let b0 = input[read];
1070            let b1 = input[read + 1];
1071            let b2 = input[read + 2];
1072
1073            output[write] = encode_base64_value::<A>(b0 >> 2);
1074            output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1075            output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1076            output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1077
1078            read += 3;
1079            write += 4;
1080        }
1081
1082        match input.len() - read {
1083            0 => {}
1084            1 => {
1085                let b0 = input[read];
1086                output[write] = encode_base64_value::<A>(b0 >> 2);
1087                output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1088                write += 2;
1089                if PAD {
1090                    output[write] = b'=';
1091                    output[write + 1] = b'=';
1092                    write += 2;
1093                }
1094            }
1095            2 => {
1096                let b0 = input[read];
1097                let b1 = input[read + 1];
1098                output[write] = encode_base64_value::<A>(b0 >> 2);
1099                output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1100                output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1101                write += 3;
1102                if PAD {
1103                    output[write] = b'=';
1104                    write += 1;
1105                }
1106            }
1107            _ => unreachable!(),
1108        }
1109
1110        Ok(write)
1111    }
1112
1113    /// Encodes `input` into `output` and clears all bytes after the encoded
1114    /// prefix.
1115    ///
1116    /// If encoding fails, the entire output buffer is cleared before the error
1117    /// is returned.
1118    ///
1119    /// # Examples
1120    ///
1121    /// ```
1122    /// use base64_ng::STANDARD;
1123    ///
1124    /// let mut output = [0xff; 12];
1125    /// let written = STANDARD
1126    ///     .encode_slice_clear_tail(b"hello", &mut output)
1127    ///     .unwrap();
1128    ///
1129    /// assert_eq!(&output[..written], b"aGVsbG8=");
1130    /// assert!(output[written..].iter().all(|byte| *byte == 0));
1131    /// ```
1132    pub fn encode_slice_clear_tail(
1133        &self,
1134        input: &[u8],
1135        output: &mut [u8],
1136    ) -> Result<usize, EncodeError> {
1137        let written = match self.encode_slice(input, output) {
1138            Ok(written) => written,
1139            Err(err) => {
1140                output.fill(0);
1141                return Err(err);
1142            }
1143        };
1144        output[written..].fill(0);
1145        Ok(written)
1146    }
1147
1148    /// Encodes `input` into a newly allocated byte vector.
1149    #[cfg(feature = "alloc")]
1150    pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
1151        let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1152        let mut output = alloc::vec![0; required];
1153        let written = self.encode_slice(input, &mut output)?;
1154        output.truncate(written);
1155        Ok(output)
1156    }
1157
1158    /// Encodes `input` into a newly allocated UTF-8 string.
1159    ///
1160    /// Base64 output is ASCII by construction. This helper is available with
1161    /// the `alloc` feature and has the same encoding semantics as
1162    /// [`Self::encode_slice`].
1163    ///
1164    /// # Examples
1165    ///
1166    /// ```
1167    /// use base64_ng::{STANDARD, URL_SAFE_NO_PAD};
1168    ///
1169    /// assert_eq!(STANDARD.encode_string(b"hello").unwrap(), "aGVsbG8=");
1170    /// assert_eq!(URL_SAFE_NO_PAD.encode_string(b"\xfb\xff").unwrap(), "-_8");
1171    /// ```
1172    #[cfg(feature = "alloc")]
1173    pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
1174        let output = self.encode_vec(input)?;
1175        match alloc::string::String::from_utf8(output) {
1176            Ok(output) => Ok(output),
1177            Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1178        }
1179    }
1180
1181    /// Encodes the first `input_len` bytes of `buffer` in place.
1182    ///
1183    /// The buffer must have enough spare capacity for the encoded output. The
1184    /// implementation writes from right to left, so unread input bytes are not
1185    /// overwritten before they are encoded.
1186    ///
1187    /// # Examples
1188    ///
1189    /// ```
1190    /// use base64_ng::STANDARD;
1191    ///
1192    /// let mut buffer = [0u8; 8];
1193    /// buffer[..5].copy_from_slice(b"hello");
1194    /// let encoded = STANDARD.encode_in_place(&mut buffer, 5).unwrap();
1195    /// assert_eq!(encoded, b"aGVsbG8=");
1196    /// ```
1197    pub fn encode_in_place<'a>(
1198        &self,
1199        buffer: &'a mut [u8],
1200        input_len: usize,
1201    ) -> Result<&'a mut [u8], EncodeError> {
1202        if input_len > buffer.len() {
1203            return Err(EncodeError::InputTooLarge {
1204                input_len,
1205                buffer_len: buffer.len(),
1206            });
1207        }
1208
1209        let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
1210        if buffer.len() < required {
1211            return Err(EncodeError::OutputTooSmall {
1212                required,
1213                available: buffer.len(),
1214            });
1215        }
1216
1217        let mut read = input_len;
1218        let mut write = required;
1219
1220        match input_len % 3 {
1221            0 => {}
1222            1 => {
1223                read -= 1;
1224                let b0 = buffer[read];
1225                if PAD {
1226                    write -= 4;
1227                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1228                    buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1229                    buffer[write + 2] = b'=';
1230                    buffer[write + 3] = b'=';
1231                } else {
1232                    write -= 2;
1233                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1234                    buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1235                }
1236            }
1237            2 => {
1238                read -= 2;
1239                let b0 = buffer[read];
1240                let b1 = buffer[read + 1];
1241                if PAD {
1242                    write -= 4;
1243                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1244                    buffer[write + 1] =
1245                        encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1246                    buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1247                    buffer[write + 3] = b'=';
1248                } else {
1249                    write -= 3;
1250                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1251                    buffer[write + 1] =
1252                        encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1253                    buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1254                }
1255            }
1256            _ => unreachable!(),
1257        }
1258
1259        while read > 0 {
1260            read -= 3;
1261            write -= 4;
1262            let b0 = buffer[read];
1263            let b1 = buffer[read + 1];
1264            let b2 = buffer[read + 2];
1265
1266            buffer[write] = encode_base64_value::<A>(b0 >> 2);
1267            buffer[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1268            buffer[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1269            buffer[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1270        }
1271
1272        debug_assert_eq!(write, 0);
1273        Ok(&mut buffer[..required])
1274    }
1275
1276    /// Encodes the first `input_len` bytes of `buffer` in place and clears all
1277    /// bytes after the encoded prefix.
1278    ///
1279    /// If encoding fails because `input_len` is too large, the output buffer is
1280    /// too small, or the encoded length overflows `usize`, the entire buffer is
1281    /// cleared before the error is returned.
1282    ///
1283    /// # Examples
1284    ///
1285    /// ```
1286    /// use base64_ng::STANDARD;
1287    ///
1288    /// let mut buffer = [0xff; 12];
1289    /// buffer[..5].copy_from_slice(b"hello");
1290    /// let encoded = STANDARD.encode_in_place_clear_tail(&mut buffer, 5).unwrap();
1291    /// assert_eq!(encoded, b"aGVsbG8=");
1292    /// ```
1293    pub fn encode_in_place_clear_tail<'a>(
1294        &self,
1295        buffer: &'a mut [u8],
1296        input_len: usize,
1297    ) -> Result<&'a mut [u8], EncodeError> {
1298        let len = match self.encode_in_place(buffer, input_len) {
1299            Ok(encoded) => encoded.len(),
1300            Err(err) => {
1301                buffer.fill(0);
1302                return Err(err);
1303            }
1304        };
1305        buffer[len..].fill(0);
1306        Ok(&mut buffer[..len])
1307    }
1308
1309    /// Decodes `input` into `output`, returning the number of bytes written.
1310    ///
1311    /// This is strict decoding. Whitespace, mixed alphabets, malformed padding,
1312    /// and trailing non-padding data are rejected.
1313    pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1314        if input.is_empty() {
1315            return Ok(0);
1316        }
1317
1318        if PAD {
1319            decode_padded::<A>(input, output)
1320        } else {
1321            decode_unpadded::<A>(input, output)
1322        }
1323    }
1324
1325    /// Decodes `input` into `output` and clears all bytes after the decoded
1326    /// prefix.
1327    ///
1328    /// If decoding fails, the entire output buffer is cleared before the error
1329    /// is returned.
1330    ///
1331    /// # Examples
1332    ///
1333    /// ```
1334    /// use base64_ng::STANDARD;
1335    ///
1336    /// let mut output = [0xff; 8];
1337    /// let written = STANDARD
1338    ///     .decode_slice_clear_tail(b"aGk=", &mut output)
1339    ///     .unwrap();
1340    ///
1341    /// assert_eq!(&output[..written], b"hi");
1342    /// assert!(output[written..].iter().all(|byte| *byte == 0));
1343    /// ```
1344    pub fn decode_slice_clear_tail(
1345        &self,
1346        input: &[u8],
1347        output: &mut [u8],
1348    ) -> Result<usize, DecodeError> {
1349        let written = match self.decode_slice(input, output) {
1350            Ok(written) => written,
1351            Err(err) => {
1352                output.fill(0);
1353                return Err(err);
1354            }
1355        };
1356        output[written..].fill(0);
1357        Ok(written)
1358    }
1359
1360    /// Decodes `input` using the explicit legacy whitespace profile.
1361    ///
1362    /// ASCII space, tab, carriage return, and line feed bytes are ignored.
1363    /// Alphabet selection, padding placement, trailing data after padding, and
1364    /// non-canonical trailing bits remain strict.
1365    pub fn decode_slice_legacy(
1366        &self,
1367        input: &[u8],
1368        output: &mut [u8],
1369    ) -> Result<usize, DecodeError> {
1370        let required = validate_legacy_decode::<A, PAD>(input)?;
1371        if output.len() < required {
1372            return Err(DecodeError::OutputTooSmall {
1373                required,
1374                available: output.len(),
1375            });
1376        }
1377        decode_legacy_to_slice::<A, PAD>(input, output)
1378    }
1379
1380    /// Decodes `input` using the explicit legacy whitespace profile and clears
1381    /// all bytes after the decoded prefix.
1382    ///
1383    /// If validation or decoding fails, the entire output buffer is cleared
1384    /// before the error is returned.
1385    ///
1386    /// # Examples
1387    ///
1388    /// ```
1389    /// use base64_ng::STANDARD;
1390    ///
1391    /// let mut output = [0xff; 8];
1392    /// let written = STANDARD
1393    ///     .decode_slice_legacy_clear_tail(b" aG\r\nk= ", &mut output)
1394    ///     .unwrap();
1395    ///
1396    /// assert_eq!(&output[..written], b"hi");
1397    /// assert!(output[written..].iter().all(|byte| *byte == 0));
1398    /// ```
1399    pub fn decode_slice_legacy_clear_tail(
1400        &self,
1401        input: &[u8],
1402        output: &mut [u8],
1403    ) -> Result<usize, DecodeError> {
1404        let written = match self.decode_slice_legacy(input, output) {
1405            Ok(written) => written,
1406            Err(err) => {
1407                output.fill(0);
1408                return Err(err);
1409            }
1410        };
1411        output[written..].fill(0);
1412        Ok(written)
1413    }
1414
1415    /// Decodes `input` into a newly allocated byte vector.
1416    ///
1417    /// This is strict decoding with the same semantics as [`Self::decode_slice`].
1418    #[cfg(feature = "alloc")]
1419    pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1420        let required = validate_decode::<A, PAD>(input)?;
1421        let mut output = alloc::vec![0; required];
1422        let written = match self.decode_slice(input, &mut output) {
1423            Ok(written) => written,
1424            Err(err) => {
1425                output.fill(0);
1426                return Err(err);
1427            }
1428        };
1429        output.truncate(written);
1430        Ok(output)
1431    }
1432
1433    /// Decodes `input` into a newly allocated byte vector using the explicit
1434    /// legacy whitespace profile.
1435    #[cfg(feature = "alloc")]
1436    pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1437        let required = validate_legacy_decode::<A, PAD>(input)?;
1438        let mut output = alloc::vec![0; required];
1439        let written = match self.decode_slice_legacy(input, &mut output) {
1440            Ok(written) => written,
1441            Err(err) => {
1442                output.fill(0);
1443                return Err(err);
1444            }
1445        };
1446        output.truncate(written);
1447        Ok(output)
1448    }
1449
1450    /// Decodes the buffer in place and returns the decoded prefix.
1451    ///
1452    /// # Examples
1453    ///
1454    /// ```
1455    /// use base64_ng::STANDARD_NO_PAD;
1456    ///
1457    /// let mut buffer = *b"Zm9vYmFy";
1458    /// let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
1459    /// assert_eq!(decoded, b"foobar");
1460    /// ```
1461    pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
1462        let len = Self::decode_slice_to_start(buffer)?;
1463        Ok(&mut buffer[..len])
1464    }
1465
1466    /// Decodes the buffer in place and clears all bytes after the decoded prefix.
1467    ///
1468    /// If decoding fails, the entire buffer is cleared before the error is
1469    /// returned. Use this variant when the encoded or partially decoded data is
1470    /// sensitive and the caller wants best-effort cleanup without adding a
1471    /// dependency.
1472    ///
1473    /// # Examples
1474    ///
1475    /// ```
1476    /// use base64_ng::STANDARD;
1477    ///
1478    /// let mut buffer = *b"aGk=";
1479    /// let decoded = STANDARD.decode_in_place_clear_tail(&mut buffer).unwrap();
1480    /// assert_eq!(decoded, b"hi");
1481    /// ```
1482    pub fn decode_in_place_clear_tail<'a>(
1483        &self,
1484        buffer: &'a mut [u8],
1485    ) -> Result<&'a mut [u8], DecodeError> {
1486        let len = match Self::decode_slice_to_start(buffer) {
1487            Ok(len) => len,
1488            Err(err) => {
1489                buffer.fill(0);
1490                return Err(err);
1491            }
1492        };
1493        buffer[len..].fill(0);
1494        Ok(&mut buffer[..len])
1495    }
1496
1497    /// Decodes `buffer` in place using the explicit legacy whitespace profile.
1498    ///
1499    /// Ignored whitespace is compacted out before decoding. If validation
1500    /// fails, the buffer contents are unspecified.
1501    pub fn decode_in_place_legacy<'a>(
1502        &self,
1503        buffer: &'a mut [u8],
1504    ) -> Result<&'a mut [u8], DecodeError> {
1505        let _required = validate_legacy_decode::<A, PAD>(buffer)?;
1506        let mut write = 0;
1507        let mut read = 0;
1508        while read < buffer.len() {
1509            let byte = buffer[read];
1510            if !is_legacy_whitespace(byte) {
1511                buffer[write] = byte;
1512                write += 1;
1513            }
1514            read += 1;
1515        }
1516        let len = Self::decode_slice_to_start(&mut buffer[..write])?;
1517        Ok(&mut buffer[..len])
1518    }
1519
1520    /// Decodes `buffer` in place using the explicit legacy whitespace profile
1521    /// and clears all bytes after the decoded prefix.
1522    ///
1523    /// If validation or decoding fails, the entire buffer is cleared before the
1524    /// error is returned.
1525    pub fn decode_in_place_legacy_clear_tail<'a>(
1526        &self,
1527        buffer: &'a mut [u8],
1528    ) -> Result<&'a mut [u8], DecodeError> {
1529        if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
1530            buffer.fill(0);
1531            return Err(err);
1532        }
1533
1534        let mut write = 0;
1535        let mut read = 0;
1536        while read < buffer.len() {
1537            let byte = buffer[read];
1538            if !is_legacy_whitespace(byte) {
1539                buffer[write] = byte;
1540                write += 1;
1541            }
1542            read += 1;
1543        }
1544
1545        let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
1546            Ok(len) => len,
1547            Err(err) => {
1548                buffer.fill(0);
1549                return Err(err);
1550            }
1551        };
1552        buffer[len..].fill(0);
1553        Ok(&mut buffer[..len])
1554    }
1555
1556    fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
1557        let input_len = buffer.len();
1558        let mut read = 0;
1559        let mut write = 0;
1560        while read + 4 <= input_len {
1561            let chunk = [
1562                buffer[read],
1563                buffer[read + 1],
1564                buffer[read + 2],
1565                buffer[read + 3],
1566            ];
1567            let written = decode_chunk::<A, PAD>(&chunk, &mut buffer[write..])
1568                .map_err(|err| err.with_index_offset(read))?;
1569            read += 4;
1570            write += written;
1571            if written < 3 {
1572                if read != input_len {
1573                    return Err(DecodeError::InvalidPadding { index: read - 4 });
1574                }
1575                return Ok(write);
1576            }
1577        }
1578
1579        let rem = input_len - read;
1580        if rem == 0 {
1581            return Ok(write);
1582        }
1583        if PAD {
1584            return Err(DecodeError::InvalidLength);
1585        }
1586        let mut tail = [0u8; 3];
1587        tail[..rem].copy_from_slice(&buffer[read..input_len]);
1588        decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
1589            .map_err(|err| err.with_index_offset(read))
1590            .map(|n| write + n)
1591    }
1592}
1593
1594/// Encoding error.
1595#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1596pub enum EncodeError {
1597    /// The encoded output length would overflow `usize`.
1598    LengthOverflow,
1599    /// The caller-provided input length exceeds the provided buffer.
1600    InputTooLarge {
1601        /// Requested input bytes.
1602        input_len: usize,
1603        /// Available buffer bytes.
1604        buffer_len: usize,
1605    },
1606    /// The output buffer is too small.
1607    OutputTooSmall {
1608        /// Required output bytes.
1609        required: usize,
1610        /// Available output bytes.
1611        available: usize,
1612    },
1613}
1614
1615impl core::fmt::Display for EncodeError {
1616    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1617        match self {
1618            Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
1619            Self::InputTooLarge {
1620                input_len,
1621                buffer_len,
1622            } => write!(
1623                f,
1624                "base64 input length {input_len} exceeds buffer length {buffer_len}"
1625            ),
1626            Self::OutputTooSmall {
1627                required,
1628                available,
1629            } => write!(
1630                f,
1631                "base64 output buffer too small: required {required}, available {available}"
1632            ),
1633        }
1634    }
1635}
1636
1637#[cfg(feature = "std")]
1638impl std::error::Error for EncodeError {}
1639
1640/// Decoding error.
1641#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1642pub enum DecodeError {
1643    /// The encoded input length is impossible for the selected padding policy.
1644    InvalidLength,
1645    /// A byte is not valid for the selected alphabet.
1646    InvalidByte {
1647        /// Byte index in the input.
1648        index: usize,
1649        /// Invalid byte value.
1650        byte: u8,
1651    },
1652    /// Padding is missing, misplaced, or non-canonical.
1653    InvalidPadding {
1654        /// Byte index where padding became invalid.
1655        index: usize,
1656    },
1657    /// The output buffer is too small.
1658    OutputTooSmall {
1659        /// Required output bytes.
1660        required: usize,
1661        /// Available output bytes.
1662        available: usize,
1663    },
1664}
1665
1666impl core::fmt::Display for DecodeError {
1667    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1668        match self {
1669            Self::InvalidLength => f.write_str("invalid base64 input length"),
1670            Self::InvalidByte { index, byte } => {
1671                write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
1672            }
1673            Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
1674            Self::OutputTooSmall {
1675                required,
1676                available,
1677            } => write!(
1678                f,
1679                "base64 decode output buffer too small: required {required}, available {available}"
1680            ),
1681        }
1682    }
1683}
1684
1685impl DecodeError {
1686    fn with_index_offset(self, offset: usize) -> Self {
1687        match self {
1688            Self::InvalidByte { index, byte } => Self::InvalidByte {
1689                index: index + offset,
1690                byte,
1691            },
1692            Self::InvalidPadding { index } => Self::InvalidPadding {
1693                index: index + offset,
1694            },
1695            Self::InvalidLength | Self::OutputTooSmall { .. } => self,
1696        }
1697    }
1698}
1699
1700#[cfg(feature = "std")]
1701impl std::error::Error for DecodeError {}
1702
1703fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
1704    input: &[u8],
1705) -> Result<usize, DecodeError> {
1706    let mut chunk = [0u8; 4];
1707    let mut indexes = [0usize; 4];
1708    let mut chunk_len = 0;
1709    let mut required = 0;
1710    let mut terminal_seen = false;
1711
1712    for (index, byte) in input.iter().copied().enumerate() {
1713        if is_legacy_whitespace(byte) {
1714            continue;
1715        }
1716        if terminal_seen {
1717            return Err(DecodeError::InvalidPadding { index });
1718        }
1719
1720        chunk[chunk_len] = byte;
1721        indexes[chunk_len] = index;
1722        chunk_len += 1;
1723
1724        if chunk_len == 4 {
1725            let written =
1726                validate_chunk::<A, PAD>(&chunk).map_err(|err| map_chunk_error(err, &indexes))?;
1727            required += written;
1728            terminal_seen = written < 3;
1729            chunk_len = 0;
1730        }
1731    }
1732
1733    if chunk_len == 0 {
1734        return Ok(required);
1735    }
1736    if PAD {
1737        return Err(DecodeError::InvalidLength);
1738    }
1739
1740    validate_tail_unpadded::<A>(&chunk[..chunk_len])
1741        .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
1742    Ok(required + decoded_capacity(chunk_len))
1743}
1744
1745fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
1746    input: &[u8],
1747    output: &mut [u8],
1748) -> Result<usize, DecodeError> {
1749    let mut chunk = [0u8; 4];
1750    let mut indexes = [0usize; 4];
1751    let mut chunk_len = 0;
1752    let mut write = 0;
1753    let mut terminal_seen = false;
1754
1755    for (index, byte) in input.iter().copied().enumerate() {
1756        if is_legacy_whitespace(byte) {
1757            continue;
1758        }
1759        if terminal_seen {
1760            return Err(DecodeError::InvalidPadding { index });
1761        }
1762
1763        chunk[chunk_len] = byte;
1764        indexes[chunk_len] = index;
1765        chunk_len += 1;
1766
1767        if chunk_len == 4 {
1768            let written = decode_chunk::<A, PAD>(&chunk, &mut output[write..])
1769                .map_err(|err| map_chunk_error(err, &indexes))?;
1770            write += written;
1771            terminal_seen = written < 3;
1772            chunk_len = 0;
1773        }
1774    }
1775
1776    if chunk_len == 0 {
1777        return Ok(write);
1778    }
1779    if PAD {
1780        return Err(DecodeError::InvalidLength);
1781    }
1782
1783    decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
1784        .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
1785        .map(|n| write + n)
1786}
1787
1788#[inline]
1789const fn is_legacy_whitespace(byte: u8) -> bool {
1790    matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
1791}
1792
1793fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
1794    match err {
1795        DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
1796            index: indexes[index],
1797            byte,
1798        },
1799        DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
1800            index: indexes[index],
1801        },
1802        DecodeError::InvalidLength | DecodeError::OutputTooSmall { .. } => err,
1803    }
1804}
1805
1806fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
1807    match err {
1808        DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
1809            index: indexes[index],
1810            byte,
1811        },
1812        DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
1813            index: indexes[index],
1814        },
1815        DecodeError::InvalidByte { .. }
1816        | DecodeError::InvalidPadding { .. }
1817        | DecodeError::InvalidLength
1818        | DecodeError::OutputTooSmall { .. } => err,
1819    }
1820}
1821
1822fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1823    if !input.len().is_multiple_of(4) {
1824        return Err(DecodeError::InvalidLength);
1825    }
1826    let required = decoded_len_padded(input)?;
1827    if output.len() < required {
1828        return Err(DecodeError::OutputTooSmall {
1829            required,
1830            available: output.len(),
1831        });
1832    }
1833
1834    let mut read = 0;
1835    let mut write = 0;
1836    while read < input.len() {
1837        let written = decode_chunk::<A, true>(&input[read..read + 4], &mut output[write..])
1838            .map_err(|err| err.with_index_offset(read))?;
1839        read += 4;
1840        write += written;
1841        if written < 3 && read != input.len() {
1842            return Err(DecodeError::InvalidPadding { index: read - 4 });
1843        }
1844    }
1845    Ok(write)
1846}
1847
1848#[cfg(feature = "alloc")]
1849fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
1850    if input.is_empty() {
1851        return Ok(0);
1852    }
1853
1854    if PAD {
1855        validate_padded::<A>(input)
1856    } else {
1857        validate_unpadded::<A>(input)
1858    }
1859}
1860
1861#[cfg(feature = "alloc")]
1862fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
1863    if !input.len().is_multiple_of(4) {
1864        return Err(DecodeError::InvalidLength);
1865    }
1866    let required = decoded_len_padded(input)?;
1867
1868    let mut read = 0;
1869    while read < input.len() {
1870        let written = validate_chunk::<A, true>(&input[read..read + 4])
1871            .map_err(|err| err.with_index_offset(read))?;
1872        read += 4;
1873        if written < 3 && read != input.len() {
1874            return Err(DecodeError::InvalidPadding { index: read - 4 });
1875        }
1876    }
1877
1878    Ok(required)
1879}
1880
1881#[cfg(feature = "alloc")]
1882fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
1883    let required = decoded_len_unpadded(input)?;
1884
1885    let mut read = 0;
1886    while read + 4 <= input.len() {
1887        validate_chunk::<A, false>(&input[read..read + 4])
1888            .map_err(|err| err.with_index_offset(read))?;
1889        read += 4;
1890    }
1891    validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
1892
1893    Ok(required)
1894}
1895
1896fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1897    let required = decoded_len_unpadded(input)?;
1898    if output.len() < required {
1899        return Err(DecodeError::OutputTooSmall {
1900            required,
1901            available: output.len(),
1902        });
1903    }
1904
1905    let mut read = 0;
1906    let mut write = 0;
1907    while read + 4 <= input.len() {
1908        let written = decode_chunk::<A, false>(&input[read..read + 4], &mut output[write..])
1909            .map_err(|err| err.with_index_offset(read))?;
1910        read += 4;
1911        write += written;
1912    }
1913    decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
1914        .map_err(|err| err.with_index_offset(read))
1915        .map(|n| write + n)
1916}
1917
1918fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
1919    if input.is_empty() {
1920        return Ok(0);
1921    }
1922    if !input.len().is_multiple_of(4) {
1923        return Err(DecodeError::InvalidLength);
1924    }
1925    let mut padding = 0;
1926    if input[input.len() - 1] == b'=' {
1927        padding += 1;
1928    }
1929    if input[input.len() - 2] == b'=' {
1930        padding += 1;
1931    }
1932    if padding == 0
1933        && let Some(index) = input.iter().position(|byte| *byte == b'=')
1934    {
1935        return Err(DecodeError::InvalidPadding { index });
1936    }
1937    if padding > 0 {
1938        let first_pad = input.len() - padding;
1939        if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
1940            return Err(DecodeError::InvalidPadding { index });
1941        }
1942    }
1943    Ok(input.len() / 4 * 3 - padding)
1944}
1945
1946fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
1947    if input.len() % 4 == 1 {
1948        return Err(DecodeError::InvalidLength);
1949    }
1950    if let Some(index) = input.iter().position(|byte| *byte == b'=') {
1951        return Err(DecodeError::InvalidPadding { index });
1952    }
1953    Ok(decoded_capacity(input.len()))
1954}
1955
1956fn validate_chunk<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
1957    debug_assert_eq!(input.len(), 4);
1958    let _v0 = decode_byte::<A>(input[0], 0)?;
1959    let v1 = decode_byte::<A>(input[1], 1)?;
1960
1961    match (input[2], input[3]) {
1962        (b'=', b'=') if PAD => {
1963            if v1 & 0b0000_1111 != 0 {
1964                return Err(DecodeError::InvalidPadding { index: 1 });
1965            }
1966            Ok(1)
1967        }
1968        (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
1969        (_, b'=') if PAD => {
1970            let v2 = decode_byte::<A>(input[2], 2)?;
1971            if v2 & 0b0000_0011 != 0 {
1972                return Err(DecodeError::InvalidPadding { index: 2 });
1973            }
1974            Ok(2)
1975        }
1976        (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
1977            index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
1978        }),
1979        _ => {
1980            decode_byte::<A>(input[2], 2)?;
1981            decode_byte::<A>(input[3], 3)?;
1982            Ok(3)
1983        }
1984    }
1985}
1986
1987fn decode_chunk<A: Alphabet, const PAD: bool>(
1988    input: &[u8],
1989    output: &mut [u8],
1990) -> Result<usize, DecodeError> {
1991    debug_assert_eq!(input.len(), 4);
1992    let v0 = decode_byte::<A>(input[0], 0)?;
1993    let v1 = decode_byte::<A>(input[1], 1)?;
1994
1995    match (input[2], input[3]) {
1996        (b'=', b'=') if PAD => {
1997            if output.is_empty() {
1998                return Err(DecodeError::OutputTooSmall {
1999                    required: 1,
2000                    available: output.len(),
2001                });
2002            }
2003            if v1 & 0b0000_1111 != 0 {
2004                return Err(DecodeError::InvalidPadding { index: 1 });
2005            }
2006            output[0] = (v0 << 2) | (v1 >> 4);
2007            Ok(1)
2008        }
2009        (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
2010        (_, b'=') if PAD => {
2011            if output.len() < 2 {
2012                return Err(DecodeError::OutputTooSmall {
2013                    required: 2,
2014                    available: output.len(),
2015                });
2016            }
2017            let v2 = decode_byte::<A>(input[2], 2)?;
2018            if v2 & 0b0000_0011 != 0 {
2019                return Err(DecodeError::InvalidPadding { index: 2 });
2020            }
2021            output[0] = (v0 << 2) | (v1 >> 4);
2022            output[1] = (v1 << 4) | (v2 >> 2);
2023            Ok(2)
2024        }
2025        (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
2026            index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
2027        }),
2028        _ => {
2029            if output.len() < 3 {
2030                return Err(DecodeError::OutputTooSmall {
2031                    required: 3,
2032                    available: output.len(),
2033                });
2034            }
2035            let v2 = decode_byte::<A>(input[2], 2)?;
2036            let v3 = decode_byte::<A>(input[3], 3)?;
2037            output[0] = (v0 << 2) | (v1 >> 4);
2038            output[1] = (v1 << 4) | (v2 >> 2);
2039            output[2] = (v2 << 6) | v3;
2040            Ok(3)
2041        }
2042    }
2043}
2044
2045fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
2046    match input.len() {
2047        0 => Ok(()),
2048        2 => {
2049            decode_byte::<A>(input[0], 0)?;
2050            let v1 = decode_byte::<A>(input[1], 1)?;
2051            if v1 & 0b0000_1111 != 0 {
2052                return Err(DecodeError::InvalidPadding { index: 1 });
2053            }
2054            Ok(())
2055        }
2056        3 => {
2057            decode_byte::<A>(input[0], 0)?;
2058            decode_byte::<A>(input[1], 1)?;
2059            let v2 = decode_byte::<A>(input[2], 2)?;
2060            if v2 & 0b0000_0011 != 0 {
2061                return Err(DecodeError::InvalidPadding { index: 2 });
2062            }
2063            Ok(())
2064        }
2065        _ => Err(DecodeError::InvalidLength),
2066    }
2067}
2068
2069fn decode_tail_unpadded<A: Alphabet>(
2070    input: &[u8],
2071    output: &mut [u8],
2072) -> Result<usize, DecodeError> {
2073    match input.len() {
2074        0 => Ok(0),
2075        2 => {
2076            if output.is_empty() {
2077                return Err(DecodeError::OutputTooSmall {
2078                    required: 1,
2079                    available: output.len(),
2080                });
2081            }
2082            let v0 = decode_byte::<A>(input[0], 0)?;
2083            let v1 = decode_byte::<A>(input[1], 1)?;
2084            if v1 & 0b0000_1111 != 0 {
2085                return Err(DecodeError::InvalidPadding { index: 1 });
2086            }
2087            output[0] = (v0 << 2) | (v1 >> 4);
2088            Ok(1)
2089        }
2090        3 => {
2091            if output.len() < 2 {
2092                return Err(DecodeError::OutputTooSmall {
2093                    required: 2,
2094                    available: output.len(),
2095                });
2096            }
2097            let v0 = decode_byte::<A>(input[0], 0)?;
2098            let v1 = decode_byte::<A>(input[1], 1)?;
2099            let v2 = decode_byte::<A>(input[2], 2)?;
2100            if v2 & 0b0000_0011 != 0 {
2101                return Err(DecodeError::InvalidPadding { index: 2 });
2102            }
2103            output[0] = (v0 << 2) | (v1 >> 4);
2104            output[1] = (v1 << 4) | (v2 >> 2);
2105            Ok(2)
2106        }
2107        _ => Err(DecodeError::InvalidLength),
2108    }
2109}
2110
2111fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
2112    A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
2113}
2114
2115fn ct_decode_slice<A: Alphabet, const PAD: bool>(
2116    input: &[u8],
2117    output: &mut [u8],
2118) -> Result<usize, DecodeError> {
2119    if input.is_empty() {
2120        return Ok(0);
2121    }
2122
2123    if PAD {
2124        ct_decode_padded::<A>(input, output)
2125    } else {
2126        ct_decode_unpadded::<A>(input, output)
2127    }
2128}
2129
2130fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2131    if !input.len().is_multiple_of(4) {
2132        return Err(DecodeError::InvalidLength);
2133    }
2134
2135    let padding = ct_padding_len(input);
2136    let required = input.len() / 4 * 3 - padding;
2137    if output.len() < required {
2138        return Err(DecodeError::OutputTooSmall {
2139            required,
2140            available: output.len(),
2141        });
2142    }
2143
2144    let mut invalid_byte = 0u8;
2145    let mut invalid_padding = 0u8;
2146    let mut write = 0;
2147    let mut read = 0;
2148
2149    while read < input.len() {
2150        let is_last = read + 4 == input.len();
2151        let b0 = input[read];
2152        let b1 = input[read + 1];
2153        let b2 = input[read + 2];
2154        let b3 = input[read + 3];
2155        let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2156        let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2157        let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2158        let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
2159
2160        invalid_byte |= u8::from(!valid0);
2161        invalid_byte |= u8::from(!valid1);
2162
2163        if is_last && padding == 2 {
2164            invalid_padding |= u8::from((v1 & 0b0000_1111) != 0);
2165            output[write] = (v0 << 2) | (v1 >> 4);
2166            write += 1;
2167        } else if is_last && padding == 1 {
2168            invalid_byte |= u8::from(!valid2);
2169            invalid_padding |= u8::from(b2 == b'=');
2170            invalid_padding |= u8::from((v2 & 0b0000_0011) != 0);
2171            output[write] = (v0 << 2) | (v1 >> 4);
2172            output[write + 1] = (v1 << 4) | (v2 >> 2);
2173            write += 2;
2174        } else {
2175            invalid_byte |= u8::from(!valid2);
2176            invalid_byte |= u8::from(!valid3);
2177            invalid_padding |= u8::from(b2 == b'=');
2178            invalid_padding |= u8::from(b3 == b'=');
2179            output[write] = (v0 << 2) | (v1 >> 4);
2180            output[write + 1] = (v1 << 4) | (v2 >> 2);
2181            output[write + 2] = (v2 << 6) | v3;
2182            write += 3;
2183        }
2184
2185        read += 4;
2186    }
2187
2188    report_ct_error(invalid_byte, invalid_padding)?;
2189    Ok(write)
2190}
2191
2192fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2193    if input.len() % 4 == 1 {
2194        return Err(DecodeError::InvalidLength);
2195    }
2196
2197    let required = decoded_capacity(input.len());
2198    if output.len() < required {
2199        return Err(DecodeError::OutputTooSmall {
2200            required,
2201            available: output.len(),
2202        });
2203    }
2204
2205    let mut invalid_byte = 0u8;
2206    let mut invalid_padding = 0u8;
2207    let mut write = 0;
2208    let mut read = 0;
2209
2210    while read + 4 <= input.len() {
2211        let b0 = input[read];
2212        let b1 = input[read + 1];
2213        let b2 = input[read + 2];
2214        let b3 = input[read + 3];
2215        let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2216        let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2217        let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2218        let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
2219
2220        invalid_byte |= u8::from(!valid0);
2221        invalid_byte |= u8::from(!valid1);
2222        invalid_byte |= u8::from(!valid2);
2223        invalid_byte |= u8::from(!valid3);
2224        invalid_padding |= u8::from(b0 == b'=');
2225        invalid_padding |= u8::from(b1 == b'=');
2226        invalid_padding |= u8::from(b2 == b'=');
2227        invalid_padding |= u8::from(b3 == b'=');
2228
2229        output[write] = (v0 << 2) | (v1 >> 4);
2230        output[write + 1] = (v1 << 4) | (v2 >> 2);
2231        output[write + 2] = (v2 << 6) | v3;
2232        read += 4;
2233        write += 3;
2234    }
2235
2236    match input.len() - read {
2237        0 => {}
2238        2 => {
2239            let b0 = input[read];
2240            let b1 = input[read + 1];
2241            let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2242            let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2243            invalid_byte |= u8::from(!valid0);
2244            invalid_byte |= u8::from(!valid1);
2245            invalid_padding |= u8::from(b0 == b'=');
2246            invalid_padding |= u8::from(b1 == b'=');
2247            invalid_padding |= u8::from((v1 & 0b0000_1111) != 0);
2248            output[write] = (v0 << 2) | (v1 >> 4);
2249            write += 1;
2250        }
2251        3 => {
2252            let b0 = input[read];
2253            let b1 = input[read + 1];
2254            let b2 = input[read + 2];
2255            let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
2256            let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
2257            let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
2258            invalid_byte |= u8::from(!valid0);
2259            invalid_byte |= u8::from(!valid1);
2260            invalid_byte |= u8::from(!valid2);
2261            invalid_padding |= u8::from(b0 == b'=');
2262            invalid_padding |= u8::from(b1 == b'=');
2263            invalid_padding |= u8::from(b2 == b'=');
2264            invalid_padding |= u8::from((v2 & 0b0000_0011) != 0);
2265            output[write] = (v0 << 2) | (v1 >> 4);
2266            output[write + 1] = (v1 << 4) | (v2 >> 2);
2267            write += 2;
2268        }
2269        _ => return Err(DecodeError::InvalidLength),
2270    }
2271
2272    report_ct_error(invalid_byte, invalid_padding)?;
2273    Ok(write)
2274}
2275
2276#[inline]
2277fn ct_decode_ascii_base64<A: Alphabet>(byte: u8) -> (u8, bool) {
2278    let upper = mask_if(byte.wrapping_sub(b'A') <= b'Z' - b'A');
2279    let lower = mask_if(byte.wrapping_sub(b'a') <= b'z' - b'a');
2280    let digit = mask_if(byte.wrapping_sub(b'0') <= b'9' - b'0');
2281    let value_62 = mask_if(byte == A::ENCODE[62]);
2282    let value_63 = mask_if(byte == A::ENCODE[63]);
2283    let valid = upper | lower | digit | value_62 | value_63;
2284
2285    let decoded = (byte.wrapping_sub(b'A') & upper)
2286        | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
2287        | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
2288        | (0x3e & value_62)
2289        | (0x3f & value_63);
2290
2291    (decoded, valid != 0)
2292}
2293
2294fn ct_padding_len(input: &[u8]) -> usize {
2295    let last = input[input.len() - 1];
2296    let before_last = input[input.len() - 2];
2297    usize::from(mask_if(last == b'=') & 1) + usize::from(mask_if(before_last == b'=') & 1)
2298}
2299
2300fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
2301    if invalid_padding != 0 {
2302        Err(DecodeError::InvalidPadding { index: 0 })
2303    } else if invalid_byte != 0 {
2304        Err(DecodeError::InvalidByte { index: 0, byte: 0 })
2305    } else {
2306        Ok(())
2307    }
2308}
2309
2310#[cfg(test)]
2311mod tests {
2312    use super::*;
2313
2314    #[test]
2315    fn encodes_standard_vectors() {
2316        let vectors = [
2317            (&b""[..], &b""[..]),
2318            (&b"f"[..], &b"Zg=="[..]),
2319            (&b"fo"[..], &b"Zm8="[..]),
2320            (&b"foo"[..], &b"Zm9v"[..]),
2321            (&b"foob"[..], &b"Zm9vYg=="[..]),
2322            (&b"fooba"[..], &b"Zm9vYmE="[..]),
2323            (&b"foobar"[..], &b"Zm9vYmFy"[..]),
2324        ];
2325        for (input, expected) in vectors {
2326            let mut output = [0u8; 16];
2327            let written = STANDARD.encode_slice(input, &mut output).unwrap();
2328            assert_eq!(&output[..written], expected);
2329        }
2330    }
2331
2332    #[test]
2333    fn decodes_standard_vectors() {
2334        let vectors = [
2335            (&b""[..], &b""[..]),
2336            (&b"Zg=="[..], &b"f"[..]),
2337            (&b"Zm8="[..], &b"fo"[..]),
2338            (&b"Zm9v"[..], &b"foo"[..]),
2339            (&b"Zm9vYg=="[..], &b"foob"[..]),
2340            (&b"Zm9vYmE="[..], &b"fooba"[..]),
2341            (&b"Zm9vYmFy"[..], &b"foobar"[..]),
2342        ];
2343        for (input, expected) in vectors {
2344            let mut output = [0u8; 16];
2345            let written = STANDARD.decode_slice(input, &mut output).unwrap();
2346            assert_eq!(&output[..written], expected);
2347        }
2348    }
2349
2350    #[test]
2351    fn supports_unpadded_url_safe() {
2352        let mut encoded = [0u8; 16];
2353        let written = URL_SAFE_NO_PAD
2354            .encode_slice(b"\xfb\xff", &mut encoded)
2355            .unwrap();
2356        assert_eq!(&encoded[..written], b"-_8");
2357
2358        let mut decoded = [0u8; 2];
2359        let written = URL_SAFE_NO_PAD
2360            .decode_slice(&encoded[..written], &mut decoded)
2361            .unwrap();
2362        assert_eq!(&decoded[..written], b"\xfb\xff");
2363    }
2364
2365    #[test]
2366    fn decodes_in_place() {
2367        let mut buffer = *b"Zm9vYmFy";
2368        let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
2369        assert_eq!(decoded, b"foobar");
2370    }
2371
2372    #[test]
2373    fn rejects_non_canonical_padding_bits() {
2374        let mut output = [0u8; 4];
2375        assert_eq!(
2376            STANDARD.decode_slice(b"Zh==", &mut output),
2377            Err(DecodeError::InvalidPadding { index: 1 })
2378        );
2379        assert_eq!(
2380            STANDARD.decode_slice(b"Zm9=", &mut output),
2381            Err(DecodeError::InvalidPadding { index: 2 })
2382        );
2383    }
2384}