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/// Standard Base64 engine with padding.
673pub const STANDARD: Engine<Standard, true> = Engine::new();
674
675/// Standard Base64 engine without padding.
676pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
677
678/// URL-safe Base64 engine with padding.
679pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
680
681/// URL-safe Base64 engine without padding.
682pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
683
684/// Returns the encoded length for an input length and padding policy.
685///
686/// This function returns [`EncodeError::LengthOverflow`] instead of panicking.
687/// Use [`checked_encoded_len`] when an `Option<usize>` is more convenient.
688///
689/// # Examples
690///
691/// ```
692/// use base64_ng::encoded_len;
693///
694/// assert_eq!(encoded_len(5, true).unwrap(), 8);
695/// assert_eq!(encoded_len(5, false).unwrap(), 7);
696/// assert!(encoded_len(usize::MAX, true).is_err());
697/// ```
698pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
699    match checked_encoded_len(input_len, padded) {
700        Some(len) => Ok(len),
701        None => Err(EncodeError::LengthOverflow),
702    }
703}
704
705/// Returns the encoded length, or `None` if it would overflow `usize`.
706///
707/// # Examples
708///
709/// ```
710/// use base64_ng::checked_encoded_len;
711///
712/// assert_eq!(checked_encoded_len(5, true), Some(8));
713/// assert_eq!(checked_encoded_len(usize::MAX, true), None);
714/// ```
715#[must_use]
716pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
717    let groups = input_len / 3;
718    if groups > usize::MAX / 4 {
719        return None;
720    }
721    let full = groups * 4;
722    let rem = input_len % 3;
723    if rem == 0 {
724        Some(full)
725    } else if padded {
726        full.checked_add(4)
727    } else {
728        full.checked_add(rem + 1)
729    }
730}
731
732/// Returns the maximum decoded length for an encoded input length.
733///
734/// # Examples
735///
736/// ```
737/// use base64_ng::decoded_capacity;
738///
739/// assert_eq!(decoded_capacity(8), 6);
740/// assert_eq!(decoded_capacity(7), 5);
741/// ```
742#[must_use]
743pub const fn decoded_capacity(encoded_len: usize) -> usize {
744    let rem = encoded_len % 4;
745    encoded_len / 4 * 3
746        + if rem == 2 {
747            1
748        } else if rem == 3 {
749            2
750        } else {
751            0
752        }
753}
754
755/// Returns the exact decoded length implied by input length and padding.
756///
757/// This validates padding placement and impossible lengths, but it does not
758/// validate alphabet membership or non-canonical trailing bits.
759///
760/// # Examples
761///
762/// ```
763/// use base64_ng::decoded_len;
764///
765/// assert_eq!(decoded_len(b"aGVsbG8=", true).unwrap(), 5);
766/// assert_eq!(decoded_len(b"aGVsbG8", false).unwrap(), 5);
767/// ```
768pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
769    if padded {
770        decoded_len_padded(input)
771    } else {
772        decoded_len_unpadded(input)
773    }
774}
775
776/// A Base64 alphabet.
777pub trait Alphabet {
778    /// Encoding table indexed by 6-bit values.
779    const ENCODE: [u8; 64];
780
781    /// Decode one byte into a 6-bit value.
782    fn decode(byte: u8) -> Option<u8>;
783}
784
785/// The RFC 4648 standard Base64 alphabet.
786#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
787pub struct Standard;
788
789impl Alphabet for Standard {
790    const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
791
792    #[inline]
793    fn decode(byte: u8) -> Option<u8> {
794        decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
795    }
796}
797
798/// The RFC 4648 URL-safe Base64 alphabet.
799#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
800pub struct UrlSafe;
801
802impl Alphabet for UrlSafe {
803    const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
804
805    #[inline]
806    fn decode(byte: u8) -> Option<u8> {
807        decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
808    }
809}
810
811#[inline]
812const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
813    encode_ascii_base64(value, A::ENCODE[62], A::ENCODE[63])
814}
815
816#[inline]
817const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
818    let upper = mask_if(value < 26);
819    let lower = mask_if(value.wrapping_sub(26) < 26);
820    let digit = mask_if(value.wrapping_sub(52) < 10);
821    let value_62 = mask_if(value == 0x3e);
822    let value_63 = mask_if(value == 0x3f);
823
824    (value.wrapping_add(b'A') & upper)
825        | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
826        | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
827        | (value_62_byte & value_62)
828        | (value_63_byte & value_63)
829}
830
831#[inline]
832fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
833    let upper = mask_if(byte.wrapping_sub(b'A') <= b'Z' - b'A');
834    let lower = mask_if(byte.wrapping_sub(b'a') <= b'z' - b'a');
835    let digit = mask_if(byte.wrapping_sub(b'0') <= b'9' - b'0');
836    let value_62 = mask_if(byte == value_62_byte);
837    let value_63 = mask_if(byte == value_63_byte);
838    let valid = upper | lower | digit | value_62 | value_63;
839
840    let decoded = (byte.wrapping_sub(b'A') & upper)
841        | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
842        | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
843        | (0x3e & value_62)
844        | (0x3f & value_63);
845
846    if valid == 0 { None } else { Some(decoded) }
847}
848
849#[inline]
850const fn mask_if(condition: bool) -> u8 {
851    0u8.wrapping_sub(condition as u8)
852}
853
854/// A zero-sized Base64 engine parameterized by alphabet and padding policy.
855#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
856pub struct Engine<A, const PAD: bool> {
857    alphabet: core::marker::PhantomData<A>,
858}
859
860impl<A, const PAD: bool> Engine<A, PAD>
861where
862    A: Alphabet,
863{
864    /// Creates a new engine value.
865    #[must_use]
866    pub const fn new() -> Self {
867        Self {
868            alphabet: core::marker::PhantomData,
869        }
870    }
871
872    /// Returns the encoded length for this engine's padding policy.
873    pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
874        encoded_len(input_len, PAD)
875    }
876
877    /// Returns the encoded length for this engine, or `None` on overflow.
878    #[must_use]
879    pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
880        checked_encoded_len(input_len, PAD)
881    }
882
883    /// Returns the exact decoded length implied by input length and padding.
884    ///
885    /// This validates padding placement and impossible lengths, but it does not
886    /// validate alphabet membership or non-canonical trailing bits.
887    pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
888        decoded_len(input, PAD)
889    }
890
891    /// Returns the exact decoded length for the explicit legacy profile.
892    ///
893    /// The legacy profile ignores ASCII space, tab, carriage return, and line
894    /// feed bytes before applying the same alphabet, padding, and canonical-bit
895    /// checks as strict decoding.
896    pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
897        validate_legacy_decode::<A, PAD>(input)
898    }
899
900    /// Encodes a fixed-size input into a fixed-size output array in const contexts.
901    ///
902    /// Stable Rust does not yet allow this API to return an array whose length
903    /// is computed from `INPUT_LEN` directly. Instead, the caller supplies the
904    /// output length through the destination type and this function panics
905    /// during const evaluation if the length is wrong.
906    ///
907    /// # Panics
908    ///
909    /// Panics if `OUTPUT_LEN` is not exactly the encoded length for `INPUT_LEN`
910    /// and this engine's padding policy, or if that length overflows `usize`.
911    ///
912    /// # Examples
913    ///
914    /// ```
915    /// use base64_ng::{STANDARD, URL_SAFE_NO_PAD};
916    ///
917    /// const HELLO: [u8; 8] = STANDARD.encode_array(b"hello");
918    /// const URL_SAFE: [u8; 3] = URL_SAFE_NO_PAD.encode_array(b"\xfb\xff");
919    ///
920    /// assert_eq!(&HELLO, b"aGVsbG8=");
921    /// assert_eq!(&URL_SAFE, b"-_8");
922    /// ```
923    ///
924    /// Incorrect output lengths fail during const evaluation:
925    ///
926    /// ```compile_fail
927    /// use base64_ng::STANDARD;
928    ///
929    /// const TOO_SHORT: [u8; 7] = STANDARD.encode_array(b"hello");
930    /// ```
931    #[must_use]
932    pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
933        &self,
934        input: &[u8; INPUT_LEN],
935    ) -> [u8; OUTPUT_LEN] {
936        let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
937            panic!("encoded base64 length overflows usize");
938        };
939        assert!(
940            required == OUTPUT_LEN,
941            "base64 output array has incorrect length"
942        );
943
944        let mut output = [0u8; OUTPUT_LEN];
945        let mut read = 0;
946        let mut write = 0;
947        while INPUT_LEN - read >= 3 {
948            let b0 = input[read];
949            let b1 = input[read + 1];
950            let b2 = input[read + 2];
951
952            output[write] = encode_base64_value::<A>(b0 >> 2);
953            output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
954            output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
955            output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
956
957            read += 3;
958            write += 4;
959        }
960
961        match INPUT_LEN - read {
962            0 => {}
963            1 => {
964                let b0 = input[read];
965                output[write] = encode_base64_value::<A>(b0 >> 2);
966                output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
967                write += 2;
968                if PAD {
969                    output[write] = b'=';
970                    output[write + 1] = b'=';
971                }
972            }
973            2 => {
974                let b0 = input[read];
975                let b1 = input[read + 1];
976                output[write] = encode_base64_value::<A>(b0 >> 2);
977                output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
978                output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
979                if PAD {
980                    output[write + 3] = b'=';
981                }
982            }
983            _ => unreachable!(),
984        }
985
986        output
987    }
988
989    /// Encodes `input` into `output`, returning the number of bytes written.
990    pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
991        let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
992        if output.len() < required {
993            return Err(EncodeError::OutputTooSmall {
994                required,
995                available: output.len(),
996            });
997        }
998
999        let mut read = 0;
1000        let mut write = 0;
1001        while read + 3 <= input.len() {
1002            let b0 = input[read];
1003            let b1 = input[read + 1];
1004            let b2 = input[read + 2];
1005
1006            output[write] = encode_base64_value::<A>(b0 >> 2);
1007            output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1008            output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1009            output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1010
1011            read += 3;
1012            write += 4;
1013        }
1014
1015        match input.len() - read {
1016            0 => {}
1017            1 => {
1018                let b0 = input[read];
1019                output[write] = encode_base64_value::<A>(b0 >> 2);
1020                output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1021                write += 2;
1022                if PAD {
1023                    output[write] = b'=';
1024                    output[write + 1] = b'=';
1025                    write += 2;
1026                }
1027            }
1028            2 => {
1029                let b0 = input[read];
1030                let b1 = input[read + 1];
1031                output[write] = encode_base64_value::<A>(b0 >> 2);
1032                output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1033                output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1034                write += 3;
1035                if PAD {
1036                    output[write] = b'=';
1037                    write += 1;
1038                }
1039            }
1040            _ => unreachable!(),
1041        }
1042
1043        Ok(write)
1044    }
1045
1046    /// Encodes `input` into a newly allocated byte vector.
1047    #[cfg(feature = "alloc")]
1048    pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
1049        let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1050        let mut output = alloc::vec![0; required];
1051        let written = self.encode_slice(input, &mut output)?;
1052        output.truncate(written);
1053        Ok(output)
1054    }
1055
1056    /// Encodes `input` into a newly allocated UTF-8 string.
1057    ///
1058    /// Base64 output is ASCII by construction. This helper is available with
1059    /// the `alloc` feature and has the same encoding semantics as
1060    /// [`Self::encode_slice`].
1061    ///
1062    /// # Examples
1063    ///
1064    /// ```
1065    /// use base64_ng::{STANDARD, URL_SAFE_NO_PAD};
1066    ///
1067    /// assert_eq!(STANDARD.encode_string(b"hello").unwrap(), "aGVsbG8=");
1068    /// assert_eq!(URL_SAFE_NO_PAD.encode_string(b"\xfb\xff").unwrap(), "-_8");
1069    /// ```
1070    #[cfg(feature = "alloc")]
1071    pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
1072        let output = self.encode_vec(input)?;
1073        match alloc::string::String::from_utf8(output) {
1074            Ok(output) => Ok(output),
1075            Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1076        }
1077    }
1078
1079    /// Encodes the first `input_len` bytes of `buffer` in place.
1080    ///
1081    /// The buffer must have enough spare capacity for the encoded output. The
1082    /// implementation writes from right to left, so unread input bytes are not
1083    /// overwritten before they are encoded.
1084    ///
1085    /// # Examples
1086    ///
1087    /// ```
1088    /// use base64_ng::STANDARD;
1089    ///
1090    /// let mut buffer = [0u8; 8];
1091    /// buffer[..5].copy_from_slice(b"hello");
1092    /// let encoded = STANDARD.encode_in_place(&mut buffer, 5).unwrap();
1093    /// assert_eq!(encoded, b"aGVsbG8=");
1094    /// ```
1095    pub fn encode_in_place<'a>(
1096        &self,
1097        buffer: &'a mut [u8],
1098        input_len: usize,
1099    ) -> Result<&'a mut [u8], EncodeError> {
1100        if input_len > buffer.len() {
1101            return Err(EncodeError::InputTooLarge {
1102                input_len,
1103                buffer_len: buffer.len(),
1104            });
1105        }
1106
1107        let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
1108        if buffer.len() < required {
1109            return Err(EncodeError::OutputTooSmall {
1110                required,
1111                available: buffer.len(),
1112            });
1113        }
1114
1115        let mut read = input_len;
1116        let mut write = required;
1117
1118        match input_len % 3 {
1119            0 => {}
1120            1 => {
1121                read -= 1;
1122                let b0 = buffer[read];
1123                if PAD {
1124                    write -= 4;
1125                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1126                    buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1127                    buffer[write + 2] = b'=';
1128                    buffer[write + 3] = b'=';
1129                } else {
1130                    write -= 2;
1131                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1132                    buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1133                }
1134            }
1135            2 => {
1136                read -= 2;
1137                let b0 = buffer[read];
1138                let b1 = buffer[read + 1];
1139                if PAD {
1140                    write -= 4;
1141                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1142                    buffer[write + 1] =
1143                        encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1144                    buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1145                    buffer[write + 3] = b'=';
1146                } else {
1147                    write -= 3;
1148                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1149                    buffer[write + 1] =
1150                        encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1151                    buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1152                }
1153            }
1154            _ => unreachable!(),
1155        }
1156
1157        while read > 0 {
1158            read -= 3;
1159            write -= 4;
1160            let b0 = buffer[read];
1161            let b1 = buffer[read + 1];
1162            let b2 = buffer[read + 2];
1163
1164            buffer[write] = encode_base64_value::<A>(b0 >> 2);
1165            buffer[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1166            buffer[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1167            buffer[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1168        }
1169
1170        debug_assert_eq!(write, 0);
1171        Ok(&mut buffer[..required])
1172    }
1173
1174    /// Decodes `input` into `output`, returning the number of bytes written.
1175    ///
1176    /// This is strict decoding. Whitespace, mixed alphabets, malformed padding,
1177    /// and trailing non-padding data are rejected.
1178    pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1179        if input.is_empty() {
1180            return Ok(0);
1181        }
1182
1183        if PAD {
1184            decode_padded::<A>(input, output)
1185        } else {
1186            decode_unpadded::<A>(input, output)
1187        }
1188    }
1189
1190    /// Decodes `input` using the explicit legacy whitespace profile.
1191    ///
1192    /// ASCII space, tab, carriage return, and line feed bytes are ignored.
1193    /// Alphabet selection, padding placement, trailing data after padding, and
1194    /// non-canonical trailing bits remain strict.
1195    pub fn decode_slice_legacy(
1196        &self,
1197        input: &[u8],
1198        output: &mut [u8],
1199    ) -> Result<usize, DecodeError> {
1200        let required = validate_legacy_decode::<A, PAD>(input)?;
1201        if output.len() < required {
1202            return Err(DecodeError::OutputTooSmall {
1203                required,
1204                available: output.len(),
1205            });
1206        }
1207        decode_legacy_to_slice::<A, PAD>(input, output)
1208    }
1209
1210    /// Decodes `input` into a newly allocated byte vector.
1211    ///
1212    /// This is strict decoding with the same semantics as [`Self::decode_slice`].
1213    #[cfg(feature = "alloc")]
1214    pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1215        let required = validate_decode::<A, PAD>(input)?;
1216        let mut output = alloc::vec![0; required];
1217        let written = match self.decode_slice(input, &mut output) {
1218            Ok(written) => written,
1219            Err(err) => {
1220                output.fill(0);
1221                return Err(err);
1222            }
1223        };
1224        output.truncate(written);
1225        Ok(output)
1226    }
1227
1228    /// Decodes `input` into a newly allocated byte vector using the explicit
1229    /// legacy whitespace profile.
1230    #[cfg(feature = "alloc")]
1231    pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1232        let required = validate_legacy_decode::<A, PAD>(input)?;
1233        let mut output = alloc::vec![0; required];
1234        let written = match self.decode_slice_legacy(input, &mut output) {
1235            Ok(written) => written,
1236            Err(err) => {
1237                output.fill(0);
1238                return Err(err);
1239            }
1240        };
1241        output.truncate(written);
1242        Ok(output)
1243    }
1244
1245    /// Decodes the buffer in place and returns the decoded prefix.
1246    ///
1247    /// # Examples
1248    ///
1249    /// ```
1250    /// use base64_ng::STANDARD_NO_PAD;
1251    ///
1252    /// let mut buffer = *b"Zm9vYmFy";
1253    /// let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
1254    /// assert_eq!(decoded, b"foobar");
1255    /// ```
1256    pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
1257        let len = Self::decode_slice_to_start(buffer)?;
1258        Ok(&mut buffer[..len])
1259    }
1260
1261    /// Decodes `buffer` in place using the explicit legacy whitespace profile.
1262    ///
1263    /// Ignored whitespace is compacted out before decoding. If validation
1264    /// fails, the buffer contents are unspecified.
1265    pub fn decode_in_place_legacy<'a>(
1266        &self,
1267        buffer: &'a mut [u8],
1268    ) -> Result<&'a mut [u8], DecodeError> {
1269        let _required = validate_legacy_decode::<A, PAD>(buffer)?;
1270        let mut write = 0;
1271        let mut read = 0;
1272        while read < buffer.len() {
1273            let byte = buffer[read];
1274            if !is_legacy_whitespace(byte) {
1275                buffer[write] = byte;
1276                write += 1;
1277            }
1278            read += 1;
1279        }
1280        let len = Self::decode_slice_to_start(&mut buffer[..write])?;
1281        Ok(&mut buffer[..len])
1282    }
1283
1284    fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
1285        let input_len = buffer.len();
1286        let mut read = 0;
1287        let mut write = 0;
1288        while read + 4 <= input_len {
1289            let chunk = [
1290                buffer[read],
1291                buffer[read + 1],
1292                buffer[read + 2],
1293                buffer[read + 3],
1294            ];
1295            let written = decode_chunk::<A, PAD>(&chunk, &mut buffer[write..])
1296                .map_err(|err| err.with_index_offset(read))?;
1297            read += 4;
1298            write += written;
1299            if written < 3 {
1300                if read != input_len {
1301                    return Err(DecodeError::InvalidPadding { index: read - 4 });
1302                }
1303                return Ok(write);
1304            }
1305        }
1306
1307        let rem = input_len - read;
1308        if rem == 0 {
1309            return Ok(write);
1310        }
1311        if PAD {
1312            return Err(DecodeError::InvalidLength);
1313        }
1314        let mut tail = [0u8; 3];
1315        tail[..rem].copy_from_slice(&buffer[read..input_len]);
1316        decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
1317            .map_err(|err| err.with_index_offset(read))
1318            .map(|n| write + n)
1319    }
1320}
1321
1322/// Encoding error.
1323#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1324pub enum EncodeError {
1325    /// The encoded output length would overflow `usize`.
1326    LengthOverflow,
1327    /// The caller-provided input length exceeds the provided buffer.
1328    InputTooLarge {
1329        /// Requested input bytes.
1330        input_len: usize,
1331        /// Available buffer bytes.
1332        buffer_len: usize,
1333    },
1334    /// The output buffer is too small.
1335    OutputTooSmall {
1336        /// Required output bytes.
1337        required: usize,
1338        /// Available output bytes.
1339        available: usize,
1340    },
1341}
1342
1343impl core::fmt::Display for EncodeError {
1344    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1345        match self {
1346            Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
1347            Self::InputTooLarge {
1348                input_len,
1349                buffer_len,
1350            } => write!(
1351                f,
1352                "base64 input length {input_len} exceeds buffer length {buffer_len}"
1353            ),
1354            Self::OutputTooSmall {
1355                required,
1356                available,
1357            } => write!(
1358                f,
1359                "base64 output buffer too small: required {required}, available {available}"
1360            ),
1361        }
1362    }
1363}
1364
1365#[cfg(feature = "std")]
1366impl std::error::Error for EncodeError {}
1367
1368/// Decoding error.
1369#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1370pub enum DecodeError {
1371    /// The encoded input length is impossible for the selected padding policy.
1372    InvalidLength,
1373    /// A byte is not valid for the selected alphabet.
1374    InvalidByte {
1375        /// Byte index in the input.
1376        index: usize,
1377        /// Invalid byte value.
1378        byte: u8,
1379    },
1380    /// Padding is missing, misplaced, or non-canonical.
1381    InvalidPadding {
1382        /// Byte index where padding became invalid.
1383        index: usize,
1384    },
1385    /// The output buffer is too small.
1386    OutputTooSmall {
1387        /// Required output bytes.
1388        required: usize,
1389        /// Available output bytes.
1390        available: usize,
1391    },
1392}
1393
1394impl core::fmt::Display for DecodeError {
1395    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1396        match self {
1397            Self::InvalidLength => f.write_str("invalid base64 input length"),
1398            Self::InvalidByte { index, byte } => {
1399                write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
1400            }
1401            Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
1402            Self::OutputTooSmall {
1403                required,
1404                available,
1405            } => write!(
1406                f,
1407                "base64 decode output buffer too small: required {required}, available {available}"
1408            ),
1409        }
1410    }
1411}
1412
1413impl DecodeError {
1414    fn with_index_offset(self, offset: usize) -> Self {
1415        match self {
1416            Self::InvalidByte { index, byte } => Self::InvalidByte {
1417                index: index + offset,
1418                byte,
1419            },
1420            Self::InvalidPadding { index } => Self::InvalidPadding {
1421                index: index + offset,
1422            },
1423            Self::InvalidLength | Self::OutputTooSmall { .. } => self,
1424        }
1425    }
1426}
1427
1428#[cfg(feature = "std")]
1429impl std::error::Error for DecodeError {}
1430
1431fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
1432    input: &[u8],
1433) -> Result<usize, DecodeError> {
1434    let mut chunk = [0u8; 4];
1435    let mut indexes = [0usize; 4];
1436    let mut chunk_len = 0;
1437    let mut required = 0;
1438    let mut terminal_seen = false;
1439
1440    for (index, byte) in input.iter().copied().enumerate() {
1441        if is_legacy_whitespace(byte) {
1442            continue;
1443        }
1444        if terminal_seen {
1445            return Err(DecodeError::InvalidPadding { index });
1446        }
1447
1448        chunk[chunk_len] = byte;
1449        indexes[chunk_len] = index;
1450        chunk_len += 1;
1451
1452        if chunk_len == 4 {
1453            let written =
1454                validate_chunk::<A, PAD>(&chunk).map_err(|err| map_chunk_error(err, &indexes))?;
1455            required += written;
1456            terminal_seen = written < 3;
1457            chunk_len = 0;
1458        }
1459    }
1460
1461    if chunk_len == 0 {
1462        return Ok(required);
1463    }
1464    if PAD {
1465        return Err(DecodeError::InvalidLength);
1466    }
1467
1468    validate_tail_unpadded::<A>(&chunk[..chunk_len])
1469        .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
1470    Ok(required + decoded_capacity(chunk_len))
1471}
1472
1473fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
1474    input: &[u8],
1475    output: &mut [u8],
1476) -> Result<usize, DecodeError> {
1477    let mut chunk = [0u8; 4];
1478    let mut indexes = [0usize; 4];
1479    let mut chunk_len = 0;
1480    let mut write = 0;
1481    let mut terminal_seen = false;
1482
1483    for (index, byte) in input.iter().copied().enumerate() {
1484        if is_legacy_whitespace(byte) {
1485            continue;
1486        }
1487        if terminal_seen {
1488            return Err(DecodeError::InvalidPadding { index });
1489        }
1490
1491        chunk[chunk_len] = byte;
1492        indexes[chunk_len] = index;
1493        chunk_len += 1;
1494
1495        if chunk_len == 4 {
1496            let written = decode_chunk::<A, PAD>(&chunk, &mut output[write..])
1497                .map_err(|err| map_chunk_error(err, &indexes))?;
1498            write += written;
1499            terminal_seen = written < 3;
1500            chunk_len = 0;
1501        }
1502    }
1503
1504    if chunk_len == 0 {
1505        return Ok(write);
1506    }
1507    if PAD {
1508        return Err(DecodeError::InvalidLength);
1509    }
1510
1511    decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
1512        .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
1513        .map(|n| write + n)
1514}
1515
1516#[inline]
1517const fn is_legacy_whitespace(byte: u8) -> bool {
1518    matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
1519}
1520
1521fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
1522    match err {
1523        DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
1524            index: indexes[index],
1525            byte,
1526        },
1527        DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
1528            index: indexes[index],
1529        },
1530        DecodeError::InvalidLength | DecodeError::OutputTooSmall { .. } => err,
1531    }
1532}
1533
1534fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
1535    match err {
1536        DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
1537            index: indexes[index],
1538            byte,
1539        },
1540        DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
1541            index: indexes[index],
1542        },
1543        DecodeError::InvalidByte { .. }
1544        | DecodeError::InvalidPadding { .. }
1545        | DecodeError::InvalidLength
1546        | DecodeError::OutputTooSmall { .. } => err,
1547    }
1548}
1549
1550fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1551    if !input.len().is_multiple_of(4) {
1552        return Err(DecodeError::InvalidLength);
1553    }
1554    let required = decoded_len_padded(input)?;
1555    if output.len() < required {
1556        return Err(DecodeError::OutputTooSmall {
1557            required,
1558            available: output.len(),
1559        });
1560    }
1561
1562    let mut read = 0;
1563    let mut write = 0;
1564    while read < input.len() {
1565        let written = decode_chunk::<A, true>(&input[read..read + 4], &mut output[write..])
1566            .map_err(|err| err.with_index_offset(read))?;
1567        read += 4;
1568        write += written;
1569        if written < 3 && read != input.len() {
1570            return Err(DecodeError::InvalidPadding { index: read - 4 });
1571        }
1572    }
1573    Ok(write)
1574}
1575
1576#[cfg(feature = "alloc")]
1577fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
1578    if input.is_empty() {
1579        return Ok(0);
1580    }
1581
1582    if PAD {
1583        validate_padded::<A>(input)
1584    } else {
1585        validate_unpadded::<A>(input)
1586    }
1587}
1588
1589#[cfg(feature = "alloc")]
1590fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
1591    if !input.len().is_multiple_of(4) {
1592        return Err(DecodeError::InvalidLength);
1593    }
1594    let required = decoded_len_padded(input)?;
1595
1596    let mut read = 0;
1597    while read < input.len() {
1598        let written = validate_chunk::<A, true>(&input[read..read + 4])
1599            .map_err(|err| err.with_index_offset(read))?;
1600        read += 4;
1601        if written < 3 && read != input.len() {
1602            return Err(DecodeError::InvalidPadding { index: read - 4 });
1603        }
1604    }
1605
1606    Ok(required)
1607}
1608
1609#[cfg(feature = "alloc")]
1610fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
1611    let required = decoded_len_unpadded(input)?;
1612
1613    let mut read = 0;
1614    while read + 4 <= input.len() {
1615        validate_chunk::<A, false>(&input[read..read + 4])
1616            .map_err(|err| err.with_index_offset(read))?;
1617        read += 4;
1618    }
1619    validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
1620
1621    Ok(required)
1622}
1623
1624fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1625    let required = decoded_len_unpadded(input)?;
1626    if output.len() < required {
1627        return Err(DecodeError::OutputTooSmall {
1628            required,
1629            available: output.len(),
1630        });
1631    }
1632
1633    let mut read = 0;
1634    let mut write = 0;
1635    while read + 4 <= input.len() {
1636        let written = decode_chunk::<A, false>(&input[read..read + 4], &mut output[write..])
1637            .map_err(|err| err.with_index_offset(read))?;
1638        read += 4;
1639        write += written;
1640    }
1641    decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
1642        .map_err(|err| err.with_index_offset(read))
1643        .map(|n| write + n)
1644}
1645
1646fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
1647    if input.is_empty() {
1648        return Ok(0);
1649    }
1650    if !input.len().is_multiple_of(4) {
1651        return Err(DecodeError::InvalidLength);
1652    }
1653    let mut padding = 0;
1654    if input[input.len() - 1] == b'=' {
1655        padding += 1;
1656    }
1657    if input[input.len() - 2] == b'=' {
1658        padding += 1;
1659    }
1660    if padding == 0
1661        && let Some(index) = input.iter().position(|byte| *byte == b'=')
1662    {
1663        return Err(DecodeError::InvalidPadding { index });
1664    }
1665    if padding > 0 {
1666        let first_pad = input.len() - padding;
1667        if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
1668            return Err(DecodeError::InvalidPadding { index });
1669        }
1670    }
1671    Ok(input.len() / 4 * 3 - padding)
1672}
1673
1674fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
1675    if input.len() % 4 == 1 {
1676        return Err(DecodeError::InvalidLength);
1677    }
1678    if let Some(index) = input.iter().position(|byte| *byte == b'=') {
1679        return Err(DecodeError::InvalidPadding { index });
1680    }
1681    Ok(decoded_capacity(input.len()))
1682}
1683
1684fn validate_chunk<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
1685    debug_assert_eq!(input.len(), 4);
1686    let _v0 = decode_byte::<A>(input[0], 0)?;
1687    let v1 = decode_byte::<A>(input[1], 1)?;
1688
1689    match (input[2], input[3]) {
1690        (b'=', b'=') if PAD => {
1691            if v1 & 0b0000_1111 != 0 {
1692                return Err(DecodeError::InvalidPadding { index: 1 });
1693            }
1694            Ok(1)
1695        }
1696        (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
1697        (_, b'=') if PAD => {
1698            let v2 = decode_byte::<A>(input[2], 2)?;
1699            if v2 & 0b0000_0011 != 0 {
1700                return Err(DecodeError::InvalidPadding { index: 2 });
1701            }
1702            Ok(2)
1703        }
1704        (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
1705            index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
1706        }),
1707        _ => {
1708            decode_byte::<A>(input[2], 2)?;
1709            decode_byte::<A>(input[3], 3)?;
1710            Ok(3)
1711        }
1712    }
1713}
1714
1715fn decode_chunk<A: Alphabet, const PAD: bool>(
1716    input: &[u8],
1717    output: &mut [u8],
1718) -> Result<usize, DecodeError> {
1719    debug_assert_eq!(input.len(), 4);
1720    let v0 = decode_byte::<A>(input[0], 0)?;
1721    let v1 = decode_byte::<A>(input[1], 1)?;
1722
1723    match (input[2], input[3]) {
1724        (b'=', b'=') if PAD => {
1725            if output.is_empty() {
1726                return Err(DecodeError::OutputTooSmall {
1727                    required: 1,
1728                    available: output.len(),
1729                });
1730            }
1731            if v1 & 0b0000_1111 != 0 {
1732                return Err(DecodeError::InvalidPadding { index: 1 });
1733            }
1734            output[0] = (v0 << 2) | (v1 >> 4);
1735            Ok(1)
1736        }
1737        (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
1738        (_, b'=') if PAD => {
1739            if output.len() < 2 {
1740                return Err(DecodeError::OutputTooSmall {
1741                    required: 2,
1742                    available: output.len(),
1743                });
1744            }
1745            let v2 = decode_byte::<A>(input[2], 2)?;
1746            if v2 & 0b0000_0011 != 0 {
1747                return Err(DecodeError::InvalidPadding { index: 2 });
1748            }
1749            output[0] = (v0 << 2) | (v1 >> 4);
1750            output[1] = (v1 << 4) | (v2 >> 2);
1751            Ok(2)
1752        }
1753        (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
1754            index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
1755        }),
1756        _ => {
1757            if output.len() < 3 {
1758                return Err(DecodeError::OutputTooSmall {
1759                    required: 3,
1760                    available: output.len(),
1761                });
1762            }
1763            let v2 = decode_byte::<A>(input[2], 2)?;
1764            let v3 = decode_byte::<A>(input[3], 3)?;
1765            output[0] = (v0 << 2) | (v1 >> 4);
1766            output[1] = (v1 << 4) | (v2 >> 2);
1767            output[2] = (v2 << 6) | v3;
1768            Ok(3)
1769        }
1770    }
1771}
1772
1773fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
1774    match input.len() {
1775        0 => Ok(()),
1776        2 => {
1777            decode_byte::<A>(input[0], 0)?;
1778            let v1 = decode_byte::<A>(input[1], 1)?;
1779            if v1 & 0b0000_1111 != 0 {
1780                return Err(DecodeError::InvalidPadding { index: 1 });
1781            }
1782            Ok(())
1783        }
1784        3 => {
1785            decode_byte::<A>(input[0], 0)?;
1786            decode_byte::<A>(input[1], 1)?;
1787            let v2 = decode_byte::<A>(input[2], 2)?;
1788            if v2 & 0b0000_0011 != 0 {
1789                return Err(DecodeError::InvalidPadding { index: 2 });
1790            }
1791            Ok(())
1792        }
1793        _ => Err(DecodeError::InvalidLength),
1794    }
1795}
1796
1797fn decode_tail_unpadded<A: Alphabet>(
1798    input: &[u8],
1799    output: &mut [u8],
1800) -> Result<usize, DecodeError> {
1801    match input.len() {
1802        0 => Ok(0),
1803        2 => {
1804            if output.is_empty() {
1805                return Err(DecodeError::OutputTooSmall {
1806                    required: 1,
1807                    available: output.len(),
1808                });
1809            }
1810            let v0 = decode_byte::<A>(input[0], 0)?;
1811            let v1 = decode_byte::<A>(input[1], 1)?;
1812            if v1 & 0b0000_1111 != 0 {
1813                return Err(DecodeError::InvalidPadding { index: 1 });
1814            }
1815            output[0] = (v0 << 2) | (v1 >> 4);
1816            Ok(1)
1817        }
1818        3 => {
1819            if output.len() < 2 {
1820                return Err(DecodeError::OutputTooSmall {
1821                    required: 2,
1822                    available: output.len(),
1823                });
1824            }
1825            let v0 = decode_byte::<A>(input[0], 0)?;
1826            let v1 = decode_byte::<A>(input[1], 1)?;
1827            let v2 = decode_byte::<A>(input[2], 2)?;
1828            if v2 & 0b0000_0011 != 0 {
1829                return Err(DecodeError::InvalidPadding { index: 2 });
1830            }
1831            output[0] = (v0 << 2) | (v1 >> 4);
1832            output[1] = (v1 << 4) | (v2 >> 2);
1833            Ok(2)
1834        }
1835        _ => Err(DecodeError::InvalidLength),
1836    }
1837}
1838
1839fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
1840    A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
1841}
1842
1843#[cfg(test)]
1844mod tests {
1845    use super::*;
1846
1847    #[test]
1848    fn encodes_standard_vectors() {
1849        let vectors = [
1850            (&b""[..], &b""[..]),
1851            (&b"f"[..], &b"Zg=="[..]),
1852            (&b"fo"[..], &b"Zm8="[..]),
1853            (&b"foo"[..], &b"Zm9v"[..]),
1854            (&b"foob"[..], &b"Zm9vYg=="[..]),
1855            (&b"fooba"[..], &b"Zm9vYmE="[..]),
1856            (&b"foobar"[..], &b"Zm9vYmFy"[..]),
1857        ];
1858        for (input, expected) in vectors {
1859            let mut output = [0u8; 16];
1860            let written = STANDARD.encode_slice(input, &mut output).unwrap();
1861            assert_eq!(&output[..written], expected);
1862        }
1863    }
1864
1865    #[test]
1866    fn decodes_standard_vectors() {
1867        let vectors = [
1868            (&b""[..], &b""[..]),
1869            (&b"Zg=="[..], &b"f"[..]),
1870            (&b"Zm8="[..], &b"fo"[..]),
1871            (&b"Zm9v"[..], &b"foo"[..]),
1872            (&b"Zm9vYg=="[..], &b"foob"[..]),
1873            (&b"Zm9vYmE="[..], &b"fooba"[..]),
1874            (&b"Zm9vYmFy"[..], &b"foobar"[..]),
1875        ];
1876        for (input, expected) in vectors {
1877            let mut output = [0u8; 16];
1878            let written = STANDARD.decode_slice(input, &mut output).unwrap();
1879            assert_eq!(&output[..written], expected);
1880        }
1881    }
1882
1883    #[test]
1884    fn supports_unpadded_url_safe() {
1885        let mut encoded = [0u8; 16];
1886        let written = URL_SAFE_NO_PAD
1887            .encode_slice(b"\xfb\xff", &mut encoded)
1888            .unwrap();
1889        assert_eq!(&encoded[..written], b"-_8");
1890
1891        let mut decoded = [0u8; 2];
1892        let written = URL_SAFE_NO_PAD
1893            .decode_slice(&encoded[..written], &mut decoded)
1894            .unwrap();
1895        assert_eq!(&decoded[..written], b"\xfb\xff");
1896    }
1897
1898    #[test]
1899    fn decodes_in_place() {
1900        let mut buffer = *b"Zm9vYmFy";
1901        let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
1902        assert_eq!(decoded, b"foobar");
1903    }
1904
1905    #[test]
1906    fn rejects_non_canonical_padding_bits() {
1907        let mut output = [0u8; 4];
1908        assert_eq!(
1909            STANDARD.decode_slice(b"Zh==", &mut output),
1910            Err(DecodeError::InvalidPadding { index: 1 })
1911        );
1912        assert_eq!(
1913            STANDARD.decode_slice(b"Zm9=", &mut output),
1914            Err(DecodeError::InvalidPadding { index: 2 })
1915        );
1916    }
1917}