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    /// Encodes a fixed-size input into a fixed-size output array in const contexts.
892    ///
893    /// Stable Rust does not yet allow this API to return an array whose length
894    /// is computed from `INPUT_LEN` directly. Instead, the caller supplies the
895    /// output length through the destination type and this function panics
896    /// during const evaluation if the length is wrong.
897    ///
898    /// # Panics
899    ///
900    /// Panics if `OUTPUT_LEN` is not exactly the encoded length for `INPUT_LEN`
901    /// and this engine's padding policy, or if that length overflows `usize`.
902    ///
903    /// # Examples
904    ///
905    /// ```
906    /// use base64_ng::{STANDARD, URL_SAFE_NO_PAD};
907    ///
908    /// const HELLO: [u8; 8] = STANDARD.encode_array(b"hello");
909    /// const URL_SAFE: [u8; 3] = URL_SAFE_NO_PAD.encode_array(b"\xfb\xff");
910    ///
911    /// assert_eq!(&HELLO, b"aGVsbG8=");
912    /// assert_eq!(&URL_SAFE, b"-_8");
913    /// ```
914    ///
915    /// Incorrect output lengths fail during const evaluation:
916    ///
917    /// ```compile_fail
918    /// use base64_ng::STANDARD;
919    ///
920    /// const TOO_SHORT: [u8; 7] = STANDARD.encode_array(b"hello");
921    /// ```
922    #[must_use]
923    pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
924        &self,
925        input: &[u8; INPUT_LEN],
926    ) -> [u8; OUTPUT_LEN] {
927        let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
928            panic!("encoded base64 length overflows usize");
929        };
930        assert!(
931            required == OUTPUT_LEN,
932            "base64 output array has incorrect length"
933        );
934
935        let mut output = [0u8; OUTPUT_LEN];
936        let mut read = 0;
937        let mut write = 0;
938        while INPUT_LEN - read >= 3 {
939            let b0 = input[read];
940            let b1 = input[read + 1];
941            let b2 = input[read + 2];
942
943            output[write] = encode_base64_value::<A>(b0 >> 2);
944            output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
945            output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
946            output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
947
948            read += 3;
949            write += 4;
950        }
951
952        match INPUT_LEN - read {
953            0 => {}
954            1 => {
955                let b0 = input[read];
956                output[write] = encode_base64_value::<A>(b0 >> 2);
957                output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
958                write += 2;
959                if PAD {
960                    output[write] = b'=';
961                    output[write + 1] = b'=';
962                }
963            }
964            2 => {
965                let b0 = input[read];
966                let b1 = input[read + 1];
967                output[write] = encode_base64_value::<A>(b0 >> 2);
968                output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
969                output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
970                if PAD {
971                    output[write + 3] = b'=';
972                }
973            }
974            _ => unreachable!(),
975        }
976
977        output
978    }
979
980    /// Encodes `input` into `output`, returning the number of bytes written.
981    pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
982        let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
983        if output.len() < required {
984            return Err(EncodeError::OutputTooSmall {
985                required,
986                available: output.len(),
987            });
988        }
989
990        let mut read = 0;
991        let mut write = 0;
992        while read + 3 <= input.len() {
993            let b0 = input[read];
994            let b1 = input[read + 1];
995            let b2 = input[read + 2];
996
997            output[write] = encode_base64_value::<A>(b0 >> 2);
998            output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
999            output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1000            output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1001
1002            read += 3;
1003            write += 4;
1004        }
1005
1006        match input.len() - read {
1007            0 => {}
1008            1 => {
1009                let b0 = input[read];
1010                output[write] = encode_base64_value::<A>(b0 >> 2);
1011                output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1012                write += 2;
1013                if PAD {
1014                    output[write] = b'=';
1015                    output[write + 1] = b'=';
1016                    write += 2;
1017                }
1018            }
1019            2 => {
1020                let b0 = input[read];
1021                let b1 = input[read + 1];
1022                output[write] = encode_base64_value::<A>(b0 >> 2);
1023                output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1024                output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1025                write += 3;
1026                if PAD {
1027                    output[write] = b'=';
1028                    write += 1;
1029                }
1030            }
1031            _ => unreachable!(),
1032        }
1033
1034        Ok(write)
1035    }
1036
1037    /// Encodes `input` into a newly allocated byte vector.
1038    #[cfg(feature = "alloc")]
1039    pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
1040        let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
1041        let mut output = alloc::vec![0; required];
1042        let written = self.encode_slice(input, &mut output)?;
1043        output.truncate(written);
1044        Ok(output)
1045    }
1046
1047    /// Encodes `input` into a newly allocated UTF-8 string.
1048    ///
1049    /// Base64 output is ASCII by construction. This helper is available with
1050    /// the `alloc` feature and has the same encoding semantics as
1051    /// [`Self::encode_slice`].
1052    ///
1053    /// # Examples
1054    ///
1055    /// ```
1056    /// use base64_ng::{STANDARD, URL_SAFE_NO_PAD};
1057    ///
1058    /// assert_eq!(STANDARD.encode_string(b"hello").unwrap(), "aGVsbG8=");
1059    /// assert_eq!(URL_SAFE_NO_PAD.encode_string(b"\xfb\xff").unwrap(), "-_8");
1060    /// ```
1061    #[cfg(feature = "alloc")]
1062    pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
1063        let output = self.encode_vec(input)?;
1064        match alloc::string::String::from_utf8(output) {
1065            Ok(output) => Ok(output),
1066            Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1067        }
1068    }
1069
1070    /// Encodes the first `input_len` bytes of `buffer` in place.
1071    ///
1072    /// The buffer must have enough spare capacity for the encoded output. The
1073    /// implementation writes from right to left, so unread input bytes are not
1074    /// overwritten before they are encoded.
1075    ///
1076    /// # Examples
1077    ///
1078    /// ```
1079    /// use base64_ng::STANDARD;
1080    ///
1081    /// let mut buffer = [0u8; 8];
1082    /// buffer[..5].copy_from_slice(b"hello");
1083    /// let encoded = STANDARD.encode_in_place(&mut buffer, 5).unwrap();
1084    /// assert_eq!(encoded, b"aGVsbG8=");
1085    /// ```
1086    pub fn encode_in_place<'a>(
1087        &self,
1088        buffer: &'a mut [u8],
1089        input_len: usize,
1090    ) -> Result<&'a mut [u8], EncodeError> {
1091        if input_len > buffer.len() {
1092            return Err(EncodeError::InputTooLarge {
1093                input_len,
1094                buffer_len: buffer.len(),
1095            });
1096        }
1097
1098        let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
1099        if buffer.len() < required {
1100            return Err(EncodeError::OutputTooSmall {
1101                required,
1102                available: buffer.len(),
1103            });
1104        }
1105
1106        let mut read = input_len;
1107        let mut write = required;
1108
1109        match input_len % 3 {
1110            0 => {}
1111            1 => {
1112                read -= 1;
1113                let b0 = buffer[read];
1114                if PAD {
1115                    write -= 4;
1116                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1117                    buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1118                    buffer[write + 2] = b'=';
1119                    buffer[write + 3] = b'=';
1120                } else {
1121                    write -= 2;
1122                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1123                    buffer[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
1124                }
1125            }
1126            2 => {
1127                read -= 2;
1128                let b0 = buffer[read];
1129                let b1 = buffer[read + 1];
1130                if PAD {
1131                    write -= 4;
1132                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1133                    buffer[write + 1] =
1134                        encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1135                    buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1136                    buffer[write + 3] = b'=';
1137                } else {
1138                    write -= 3;
1139                    buffer[write] = encode_base64_value::<A>(b0 >> 2);
1140                    buffer[write + 1] =
1141                        encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1142                    buffer[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
1143                }
1144            }
1145            _ => unreachable!(),
1146        }
1147
1148        while read > 0 {
1149            read -= 3;
1150            write -= 4;
1151            let b0 = buffer[read];
1152            let b1 = buffer[read + 1];
1153            let b2 = buffer[read + 2];
1154
1155            buffer[write] = encode_base64_value::<A>(b0 >> 2);
1156            buffer[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
1157            buffer[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
1158            buffer[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
1159        }
1160
1161        debug_assert_eq!(write, 0);
1162        Ok(&mut buffer[..required])
1163    }
1164
1165    /// Decodes `input` into `output`, returning the number of bytes written.
1166    ///
1167    /// This is strict decoding. Whitespace, mixed alphabets, malformed padding,
1168    /// and trailing non-padding data are rejected.
1169    pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1170        if input.is_empty() {
1171            return Ok(0);
1172        }
1173
1174        if PAD {
1175            decode_padded::<A>(input, output)
1176        } else {
1177            decode_unpadded::<A>(input, output)
1178        }
1179    }
1180
1181    /// Decodes `input` into a newly allocated byte vector.
1182    ///
1183    /// This is strict decoding with the same semantics as [`Self::decode_slice`].
1184    #[cfg(feature = "alloc")]
1185    pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1186        let required = validate_decode::<A, PAD>(input)?;
1187        let mut output = alloc::vec![0; required];
1188        let written = match self.decode_slice(input, &mut output) {
1189            Ok(written) => written,
1190            Err(err) => {
1191                output.fill(0);
1192                return Err(err);
1193            }
1194        };
1195        output.truncate(written);
1196        Ok(output)
1197    }
1198
1199    /// Decodes the buffer in place and returns the decoded prefix.
1200    ///
1201    /// # Examples
1202    ///
1203    /// ```
1204    /// use base64_ng::STANDARD_NO_PAD;
1205    ///
1206    /// let mut buffer = *b"Zm9vYmFy";
1207    /// let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
1208    /// assert_eq!(decoded, b"foobar");
1209    /// ```
1210    pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
1211        let len = Self::decode_slice_to_start(buffer)?;
1212        Ok(&mut buffer[..len])
1213    }
1214
1215    fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
1216        let input_len = buffer.len();
1217        let mut read = 0;
1218        let mut write = 0;
1219        while read + 4 <= input_len {
1220            let chunk = [
1221                buffer[read],
1222                buffer[read + 1],
1223                buffer[read + 2],
1224                buffer[read + 3],
1225            ];
1226            let written = decode_chunk::<A, PAD>(&chunk, &mut buffer[write..])
1227                .map_err(|err| err.with_index_offset(read))?;
1228            read += 4;
1229            write += written;
1230            if written < 3 {
1231                if read != input_len {
1232                    return Err(DecodeError::InvalidPadding { index: read - 4 });
1233                }
1234                return Ok(write);
1235            }
1236        }
1237
1238        let rem = input_len - read;
1239        if rem == 0 {
1240            return Ok(write);
1241        }
1242        if PAD {
1243            return Err(DecodeError::InvalidLength);
1244        }
1245        let mut tail = [0u8; 3];
1246        tail[..rem].copy_from_slice(&buffer[read..input_len]);
1247        decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
1248            .map_err(|err| err.with_index_offset(read))
1249            .map(|n| write + n)
1250    }
1251}
1252
1253/// Encoding error.
1254#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1255pub enum EncodeError {
1256    /// The encoded output length would overflow `usize`.
1257    LengthOverflow,
1258    /// The caller-provided input length exceeds the provided buffer.
1259    InputTooLarge {
1260        /// Requested input bytes.
1261        input_len: usize,
1262        /// Available buffer bytes.
1263        buffer_len: usize,
1264    },
1265    /// The output buffer is too small.
1266    OutputTooSmall {
1267        /// Required output bytes.
1268        required: usize,
1269        /// Available output bytes.
1270        available: usize,
1271    },
1272}
1273
1274impl core::fmt::Display for EncodeError {
1275    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1276        match self {
1277            Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
1278            Self::InputTooLarge {
1279                input_len,
1280                buffer_len,
1281            } => write!(
1282                f,
1283                "base64 input length {input_len} exceeds buffer length {buffer_len}"
1284            ),
1285            Self::OutputTooSmall {
1286                required,
1287                available,
1288            } => write!(
1289                f,
1290                "base64 output buffer too small: required {required}, available {available}"
1291            ),
1292        }
1293    }
1294}
1295
1296#[cfg(feature = "std")]
1297impl std::error::Error for EncodeError {}
1298
1299/// Decoding error.
1300#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1301pub enum DecodeError {
1302    /// The encoded input length is impossible for the selected padding policy.
1303    InvalidLength,
1304    /// A byte is not valid for the selected alphabet.
1305    InvalidByte {
1306        /// Byte index in the input.
1307        index: usize,
1308        /// Invalid byte value.
1309        byte: u8,
1310    },
1311    /// Padding is missing, misplaced, or non-canonical.
1312    InvalidPadding {
1313        /// Byte index where padding became invalid.
1314        index: usize,
1315    },
1316    /// The output buffer is too small.
1317    OutputTooSmall {
1318        /// Required output bytes.
1319        required: usize,
1320        /// Available output bytes.
1321        available: usize,
1322    },
1323}
1324
1325impl core::fmt::Display for DecodeError {
1326    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1327        match self {
1328            Self::InvalidLength => f.write_str("invalid base64 input length"),
1329            Self::InvalidByte { index, byte } => {
1330                write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
1331            }
1332            Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
1333            Self::OutputTooSmall {
1334                required,
1335                available,
1336            } => write!(
1337                f,
1338                "base64 decode output buffer too small: required {required}, available {available}"
1339            ),
1340        }
1341    }
1342}
1343
1344impl DecodeError {
1345    fn with_index_offset(self, offset: usize) -> Self {
1346        match self {
1347            Self::InvalidByte { index, byte } => Self::InvalidByte {
1348                index: index + offset,
1349                byte,
1350            },
1351            Self::InvalidPadding { index } => Self::InvalidPadding {
1352                index: index + offset,
1353            },
1354            Self::InvalidLength | Self::OutputTooSmall { .. } => self,
1355        }
1356    }
1357}
1358
1359#[cfg(feature = "std")]
1360impl std::error::Error for DecodeError {}
1361
1362fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1363    if !input.len().is_multiple_of(4) {
1364        return Err(DecodeError::InvalidLength);
1365    }
1366    let required = decoded_len_padded(input)?;
1367    if output.len() < required {
1368        return Err(DecodeError::OutputTooSmall {
1369            required,
1370            available: output.len(),
1371        });
1372    }
1373
1374    let mut read = 0;
1375    let mut write = 0;
1376    while read < input.len() {
1377        let written = decode_chunk::<A, true>(&input[read..read + 4], &mut output[write..])
1378            .map_err(|err| err.with_index_offset(read))?;
1379        read += 4;
1380        write += written;
1381        if written < 3 && read != input.len() {
1382            return Err(DecodeError::InvalidPadding { index: read - 4 });
1383        }
1384    }
1385    Ok(write)
1386}
1387
1388#[cfg(feature = "alloc")]
1389fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
1390    if input.is_empty() {
1391        return Ok(0);
1392    }
1393
1394    if PAD {
1395        validate_padded::<A>(input)
1396    } else {
1397        validate_unpadded::<A>(input)
1398    }
1399}
1400
1401#[cfg(feature = "alloc")]
1402fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
1403    if !input.len().is_multiple_of(4) {
1404        return Err(DecodeError::InvalidLength);
1405    }
1406    let required = decoded_len_padded(input)?;
1407
1408    let mut read = 0;
1409    while read < input.len() {
1410        let written = validate_chunk::<A, true>(&input[read..read + 4])
1411            .map_err(|err| err.with_index_offset(read))?;
1412        read += 4;
1413        if written < 3 && read != input.len() {
1414            return Err(DecodeError::InvalidPadding { index: read - 4 });
1415        }
1416    }
1417
1418    Ok(required)
1419}
1420
1421#[cfg(feature = "alloc")]
1422fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
1423    let required = decoded_len_unpadded(input)?;
1424
1425    let mut read = 0;
1426    while read + 4 <= input.len() {
1427        validate_chunk::<A, false>(&input[read..read + 4])
1428            .map_err(|err| err.with_index_offset(read))?;
1429        read += 4;
1430    }
1431    validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
1432
1433    Ok(required)
1434}
1435
1436fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1437    let required = decoded_len_unpadded(input)?;
1438    if output.len() < required {
1439        return Err(DecodeError::OutputTooSmall {
1440            required,
1441            available: output.len(),
1442        });
1443    }
1444
1445    let mut read = 0;
1446    let mut write = 0;
1447    while read + 4 <= input.len() {
1448        let written = decode_chunk::<A, false>(&input[read..read + 4], &mut output[write..])
1449            .map_err(|err| err.with_index_offset(read))?;
1450        read += 4;
1451        write += written;
1452    }
1453    decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
1454        .map_err(|err| err.with_index_offset(read))
1455        .map(|n| write + n)
1456}
1457
1458fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
1459    if input.is_empty() {
1460        return Ok(0);
1461    }
1462    if !input.len().is_multiple_of(4) {
1463        return Err(DecodeError::InvalidLength);
1464    }
1465    let mut padding = 0;
1466    if input[input.len() - 1] == b'=' {
1467        padding += 1;
1468    }
1469    if input[input.len() - 2] == b'=' {
1470        padding += 1;
1471    }
1472    if padding == 0
1473        && let Some(index) = input.iter().position(|byte| *byte == b'=')
1474    {
1475        return Err(DecodeError::InvalidPadding { index });
1476    }
1477    if padding > 0 {
1478        let first_pad = input.len() - padding;
1479        if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
1480            return Err(DecodeError::InvalidPadding { index });
1481        }
1482    }
1483    Ok(input.len() / 4 * 3 - padding)
1484}
1485
1486fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
1487    if input.len() % 4 == 1 {
1488        return Err(DecodeError::InvalidLength);
1489    }
1490    if let Some(index) = input.iter().position(|byte| *byte == b'=') {
1491        return Err(DecodeError::InvalidPadding { index });
1492    }
1493    Ok(decoded_capacity(input.len()))
1494}
1495
1496#[cfg(feature = "alloc")]
1497fn validate_chunk<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
1498    debug_assert_eq!(input.len(), 4);
1499    let _v0 = decode_byte::<A>(input[0], 0)?;
1500    let v1 = decode_byte::<A>(input[1], 1)?;
1501
1502    match (input[2], input[3]) {
1503        (b'=', b'=') if PAD => {
1504            if v1 & 0b0000_1111 != 0 {
1505                return Err(DecodeError::InvalidPadding { index: 1 });
1506            }
1507            Ok(1)
1508        }
1509        (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
1510        (_, b'=') if PAD => {
1511            let v2 = decode_byte::<A>(input[2], 2)?;
1512            if v2 & 0b0000_0011 != 0 {
1513                return Err(DecodeError::InvalidPadding { index: 2 });
1514            }
1515            Ok(2)
1516        }
1517        (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
1518            index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
1519        }),
1520        _ => {
1521            decode_byte::<A>(input[2], 2)?;
1522            decode_byte::<A>(input[3], 3)?;
1523            Ok(3)
1524        }
1525    }
1526}
1527
1528fn decode_chunk<A: Alphabet, const PAD: bool>(
1529    input: &[u8],
1530    output: &mut [u8],
1531) -> Result<usize, DecodeError> {
1532    debug_assert_eq!(input.len(), 4);
1533    let v0 = decode_byte::<A>(input[0], 0)?;
1534    let v1 = decode_byte::<A>(input[1], 1)?;
1535
1536    match (input[2], input[3]) {
1537        (b'=', b'=') if PAD => {
1538            if output.is_empty() {
1539                return Err(DecodeError::OutputTooSmall {
1540                    required: 1,
1541                    available: output.len(),
1542                });
1543            }
1544            if v1 & 0b0000_1111 != 0 {
1545                return Err(DecodeError::InvalidPadding { index: 1 });
1546            }
1547            output[0] = (v0 << 2) | (v1 >> 4);
1548            Ok(1)
1549        }
1550        (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
1551        (_, b'=') if PAD => {
1552            if output.len() < 2 {
1553                return Err(DecodeError::OutputTooSmall {
1554                    required: 2,
1555                    available: output.len(),
1556                });
1557            }
1558            let v2 = decode_byte::<A>(input[2], 2)?;
1559            if v2 & 0b0000_0011 != 0 {
1560                return Err(DecodeError::InvalidPadding { index: 2 });
1561            }
1562            output[0] = (v0 << 2) | (v1 >> 4);
1563            output[1] = (v1 << 4) | (v2 >> 2);
1564            Ok(2)
1565        }
1566        (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
1567            index: input.iter().position(|byte| *byte == b'=').unwrap_or(0),
1568        }),
1569        _ => {
1570            if output.len() < 3 {
1571                return Err(DecodeError::OutputTooSmall {
1572                    required: 3,
1573                    available: output.len(),
1574                });
1575            }
1576            let v2 = decode_byte::<A>(input[2], 2)?;
1577            let v3 = decode_byte::<A>(input[3], 3)?;
1578            output[0] = (v0 << 2) | (v1 >> 4);
1579            output[1] = (v1 << 4) | (v2 >> 2);
1580            output[2] = (v2 << 6) | v3;
1581            Ok(3)
1582        }
1583    }
1584}
1585
1586#[cfg(feature = "alloc")]
1587fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
1588    match input.len() {
1589        0 => Ok(()),
1590        2 => {
1591            decode_byte::<A>(input[0], 0)?;
1592            let v1 = decode_byte::<A>(input[1], 1)?;
1593            if v1 & 0b0000_1111 != 0 {
1594                return Err(DecodeError::InvalidPadding { index: 1 });
1595            }
1596            Ok(())
1597        }
1598        3 => {
1599            decode_byte::<A>(input[0], 0)?;
1600            decode_byte::<A>(input[1], 1)?;
1601            let v2 = decode_byte::<A>(input[2], 2)?;
1602            if v2 & 0b0000_0011 != 0 {
1603                return Err(DecodeError::InvalidPadding { index: 2 });
1604            }
1605            Ok(())
1606        }
1607        _ => Err(DecodeError::InvalidLength),
1608    }
1609}
1610
1611fn decode_tail_unpadded<A: Alphabet>(
1612    input: &[u8],
1613    output: &mut [u8],
1614) -> Result<usize, DecodeError> {
1615    match input.len() {
1616        0 => Ok(0),
1617        2 => {
1618            if output.is_empty() {
1619                return Err(DecodeError::OutputTooSmall {
1620                    required: 1,
1621                    available: output.len(),
1622                });
1623            }
1624            let v0 = decode_byte::<A>(input[0], 0)?;
1625            let v1 = decode_byte::<A>(input[1], 1)?;
1626            if v1 & 0b0000_1111 != 0 {
1627                return Err(DecodeError::InvalidPadding { index: 1 });
1628            }
1629            output[0] = (v0 << 2) | (v1 >> 4);
1630            Ok(1)
1631        }
1632        3 => {
1633            if output.len() < 2 {
1634                return Err(DecodeError::OutputTooSmall {
1635                    required: 2,
1636                    available: output.len(),
1637                });
1638            }
1639            let v0 = decode_byte::<A>(input[0], 0)?;
1640            let v1 = decode_byte::<A>(input[1], 1)?;
1641            let v2 = decode_byte::<A>(input[2], 2)?;
1642            if v2 & 0b0000_0011 != 0 {
1643                return Err(DecodeError::InvalidPadding { index: 2 });
1644            }
1645            output[0] = (v0 << 2) | (v1 >> 4);
1646            output[1] = (v1 << 4) | (v2 >> 2);
1647            Ok(2)
1648        }
1649        _ => Err(DecodeError::InvalidLength),
1650    }
1651}
1652
1653fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
1654    A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
1655}
1656
1657#[cfg(test)]
1658mod tests {
1659    use super::*;
1660
1661    #[test]
1662    fn encodes_standard_vectors() {
1663        let vectors = [
1664            (&b""[..], &b""[..]),
1665            (&b"f"[..], &b"Zg=="[..]),
1666            (&b"fo"[..], &b"Zm8="[..]),
1667            (&b"foo"[..], &b"Zm9v"[..]),
1668            (&b"foob"[..], &b"Zm9vYg=="[..]),
1669            (&b"fooba"[..], &b"Zm9vYmE="[..]),
1670            (&b"foobar"[..], &b"Zm9vYmFy"[..]),
1671        ];
1672        for (input, expected) in vectors {
1673            let mut output = [0u8; 16];
1674            let written = STANDARD.encode_slice(input, &mut output).unwrap();
1675            assert_eq!(&output[..written], expected);
1676        }
1677    }
1678
1679    #[test]
1680    fn decodes_standard_vectors() {
1681        let vectors = [
1682            (&b""[..], &b""[..]),
1683            (&b"Zg=="[..], &b"f"[..]),
1684            (&b"Zm8="[..], &b"fo"[..]),
1685            (&b"Zm9v"[..], &b"foo"[..]),
1686            (&b"Zm9vYg=="[..], &b"foob"[..]),
1687            (&b"Zm9vYmE="[..], &b"fooba"[..]),
1688            (&b"Zm9vYmFy"[..], &b"foobar"[..]),
1689        ];
1690        for (input, expected) in vectors {
1691            let mut output = [0u8; 16];
1692            let written = STANDARD.decode_slice(input, &mut output).unwrap();
1693            assert_eq!(&output[..written], expected);
1694        }
1695    }
1696
1697    #[test]
1698    fn supports_unpadded_url_safe() {
1699        let mut encoded = [0u8; 16];
1700        let written = URL_SAFE_NO_PAD
1701            .encode_slice(b"\xfb\xff", &mut encoded)
1702            .unwrap();
1703        assert_eq!(&encoded[..written], b"-_8");
1704
1705        let mut decoded = [0u8; 2];
1706        let written = URL_SAFE_NO_PAD
1707            .decode_slice(&encoded[..written], &mut decoded)
1708            .unwrap();
1709        assert_eq!(&decoded[..written], b"\xfb\xff");
1710    }
1711
1712    #[test]
1713    fn decodes_in_place() {
1714        let mut buffer = *b"Zm9vYmFy";
1715        let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
1716        assert_eq!(decoded, b"foobar");
1717    }
1718
1719    #[test]
1720    fn rejects_non_canonical_padding_bits() {
1721        let mut output = [0u8; 4];
1722        assert_eq!(
1723            STANDARD.decode_slice(b"Zh==", &mut output),
1724            Err(DecodeError::InvalidPadding { index: 1 })
1725        );
1726        assert_eq!(
1727            STANDARD.decode_slice(b"Zm9=", &mut output),
1728            Err(DecodeError::InvalidPadding { index: 2 })
1729        );
1730    }
1731}