base64url_nopad

base64url without padding library.
git clone https://git.philomathiclife.com/repos/base64url_nopad
Log | Files | Refs | README

lib.rs (73722B)


      1 //! [![git]](https://git.philomathiclife.com/base64url_nopad/log.html) [![crates-io]](https://crates.io/crates/base64url_nopad) [![docs-rs]](crate)
      2 //!
      3 //! [git]: https://git.philomathiclife.com/git_badge.svg
      4 //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
      5 //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
      6 //!
      7 //! `base64url_nopad` is a library for efficient and correct encoding and decoding of base64url without padding
      8 //! data. All functions that can be `const` are `const`. Great care is made to ensure _all_ arithmetic is free
      9 //! from "side effects" (e.g., overflow). `panic`s are avoided at all costs unless explicitly documented
     10 //! _including_ `panic`s related to memory allocations.
     11 //!
     12 //! ## `base64url_nopad` in action
     13 //!
     14 //! ```
     15 //! # use base64url_nopad::DecodeErr;
     16 //! /// Length of our input to encode.
     17 //! const INPUT_LEN: usize = 259;
     18 //! /// The base64url encoded value without padding of our input.
     19 //! const ENCODED_VAL: &str = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_1MH0Q";
     20 //! let mut input = [0; INPUT_LEN];
     21 //! for i in 0..=255 {
     22 //!     input[usize::from(i)] = i;
     23 //! }
     24 //! input[256] = 83;
     25 //! input[257] = 7;
     26 //! input[258] = 209;
     27 //! let mut output = [0; base64url_nopad::encode_len(INPUT_LEN)];
     28 //! assert_eq!(base64url_nopad::encode_buffer(&input, &mut output), ENCODED_VAL);
     29 //! assert!(base64url_nopad::decode_len(output.len()).is_some_and(|len| len == INPUT_LEN));
     30 //! base64url_nopad::decode_buffer(ENCODED_VAL.as_bytes(), &mut output[..INPUT_LEN])?;
     31 //! assert_eq!(input, output[..INPUT_LEN]);
     32 //! # Ok::<_, DecodeErr>(())
     33 //! ```
     34 //!
     35 //! ## Cargo "features"
     36 //!
     37 //! ### `alloc`
     38 //!
     39 //! Enables support for memory allocations via [`alloc`].
     40 //!
     41 //! ## Correctness of code
     42 //!
     43 //! This library is written in a way that is free from any overflow, underflow, or other kinds of
     44 //! "arithmetic side effects". All functions that can `panic` are explicitly documented as such; and all
     45 //! possible `panic`s are isolated to convenience functions that `panic` instead of error. Strict encoding and
     46 //! decoding is performed; thus if an input contains _any_ invalid data, it is guaranteed to fail when decoding
     47 //! it (e.g., trailing non-zero bits).
     48 #![no_std]
     49 #![cfg_attr(docsrs, feature(doc_cfg))]
     50 #[cfg(any(doc, feature = "alloc"))]
     51 extern crate alloc;
     52 #[cfg(any(doc, feature = "alloc"))]
     53 use alloc::{collections::TryReserveError, string::String, vec::Vec};
     54 use core::{
     55     error::Error,
     56     fmt::{self, Display, Formatter, Write},
     57     mem,
     58 };
     59 /// `b'A'`.
     60 const UPPER_A: u8 = b'A';
     61 /// `b'B'`.
     62 const UPPER_B: u8 = b'B';
     63 /// `b'C'`.
     64 const UPPER_C: u8 = b'C';
     65 /// `b'D'`.
     66 const UPPER_D: u8 = b'D';
     67 /// `b'E'`.
     68 const UPPER_E: u8 = b'E';
     69 /// `b'F'`.
     70 const UPPER_F: u8 = b'F';
     71 /// `b'G'`.
     72 const UPPER_G: u8 = b'G';
     73 /// `b'H'`.
     74 const UPPER_H: u8 = b'H';
     75 /// `b'I'`.
     76 const UPPER_I: u8 = b'I';
     77 /// `b'J'`.
     78 const UPPER_J: u8 = b'J';
     79 /// `b'K'`.
     80 const UPPER_K: u8 = b'K';
     81 /// `b'L'`.
     82 const UPPER_L: u8 = b'L';
     83 /// `b'M'`.
     84 const UPPER_M: u8 = b'M';
     85 /// `b'N'`.
     86 const UPPER_N: u8 = b'N';
     87 /// `b'O'`.
     88 const UPPER_O: u8 = b'O';
     89 /// `b'P'`.
     90 const UPPER_P: u8 = b'P';
     91 /// `b'Q'`.
     92 const UPPER_Q: u8 = b'Q';
     93 /// `b'R'`.
     94 const UPPER_R: u8 = b'R';
     95 /// `b'S'`.
     96 const UPPER_S: u8 = b'S';
     97 /// `b'T'`.
     98 const UPPER_T: u8 = b'T';
     99 /// `b'U'`.
    100 const UPPER_U: u8 = b'U';
    101 /// `b'V'`.
    102 const UPPER_V: u8 = b'V';
    103 /// `b'W'`.
    104 const UPPER_W: u8 = b'W';
    105 /// `b'X'`.
    106 const UPPER_X: u8 = b'X';
    107 /// `b'Y'`.
    108 const UPPER_Y: u8 = b'Y';
    109 /// `b'Z'`.
    110 const UPPER_Z: u8 = b'Z';
    111 /// `b'a'`.
    112 const LOWER_A: u8 = b'a';
    113 /// `b'b'`.
    114 const LOWER_B: u8 = b'b';
    115 /// `b'c'`.
    116 const LOWER_C: u8 = b'c';
    117 /// `b'd'`.
    118 const LOWER_D: u8 = b'd';
    119 /// `b'e'`.
    120 const LOWER_E: u8 = b'e';
    121 /// `b'f'`.
    122 const LOWER_F: u8 = b'f';
    123 /// `b'g'`.
    124 const LOWER_G: u8 = b'g';
    125 /// `b'h'`.
    126 const LOWER_H: u8 = b'h';
    127 /// `b'i'`.
    128 const LOWER_I: u8 = b'i';
    129 /// `b'j'`.
    130 const LOWER_J: u8 = b'j';
    131 /// `b'k'`.
    132 const LOWER_K: u8 = b'k';
    133 /// `b'l'`.
    134 const LOWER_L: u8 = b'l';
    135 /// `b'm'`.
    136 const LOWER_M: u8 = b'm';
    137 /// `b'n'`.
    138 const LOWER_N: u8 = b'n';
    139 /// `b'o'`.
    140 const LOWER_O: u8 = b'o';
    141 /// `b'p'`.
    142 const LOWER_P: u8 = b'p';
    143 /// `b'q'`.
    144 const LOWER_Q: u8 = b'q';
    145 /// `b'r'`.
    146 const LOWER_R: u8 = b'r';
    147 /// `b's'`.
    148 const LOWER_S: u8 = b's';
    149 /// `b't'`.
    150 const LOWER_T: u8 = b't';
    151 /// `b'u'`.
    152 const LOWER_U: u8 = b'u';
    153 /// `b'v'`.
    154 const LOWER_V: u8 = b'v';
    155 /// `b'w'`.
    156 const LOWER_W: u8 = b'w';
    157 /// `b'x'`.
    158 const LOWER_X: u8 = b'x';
    159 /// `b'y'`.
    160 const LOWER_Y: u8 = b'y';
    161 /// `b'z'`.
    162 const LOWER_Z: u8 = b'z';
    163 /// `b'0'`.
    164 const ZERO: u8 = b'0';
    165 /// `b'1'`.
    166 const ONE: u8 = b'1';
    167 /// `b'2'`.
    168 const TWO: u8 = b'2';
    169 /// `b'3'`.
    170 const THREE: u8 = b'3';
    171 /// `b'4'`.
    172 const FOUR: u8 = b'4';
    173 /// `b'5'`.
    174 const FIVE: u8 = b'5';
    175 /// `b'6'`.
    176 const SIX: u8 = b'6';
    177 /// `b'7'`.
    178 const SEVEN: u8 = b'7';
    179 /// `b'8'`.
    180 const EIGHT: u8 = b'8';
    181 /// `b'9'`.
    182 const NINE: u8 = b'9';
    183 /// `b'-'`.
    184 const HYPHEN: u8 = b'-';
    185 /// `b'_'`.
    186 const UNDERSCORE: u8 = b'_';
    187 /// `'A'`.
    188 const UPPER_A_CHAR: char = 'A';
    189 /// `'B'`.
    190 const UPPER_B_CHAR: char = 'B';
    191 /// `'C'`.
    192 const UPPER_C_CHAR: char = 'C';
    193 /// `'D'`.
    194 const UPPER_D_CHAR: char = 'D';
    195 /// `'E'`.
    196 const UPPER_E_CHAR: char = 'E';
    197 /// `'F'`.
    198 const UPPER_F_CHAR: char = 'F';
    199 /// `'G'`.
    200 const UPPER_G_CHAR: char = 'G';
    201 /// `'H'`.
    202 const UPPER_H_CHAR: char = 'H';
    203 /// `'I'`.
    204 const UPPER_I_CHAR: char = 'I';
    205 /// `'J'`.
    206 const UPPER_J_CHAR: char = 'J';
    207 /// `'K'`.
    208 const UPPER_K_CHAR: char = 'K';
    209 /// `'L'`.
    210 const UPPER_L_CHAR: char = 'L';
    211 /// `'M'`.
    212 const UPPER_M_CHAR: char = 'M';
    213 /// `'N'`.
    214 const UPPER_N_CHAR: char = 'N';
    215 /// `'O'`.
    216 const UPPER_O_CHAR: char = 'O';
    217 /// `'P'`.
    218 const UPPER_P_CHAR: char = 'P';
    219 /// `'Q'`.
    220 const UPPER_Q_CHAR: char = 'Q';
    221 /// `'R'`.
    222 const UPPER_R_CHAR: char = 'R';
    223 /// `'S'`.
    224 const UPPER_S_CHAR: char = 'S';
    225 /// `'T'`.
    226 const UPPER_T_CHAR: char = 'T';
    227 /// `'U'`.
    228 const UPPER_U_CHAR: char = 'U';
    229 /// `'V'`.
    230 const UPPER_V_CHAR: char = 'V';
    231 /// `'W'`.
    232 const UPPER_W_CHAR: char = 'W';
    233 /// `'X'`.
    234 const UPPER_X_CHAR: char = 'X';
    235 /// `'Y'`.
    236 const UPPER_Y_CHAR: char = 'Y';
    237 /// `'Z'`.
    238 const UPPER_Z_CHAR: char = 'Z';
    239 /// `'a'`.
    240 const LOWER_A_CHAR: char = 'a';
    241 /// `'b'`.
    242 const LOWER_B_CHAR: char = 'b';
    243 /// `'c'`.
    244 const LOWER_C_CHAR: char = 'c';
    245 /// `'d'`.
    246 const LOWER_D_CHAR: char = 'd';
    247 /// `'e'`.
    248 const LOWER_E_CHAR: char = 'e';
    249 /// `'f'`.
    250 const LOWER_F_CHAR: char = 'f';
    251 /// `'g'`.
    252 const LOWER_G_CHAR: char = 'g';
    253 /// `'h'`.
    254 const LOWER_H_CHAR: char = 'h';
    255 /// `'i'`.
    256 const LOWER_I_CHAR: char = 'i';
    257 /// `'j'`.
    258 const LOWER_J_CHAR: char = 'j';
    259 /// `'k'`.
    260 const LOWER_K_CHAR: char = 'k';
    261 /// `'l'`.
    262 const LOWER_L_CHAR: char = 'l';
    263 /// `'m'`.
    264 const LOWER_M_CHAR: char = 'm';
    265 /// `'n'`.
    266 const LOWER_N_CHAR: char = 'n';
    267 /// `'o'`.
    268 const LOWER_O_CHAR: char = 'o';
    269 /// `'p'`.
    270 const LOWER_P_CHAR: char = 'p';
    271 /// `'q'`.
    272 const LOWER_Q_CHAR: char = 'q';
    273 /// `'r'`.
    274 const LOWER_R_CHAR: char = 'r';
    275 /// `'s'`.
    276 const LOWER_S_CHAR: char = 's';
    277 /// `'t'`.
    278 const LOWER_T_CHAR: char = 't';
    279 /// `'u'`.
    280 const LOWER_U_CHAR: char = 'u';
    281 /// `'v'`.
    282 const LOWER_V_CHAR: char = 'v';
    283 /// `'w'`.
    284 const LOWER_W_CHAR: char = 'w';
    285 /// `'x'`.
    286 const LOWER_X_CHAR: char = 'x';
    287 /// `'y'`.
    288 const LOWER_Y_CHAR: char = 'y';
    289 /// `'z'`.
    290 const LOWER_Z_CHAR: char = 'z';
    291 /// `'0'`.
    292 const ZERO_CHAR: char = '0';
    293 /// `'1'`.
    294 const ONE_CHAR: char = '1';
    295 /// `'2'`.
    296 const TWO_CHAR: char = '2';
    297 /// `'3'`.
    298 const THREE_CHAR: char = '3';
    299 /// `'4'`.
    300 const FOUR_CHAR: char = '4';
    301 /// `'5'`.
    302 const FIVE_CHAR: char = '5';
    303 /// `'6'`.
    304 const SIX_CHAR: char = '6';
    305 /// `'7'`.
    306 const SEVEN_CHAR: char = '7';
    307 /// `'8'`.
    308 const EIGHT_CHAR: char = '8';
    309 /// `'9'`.
    310 const NINE_CHAR: char = '9';
    311 /// `'-'`.
    312 const HYPHEN_CHAR: char = '-';
    313 /// `'_'`.
    314 const UNDERSCORE_CHAR: char = '_';
    315 /// The base64url alphabet.
    316 #[expect(
    317     non_camel_case_types,
    318     reason = "want to use a variant as close to what the value is"
    319 )]
    320 #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
    321 #[repr(u8)]
    322 pub enum Alphabet {
    323     /// A.
    324     #[default]
    325     A,
    326     /// B.
    327     B,
    328     /// C.
    329     C,
    330     /// D.
    331     D,
    332     /// E.
    333     E,
    334     /// F.
    335     F,
    336     /// G.
    337     G,
    338     /// H.
    339     H,
    340     /// I.
    341     I,
    342     /// J.
    343     J,
    344     /// K.
    345     K,
    346     /// L.
    347     L,
    348     /// M.
    349     M,
    350     /// N.
    351     N,
    352     /// O.
    353     O,
    354     /// P.
    355     P,
    356     /// Q.
    357     Q,
    358     /// R.
    359     R,
    360     /// S.
    361     S,
    362     /// T.
    363     T,
    364     /// U.
    365     U,
    366     /// V.
    367     V,
    368     /// W.
    369     W,
    370     /// X.
    371     X,
    372     /// Y.
    373     Y,
    374     /// Z.
    375     Z,
    376     /// a.
    377     a,
    378     /// b.
    379     b,
    380     /// c.
    381     c,
    382     /// d.
    383     d,
    384     /// e.
    385     e,
    386     /// f.
    387     f,
    388     /// g.
    389     g,
    390     /// h.
    391     h,
    392     /// i.
    393     i,
    394     /// j.
    395     j,
    396     /// k.
    397     k,
    398     /// l.
    399     l,
    400     /// m.
    401     m,
    402     /// n.
    403     n,
    404     /// o.
    405     o,
    406     /// p.
    407     p,
    408     /// q.
    409     q,
    410     /// r.
    411     r,
    412     /// s.
    413     s,
    414     /// t.
    415     t,
    416     /// u.
    417     u,
    418     /// v.
    419     v,
    420     /// w.
    421     w,
    422     /// x.
    423     x,
    424     /// y.
    425     y,
    426     /// z.
    427     z,
    428     /// 0.
    429     Zero,
    430     /// 1.
    431     One,
    432     /// 2.
    433     Two,
    434     /// 3.
    435     Three,
    436     /// 4.
    437     Four,
    438     /// 5.
    439     Five,
    440     /// 6.
    441     Six,
    442     /// 7.
    443     Seven,
    444     /// 8.
    445     Eight,
    446     /// 9.
    447     Nine,
    448     /// -.
    449     Hyphen,
    450     /// _.
    451     Underscore,
    452 }
    453 impl Alphabet {
    454     /// Returns `Self` that corresponds to `b`.
    455     ///
    456     /// `Some` is returned iff `b` is in `0..=63`.
    457     ///
    458     /// # Examples
    459     ///
    460     /// ```
    461     /// # use base64url_nopad::Alphabet;
    462     /// assert!(Alphabet::from_u8(25).is_some_and(|val| val == Alphabet::Z));
    463     /// for i in 0..=63 {
    464     ///     assert!(Alphabet::from_u8(i).is_some());
    465     /// }
    466     /// for i in 64..=255 {
    467     ///     assert!(Alphabet::from_u8(i).is_none());
    468     /// }
    469     /// ```
    470     #[expect(unsafe_code, reason = "comment justifies correctness")]
    471     #[expect(clippy::as_conversions, reason = "comment justifies correctness")]
    472     #[inline]
    473     #[must_use]
    474     pub const fn from_u8(b: u8) -> Option<Self> {
    475         // `Self` is `repr(u8)` and all `u8`s are valid from 0 until the maximum value
    476         // represented by `Self::Underscore`.
    477         if b <= Self::Underscore as u8 {
    478             // SAFETY:
    479             // Just checked that `b` is in-range
    480             Some(unsafe { Self::from_u8_unchecked(b) })
    481         } else {
    482             None
    483         }
    484     }
    485     /// # Safety:
    486     ///
    487     /// `b` must be in `0..=63`, or else this is UB.
    488     #[expect(unsafe_code, reason = "comment justifies correctness")]
    489     const unsafe fn from_u8_unchecked(b: u8) -> Self {
    490         // SAFETY:
    491         // Our safety precondition is that `b` is in-range.
    492         unsafe { mem::transmute(b) }
    493     }
    494     /// Returns the `u8` `self` represents.
    495     ///
    496     /// # Examples
    497     ///
    498     /// ```
    499     /// # use base64url_nopad::Alphabet;
    500     /// assert_eq!(Alphabet::Hyphen.to_u8(), 62);
    501     /// assert_eq!(Alphabet::Eight.to_u8(), Alphabet::Eight as u8);
    502     /// ```
    503     #[expect(clippy::as_conversions, reason = "comment justifies correctness")]
    504     #[inline]
    505     #[must_use]
    506     pub const fn to_u8(self) -> u8 {
    507         // `Self` is `repr(u8)`; thus this is correct.
    508         self as u8
    509     }
    510     /// Returns the ASCII representation of `self`.
    511     ///
    512     /// # Examples
    513     ///
    514     /// ```
    515     /// # use base64url_nopad::Alphabet;
    516     /// assert_eq!(Alphabet::c.to_ascii(), b'c');
    517     /// ```
    518     #[inline]
    519     #[must_use]
    520     pub const fn to_ascii(self) -> u8 {
    521         match self {
    522             Self::A => UPPER_A,
    523             Self::B => UPPER_B,
    524             Self::C => UPPER_C,
    525             Self::D => UPPER_D,
    526             Self::E => UPPER_E,
    527             Self::F => UPPER_F,
    528             Self::G => UPPER_G,
    529             Self::H => UPPER_H,
    530             Self::I => UPPER_I,
    531             Self::J => UPPER_J,
    532             Self::K => UPPER_K,
    533             Self::L => UPPER_L,
    534             Self::M => UPPER_M,
    535             Self::N => UPPER_N,
    536             Self::O => UPPER_O,
    537             Self::P => UPPER_P,
    538             Self::Q => UPPER_Q,
    539             Self::R => UPPER_R,
    540             Self::S => UPPER_S,
    541             Self::T => UPPER_T,
    542             Self::U => UPPER_U,
    543             Self::V => UPPER_V,
    544             Self::W => UPPER_W,
    545             Self::X => UPPER_X,
    546             Self::Y => UPPER_Y,
    547             Self::Z => UPPER_Z,
    548             Self::a => LOWER_A,
    549             Self::b => LOWER_B,
    550             Self::c => LOWER_C,
    551             Self::d => LOWER_D,
    552             Self::e => LOWER_E,
    553             Self::f => LOWER_F,
    554             Self::g => LOWER_G,
    555             Self::h => LOWER_H,
    556             Self::i => LOWER_I,
    557             Self::j => LOWER_J,
    558             Self::k => LOWER_K,
    559             Self::l => LOWER_L,
    560             Self::m => LOWER_M,
    561             Self::n => LOWER_N,
    562             Self::o => LOWER_O,
    563             Self::p => LOWER_P,
    564             Self::q => LOWER_Q,
    565             Self::r => LOWER_R,
    566             Self::s => LOWER_S,
    567             Self::t => LOWER_T,
    568             Self::u => LOWER_U,
    569             Self::v => LOWER_V,
    570             Self::w => LOWER_W,
    571             Self::x => LOWER_X,
    572             Self::y => LOWER_Y,
    573             Self::z => LOWER_Z,
    574             Self::Zero => ZERO,
    575             Self::One => ONE,
    576             Self::Two => TWO,
    577             Self::Three => THREE,
    578             Self::Four => FOUR,
    579             Self::Five => FIVE,
    580             Self::Six => SIX,
    581             Self::Seven => SEVEN,
    582             Self::Eight => EIGHT,
    583             Self::Nine => NINE,
    584             Self::Hyphen => HYPHEN,
    585             Self::Underscore => UNDERSCORE,
    586         }
    587     }
    588     /// Returns `Some` iff `ascii` is the ASCII representation of `Self`.
    589     ///
    590     /// # Examples
    591     ///
    592     /// ```
    593     /// # use base64url_nopad::Alphabet;
    594     /// for i in 0u8..=255 {
    595     ///     if i.is_ascii_alphanumeric() || i == b'-' || i == b'_' {
    596     ///         assert!(Alphabet::from_ascii(i).is_some());
    597     ///     } else {
    598     ///         assert!(Alphabet::from_ascii(i).is_none());
    599     ///     }
    600     /// }
    601     /// ```
    602     #[inline]
    603     #[must_use]
    604     pub const fn from_ascii(ascii: u8) -> Option<Self> {
    605         match ascii {
    606             UPPER_A => Some(Self::A),
    607             UPPER_B => Some(Self::B),
    608             UPPER_C => Some(Self::C),
    609             UPPER_D => Some(Self::D),
    610             UPPER_E => Some(Self::E),
    611             UPPER_F => Some(Self::F),
    612             UPPER_G => Some(Self::G),
    613             UPPER_H => Some(Self::H),
    614             UPPER_I => Some(Self::I),
    615             UPPER_J => Some(Self::J),
    616             UPPER_K => Some(Self::K),
    617             UPPER_L => Some(Self::L),
    618             UPPER_M => Some(Self::M),
    619             UPPER_N => Some(Self::N),
    620             UPPER_O => Some(Self::O),
    621             UPPER_P => Some(Self::P),
    622             UPPER_Q => Some(Self::Q),
    623             UPPER_R => Some(Self::R),
    624             UPPER_S => Some(Self::S),
    625             UPPER_T => Some(Self::T),
    626             UPPER_U => Some(Self::U),
    627             UPPER_V => Some(Self::V),
    628             UPPER_W => Some(Self::W),
    629             UPPER_X => Some(Self::X),
    630             UPPER_Y => Some(Self::Y),
    631             UPPER_Z => Some(Self::Z),
    632             LOWER_A => Some(Self::a),
    633             LOWER_B => Some(Self::b),
    634             LOWER_C => Some(Self::c),
    635             LOWER_D => Some(Self::d),
    636             LOWER_E => Some(Self::e),
    637             LOWER_F => Some(Self::f),
    638             LOWER_G => Some(Self::g),
    639             LOWER_H => Some(Self::h),
    640             LOWER_I => Some(Self::i),
    641             LOWER_J => Some(Self::j),
    642             LOWER_K => Some(Self::k),
    643             LOWER_L => Some(Self::l),
    644             LOWER_M => Some(Self::m),
    645             LOWER_N => Some(Self::n),
    646             LOWER_O => Some(Self::o),
    647             LOWER_P => Some(Self::p),
    648             LOWER_Q => Some(Self::q),
    649             LOWER_R => Some(Self::r),
    650             LOWER_S => Some(Self::s),
    651             LOWER_T => Some(Self::t),
    652             LOWER_U => Some(Self::u),
    653             LOWER_V => Some(Self::v),
    654             LOWER_W => Some(Self::w),
    655             LOWER_X => Some(Self::x),
    656             LOWER_Y => Some(Self::y),
    657             LOWER_Z => Some(Self::z),
    658             ZERO => Some(Self::Zero),
    659             ONE => Some(Self::One),
    660             TWO => Some(Self::Two),
    661             THREE => Some(Self::Three),
    662             FOUR => Some(Self::Four),
    663             FIVE => Some(Self::Five),
    664             SIX => Some(Self::Six),
    665             SEVEN => Some(Self::Seven),
    666             EIGHT => Some(Self::Eight),
    667             NINE => Some(Self::Nine),
    668             HYPHEN => Some(Self::Hyphen),
    669             UNDERSCORE => Some(Self::Underscore),
    670             _ => None,
    671         }
    672     }
    673     /// Same as [`Self::to_ascii`] except a `char` is returned.
    674     ///
    675     /// # Examples
    676     ///
    677     /// ```
    678     /// # use base64url_nopad::Alphabet;
    679     /// assert_eq!(Alphabet::J.to_char(), 'J');
    680     /// ```
    681     #[inline]
    682     #[must_use]
    683     pub const fn to_char(self) -> char {
    684         match self {
    685             Self::A => UPPER_A_CHAR,
    686             Self::B => UPPER_B_CHAR,
    687             Self::C => UPPER_C_CHAR,
    688             Self::D => UPPER_D_CHAR,
    689             Self::E => UPPER_E_CHAR,
    690             Self::F => UPPER_F_CHAR,
    691             Self::G => UPPER_G_CHAR,
    692             Self::H => UPPER_H_CHAR,
    693             Self::I => UPPER_I_CHAR,
    694             Self::J => UPPER_J_CHAR,
    695             Self::K => UPPER_K_CHAR,
    696             Self::L => UPPER_L_CHAR,
    697             Self::M => UPPER_M_CHAR,
    698             Self::N => UPPER_N_CHAR,
    699             Self::O => UPPER_O_CHAR,
    700             Self::P => UPPER_P_CHAR,
    701             Self::Q => UPPER_Q_CHAR,
    702             Self::R => UPPER_R_CHAR,
    703             Self::S => UPPER_S_CHAR,
    704             Self::T => UPPER_T_CHAR,
    705             Self::U => UPPER_U_CHAR,
    706             Self::V => UPPER_V_CHAR,
    707             Self::W => UPPER_W_CHAR,
    708             Self::X => UPPER_X_CHAR,
    709             Self::Y => UPPER_Y_CHAR,
    710             Self::Z => UPPER_Z_CHAR,
    711             Self::a => LOWER_A_CHAR,
    712             Self::b => LOWER_B_CHAR,
    713             Self::c => LOWER_C_CHAR,
    714             Self::d => LOWER_D_CHAR,
    715             Self::e => LOWER_E_CHAR,
    716             Self::f => LOWER_F_CHAR,
    717             Self::g => LOWER_G_CHAR,
    718             Self::h => LOWER_H_CHAR,
    719             Self::i => LOWER_I_CHAR,
    720             Self::j => LOWER_J_CHAR,
    721             Self::k => LOWER_K_CHAR,
    722             Self::l => LOWER_L_CHAR,
    723             Self::m => LOWER_M_CHAR,
    724             Self::n => LOWER_N_CHAR,
    725             Self::o => LOWER_O_CHAR,
    726             Self::p => LOWER_P_CHAR,
    727             Self::q => LOWER_Q_CHAR,
    728             Self::r => LOWER_R_CHAR,
    729             Self::s => LOWER_S_CHAR,
    730             Self::t => LOWER_T_CHAR,
    731             Self::u => LOWER_U_CHAR,
    732             Self::v => LOWER_V_CHAR,
    733             Self::w => LOWER_W_CHAR,
    734             Self::x => LOWER_X_CHAR,
    735             Self::y => LOWER_Y_CHAR,
    736             Self::z => LOWER_Z_CHAR,
    737             Self::Zero => ZERO_CHAR,
    738             Self::One => ONE_CHAR,
    739             Self::Two => TWO_CHAR,
    740             Self::Three => THREE_CHAR,
    741             Self::Four => FOUR_CHAR,
    742             Self::Five => FIVE_CHAR,
    743             Self::Six => SIX_CHAR,
    744             Self::Seven => SEVEN_CHAR,
    745             Self::Eight => EIGHT_CHAR,
    746             Self::Nine => NINE_CHAR,
    747             Self::Hyphen => HYPHEN_CHAR,
    748             Self::Underscore => UNDERSCORE_CHAR,
    749         }
    750     }
    751     /// Same as [`Self::from_ascii`] except the input is a `char`.
    752     ///
    753     /// # Examples
    754     ///
    755     /// ```
    756     /// # use base64url_nopad::Alphabet;
    757     /// for i in char::MIN..=char::MAX {
    758     ///     if i.is_ascii_alphanumeric() || i == '-' || i == '_' {
    759     ///         assert!(Alphabet::from_char(i).is_some());
    760     ///     } else {
    761     ///         assert!(Alphabet::from_char(i).is_none());
    762     ///     }
    763     /// }
    764     /// ```
    765     #[inline]
    766     #[must_use]
    767     pub const fn from_char(c: char) -> Option<Self> {
    768         match c {
    769             UPPER_A_CHAR => Some(Self::A),
    770             UPPER_B_CHAR => Some(Self::B),
    771             UPPER_C_CHAR => Some(Self::C),
    772             UPPER_D_CHAR => Some(Self::D),
    773             UPPER_E_CHAR => Some(Self::E),
    774             UPPER_F_CHAR => Some(Self::F),
    775             UPPER_G_CHAR => Some(Self::G),
    776             UPPER_H_CHAR => Some(Self::H),
    777             UPPER_I_CHAR => Some(Self::I),
    778             UPPER_J_CHAR => Some(Self::J),
    779             UPPER_K_CHAR => Some(Self::K),
    780             UPPER_L_CHAR => Some(Self::L),
    781             UPPER_M_CHAR => Some(Self::M),
    782             UPPER_N_CHAR => Some(Self::N),
    783             UPPER_O_CHAR => Some(Self::O),
    784             UPPER_P_CHAR => Some(Self::P),
    785             UPPER_Q_CHAR => Some(Self::Q),
    786             UPPER_R_CHAR => Some(Self::R),
    787             UPPER_S_CHAR => Some(Self::S),
    788             UPPER_T_CHAR => Some(Self::T),
    789             UPPER_U_CHAR => Some(Self::U),
    790             UPPER_V_CHAR => Some(Self::V),
    791             UPPER_W_CHAR => Some(Self::W),
    792             UPPER_X_CHAR => Some(Self::X),
    793             UPPER_Y_CHAR => Some(Self::Y),
    794             UPPER_Z_CHAR => Some(Self::Z),
    795             LOWER_A_CHAR => Some(Self::a),
    796             LOWER_B_CHAR => Some(Self::b),
    797             LOWER_C_CHAR => Some(Self::c),
    798             LOWER_D_CHAR => Some(Self::d),
    799             LOWER_E_CHAR => Some(Self::e),
    800             LOWER_F_CHAR => Some(Self::f),
    801             LOWER_G_CHAR => Some(Self::g),
    802             LOWER_H_CHAR => Some(Self::h),
    803             LOWER_I_CHAR => Some(Self::i),
    804             LOWER_J_CHAR => Some(Self::j),
    805             LOWER_K_CHAR => Some(Self::k),
    806             LOWER_L_CHAR => Some(Self::l),
    807             LOWER_M_CHAR => Some(Self::m),
    808             LOWER_N_CHAR => Some(Self::n),
    809             LOWER_O_CHAR => Some(Self::o),
    810             LOWER_P_CHAR => Some(Self::p),
    811             LOWER_Q_CHAR => Some(Self::q),
    812             LOWER_R_CHAR => Some(Self::r),
    813             LOWER_S_CHAR => Some(Self::s),
    814             LOWER_T_CHAR => Some(Self::t),
    815             LOWER_U_CHAR => Some(Self::u),
    816             LOWER_V_CHAR => Some(Self::v),
    817             LOWER_W_CHAR => Some(Self::w),
    818             LOWER_X_CHAR => Some(Self::x),
    819             LOWER_Y_CHAR => Some(Self::y),
    820             LOWER_Z_CHAR => Some(Self::z),
    821             ZERO_CHAR => Some(Self::Zero),
    822             ONE_CHAR => Some(Self::One),
    823             TWO_CHAR => Some(Self::Two),
    824             THREE_CHAR => Some(Self::Three),
    825             FOUR_CHAR => Some(Self::Four),
    826             FIVE_CHAR => Some(Self::Five),
    827             SIX_CHAR => Some(Self::Six),
    828             SEVEN_CHAR => Some(Self::Seven),
    829             EIGHT_CHAR => Some(Self::Eight),
    830             NINE_CHAR => Some(Self::Nine),
    831             HYPHEN_CHAR => Some(Self::Hyphen),
    832             UNDERSCORE_CHAR => Some(Self::Underscore),
    833             _ => None,
    834         }
    835     }
    836 }
    837 impl Display for Alphabet {
    838     #[inline]
    839     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    840         f.write_char(self.to_char())
    841     }
    842 }
    843 impl From<Alphabet> for u8 {
    844     #[inline]
    845     fn from(value: Alphabet) -> Self {
    846         value.to_u8()
    847     }
    848 }
    849 impl From<Alphabet> for u16 {
    850     #[inline]
    851     fn from(value: Alphabet) -> Self {
    852         Self::from(value.to_u8())
    853     }
    854 }
    855 impl From<Alphabet> for u32 {
    856     #[inline]
    857     fn from(value: Alphabet) -> Self {
    858         Self::from(value.to_u8())
    859     }
    860 }
    861 impl From<Alphabet> for u64 {
    862     #[inline]
    863     fn from(value: Alphabet) -> Self {
    864         Self::from(value.to_u8())
    865     }
    866 }
    867 impl From<Alphabet> for u128 {
    868     #[inline]
    869     fn from(value: Alphabet) -> Self {
    870         Self::from(value.to_u8())
    871     }
    872 }
    873 impl From<Alphabet> for char {
    874     #[inline]
    875     fn from(value: Alphabet) -> Self {
    876         value.to_char()
    877     }
    878 }
    879 /// Ordinal numbers from first to third inclusively.
    880 enum ThreeOrdinal {
    881     /// First.
    882     First,
    883     /// Second.
    884     Second,
    885     /// Third.
    886     Third,
    887 }
    888 /// The maximum value [`encode_len_checked`] will accept before returning `None`.
    889 // This won't `panic` since `usize::MAX` ≢ 1 (mod 4).
    890 pub const MAX_ENCODE_INPUT_LEN: usize = decode_len(usize::MAX).unwrap();
    891 /// Returns the exact number of bytes needed to encode an input of length `input_length`.
    892 ///
    893 /// `Some` is returned iff the length needed does not exceed [`usize::MAX`].
    894 ///
    895 /// Note since Rust guarantees all memory allocations don't exceed [`isize::MAX`] bytes, then one can
    896 /// instead call [`encode_len`] when the argument passed corresponds to the length of an allocation since
    897 /// `isize::MAX <` [`MAX_ENCODE_INPUT_LEN`].
    898 ///
    899 /// # Examples
    900 ///
    901 /// ```
    902 /// # use base64url_nopad::MAX_ENCODE_INPUT_LEN;
    903 /// assert!(base64url_nopad::encode_len_checked(usize::MAX).is_none());
    904 /// assert!(base64url_nopad::encode_len_checked(MAX_ENCODE_INPUT_LEN + 1).is_none());
    905 /// assert!(base64url_nopad::encode_len_checked(MAX_ENCODE_INPUT_LEN).is_some_and(|len| len == usize::MAX));
    906 /// assert!(base64url_nopad::encode_len_checked(3).is_some_and(|len| len == 4));
    907 /// assert!(base64url_nopad::encode_len_checked(2).is_some_and(|len| len == 3));
    908 /// assert!(base64url_nopad::encode_len_checked(1).is_some_and(|len| len == 2));
    909 /// assert!(base64url_nopad::encode_len_checked(0).is_some_and(|len| len == 0));
    910 /// ```
    911 #[expect(
    912     clippy::arithmetic_side_effects,
    913     clippy::integer_division,
    914     clippy::integer_division_remainder_used,
    915     reason = "proof and comment justifies their correctness"
    916 )]
    917 #[inline]
    918 #[must_use]
    919 pub const fn encode_len_checked(input_length: usize) -> Option<usize> {
    920     // 256^n is the number of distinct values of the input. Let the base64 encoding in a URL safe
    921     // way without padding of the input be O. There are 64 possible values each byte in O can be; thus we must find
    922     // the minimum nonnegative integer m such that:
    923     // 64^m = (2^6)^m = 2^(6m) >= 256^n = (2^8)^n = 2^(8n)
    924     // <==>
    925     // lg(2^(6m)) = 6m >= lg(2^(8n)) = 8n   lg is defined on all positive reals which 2^(6m) and 2^(8n) are
    926     // <==>
    927     // m >= 8n/6 = 4n/3
    928     // Clearly that corresponds to m = ⌈4n/3⌉.
    929     // We claim ⌈4n/3⌉ = 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉.
    930     // Proof:
    931     // There are three partitions for n:
    932     // (1) 3i = n ≡ 0 (mod 3) for some integer i
    933     //   <==>
    934     //   ⌈4n/3⌉ = ⌈4(3i)/3⌉ = ⌈4i⌉ = 4i = 4⌊i⌋ = 4⌊3i/3⌋ = 4⌊n/3⌋ + 0 = 4⌊n/3⌋ + ⌈4(0)/3⌉ = 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉
    935     // (2) 3i + 1 = n ≡ 1 (mod 3) for some integer i
    936     //   <==>
    937     //   ⌈4n/3⌉ = ⌈4(3i + 1)/3⌉ = ⌈4i + 4/3⌉ = 4i + ⌈4/3⌉ = 4i + 2 = 4⌊i + 1/3⌋ + ⌈4(1)/3⌉
    938     //                                                             = 4⌊(3i + 1)/3⌋ + ⌈4((3i + 1) mod 3)/3⌉
    939     //                                                             = 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉
    940     // (3) 3i + 2 = n ≡ 2 (mod 3) for some integer i
    941     //   <==>
    942     //   ⌈4n/3⌉ = ⌈4(3i + 2)/3⌉ = ⌈4i + 8/3⌉ = 4i + ⌈8/3⌉ = 4i + 3 = 4⌊i + 2/3⌋ + ⌈4(2)/3⌉
    943     //                                                             = 4⌊(3i + 2)/3⌋ + ⌈4((3i + 2) mod 3)/3⌉
    944     //                                                             = 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉
    945     // QED
    946     // Proof of no overflow:
    947     // `MAX_ENCODE_INPUT_LEN` = decode_len(usize::MAX).unwrap(); thus all values less than or equal to
    948     // `MAX_ENCODE_INPUT_LEN` won't overflow ignoring intermediate calcuations since ⌈4n/3⌉ is a
    949     // monotonically increasing function.
    950     // QED
    951     // Naively implementing ⌈4n/3⌉ as (4 * n).div_ceil(3) can cause overflow due to `4 * n`; thus
    952     // we implement the equivalent equation 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉ instead:
    953     // `(4 * (n / 3)) + (4 * (n % 3)).div_ceil(3)` since none of the intermediate calculations suffer
    954     // from overflow.
    955     if input_length <= MAX_ENCODE_INPUT_LEN {
    956         // (n / 3) << 2u8 <= m <= usize::MAX; thus the left operand of + is fine.
    957         // n % 3 <= 2
    958         // <==>
    959         // 4(n % 3) <= 8 < usize::MAX; thus (n % 3) << 2u8 is fine.
    960         // <==>
    961         // ⌈4(n % 3)/3⌉ <= 4(n % 3), so the right operand of + is fine.
    962         // The sum is fine since
    963         // m = ⌈4n/3⌉ = 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉ = ((n / 3) << 2u8) + ((n % 3) << 2u8).div_ceil(3), and m <= usize::MAX.
    964         Some(((input_length / 3) << 2u8) + ((input_length % 3) << 2u8).div_ceil(3))
    965     } else {
    966         None
    967     }
    968 }
    969 /// Same as [`encode_len_checked`] except a `panic` occurs instead of `None` being returned.
    970 ///
    971 /// One should prefer this function over `encode_len_checked` when passing the length of a memory allocation
    972 /// since such a length is guaranteed to succeed.
    973 ///
    974 /// # Panics
    975 ///
    976 /// `panic`s iff [`encode_len_checked`] returns `None`.
    977 ///
    978 /// # Examples
    979 ///
    980 /// ```
    981 /// # use base64url_nopad::MAX_ENCODE_INPUT_LEN;
    982 /// // Uncommenting below will cause a `panic`.
    983 /// // base64url_nopad::encode_len(usize::MAX - 4);
    984 /// // Uncommenting below will cause a `panic`.
    985 /// // base64url_nopad::encode_len(MAX_ENCODE_INPUT_LEN + 1);
    986 /// assert_eq!(base64url_nopad::encode_len(MAX_ENCODE_INPUT_LEN), usize::MAX);
    987 /// assert!(base64url_nopad::encode_len(isize::MAX as usize) > isize::MAX as usize);
    988 /// assert_eq!(base64url_nopad::encode_len(3), 4);
    989 /// assert_eq!(base64url_nopad::encode_len(2), 3);
    990 /// assert_eq!(base64url_nopad::encode_len(1), 2);
    991 /// assert_eq!(base64url_nopad::encode_len(0), 0);
    992 /// ```
    993 #[expect(clippy::unwrap_used, reason = "comment justifies correctness")]
    994 #[inline]
    995 #[must_use]
    996 pub const fn encode_len(input_length: usize) -> usize {
    997     // A precondition for calling this function is to ensure `encode_len_checked` can't return `None`.
    998     encode_len_checked(input_length).unwrap()
    999 }
   1000 /// Encodes `input` into `output` re-interpreting the encoded subset of `output` as a `str` before returning it.
   1001 ///
   1002 /// `Some` is returned iff `output.len()` is large enough to write the encoded data into.
   1003 ///
   1004 /// Note since Rust guarantees all memory allocations don't exceed [`isize::MAX`] bytes, one can
   1005 /// instead call [`encode_buffer`] using a buffer whose length is at least as large as the value returned from
   1006 /// [`encode_len`] without fear of a `panic` and the benefit of getting a `str` instead of an `Option`.
   1007 ///
   1008 /// # Examples
   1009 ///
   1010 /// ```
   1011 /// assert!(
   1012 ///     base64url_nopad::encode_buffer_checked([0; 0].as_slice(), [0; 0].as_mut_slice()).is_some_and(|val| val.is_empty())
   1013 /// );
   1014 /// assert!(
   1015 ///     base64url_nopad::encode_buffer_checked([0; 1].as_slice(), [0; 2].as_mut_slice()).is_some_and(|val| val == "AA")
   1016 /// );
   1017 /// // A larger output buffer than necessary is OK.
   1018 /// assert!(
   1019 ///     base64url_nopad::encode_buffer_checked([1; 1].as_slice(), [0; 128].as_mut_slice()).is_some_and(|val| val == "AQ")
   1020 /// );
   1021 /// assert!(
   1022 ///     base64url_nopad::encode_buffer_checked(
   1023 ///         [0xc9; 14].as_slice(),
   1024 ///         [0; base64url_nopad::encode_len(14)].as_mut_slice()
   1025 ///     ).is_some_and(|val| val == "ycnJycnJycnJycnJyck")
   1026 /// );
   1027 /// assert!(base64url_nopad::encode_buffer_checked([0; 1].as_slice(), [0; 1].as_mut_slice()).is_none());
   1028 /// ```
   1029 #[expect(unsafe_code, reason = "comments justify correctness")]
   1030 #[expect(
   1031     clippy::arithmetic_side_effects,
   1032     clippy::indexing_slicing,
   1033     reason = "comments justify correctness"
   1034 )]
   1035 #[inline]
   1036 pub const fn encode_buffer_checked<'a>(
   1037     mut input: &[u8],
   1038     output: &'a mut [u8],
   1039 ) -> Option<&'a mut str> {
   1040     // This won't `panic` since Rust guarantees that all memory allocations won't exceed `isize::MAX`.
   1041     let final_len = encode_len(input.len());
   1042     if output.len() >= final_len {
   1043         // We increment this by `1` for each `u8` in `input`. On every third `u8`, we increment it an extra
   1044         // time since we use 4 base64url `u8`s for each `u8`.
   1045         // We also verified that `output.len()` large enough; thus all indexing operations
   1046         // using it are correct, and incrementing it never results in overflow.
   1047         let mut output_idx = 0;
   1048         let mut counter = ThreeOrdinal::First;
   1049         let mut trailing = 0;
   1050         let mut shift;
   1051         while let [first, ref rest @ ..] = *input {
   1052             match counter {
   1053                 ThreeOrdinal::First => {
   1054                     // We trim the last two bits and interpret `first` as a 6-bit integer.
   1055                     shift = first >> 2;
   1056                     // SAFETY:
   1057                     // `shift <= 63` since we shifted at least two bits to the right.
   1058                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1059                     // The two bits we trimmed are the first two bits of the next 6-bit integer.
   1060                     trailing = (first & 3) << 4;
   1061                     counter = ThreeOrdinal::Second;
   1062                 }
   1063                 ThreeOrdinal::Second => {
   1064                     // We trim the last four bits and interpret `first` as a 6-bit integer.
   1065                     // The first two bits are the trailing 2 bits from the previous value.
   1066                     shift = trailing | (first >> 4);
   1067                     // SAFETY:
   1068                     // `shift <= 63` since `first` was shifted at least two bits to the right, and
   1069                     // `trailing = (first & 3) << 4` which means its high two bits are 0 as well.
   1070                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1071                     // The four bits we trimmed are the first four bits of the next 6-bit integer.
   1072                     trailing = (first & 15) << 2;
   1073                     counter = ThreeOrdinal::Third;
   1074                 }
   1075                 ThreeOrdinal::Third => {
   1076                     // We trim the last six bits and interpret `first` as a 6-bit integer.
   1077                     // The first four bits are the trailing 4 bits from the previous value.
   1078                     shift = trailing | (first >> 6);
   1079                     // SAFETY:
   1080                     // `shift <= 63` since `first` was shifted at least two bits to the right, and
   1081                     // `trailing = (first & 15) << 2` which means its high two bits are 0 as well.
   1082                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1083                     // Every third `u8` corresponds to a fourth base64url `u8`.
   1084                     output_idx += 1;
   1085                     // We use the 6 bits we just trimmed.
   1086                     shift = first & 63;
   1087                     // SAFETY:
   1088                     // `shift <= 63` since `first & 63` is.
   1089                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1090                     counter = ThreeOrdinal::First;
   1091                 }
   1092             }
   1093             input = rest;
   1094             output_idx += 1;
   1095         }
   1096         if !matches!(counter, ThreeOrdinal::First) {
   1097             // `input.len()` is not a multiple of 3; thus we have to append a trailing base64url `u8` that
   1098             // is simply the current value of `trailing`.
   1099             // SAFETY:
   1100             // `trailing <= 63` since `trailing` is either `(first & 3) << 4` or `(first & 15) << 2` where
   1101             // `first` is any `u8`. This means the high two bits are guaranteed to be 0.
   1102             output[output_idx] = unsafe { Alphabet::from_u8_unchecked(trailing) }.to_ascii();
   1103         }
   1104         // SAFETY:
   1105         // We verified `output.len() >= final_len`.
   1106         let val = unsafe { output.split_at_mut_unchecked(final_len) }.0;
   1107         // SAFETY:
   1108         // `val` has the exact length needed to encode `input`, and all of the `u8`s in it
   1109         // are from `Alphabet::to_ascii` which is a subset of UTF-8; thus this is safe.
   1110         // Note the above is vacuously true when `val` is empty.
   1111         Some(unsafe { str::from_utf8_unchecked_mut(val) })
   1112     } else {
   1113         None
   1114     }
   1115 }
   1116 /// Same as [`encode_buffer_checked`] except a `panic` occurs instead of `None` being returned.
   1117 ///
   1118 /// # Panics
   1119 ///
   1120 /// `panic`s iff [`encode_buffer_checked`] returns `None` (i.e., the length of the output buffer is too small).
   1121 ///
   1122 /// # Examples
   1123 ///
   1124 /// ```
   1125 /// assert_eq!(
   1126 ///     base64url_nopad::encode_buffer([0; 0].as_slice(), [0; 0].as_mut_slice()),
   1127 ///     ""
   1128 /// );
   1129 /// assert_eq!(
   1130 ///     base64url_nopad::encode_buffer([0; 1].as_slice(), [0; 2].as_mut_slice()),
   1131 ///     "AA"
   1132 /// );
   1133 /// // A larger output buffer than necessary is OK.
   1134 /// assert_eq!(
   1135 ///     base64url_nopad::encode_buffer([255; 1].as_slice(), [0; 256].as_mut_slice()),
   1136 ///     "_w"
   1137 /// );
   1138 /// assert_eq!(
   1139 ///     base64url_nopad::encode_buffer(
   1140 ///         [0xc9; 14].as_slice(),
   1141 ///         [0; base64url_nopad::encode_len(14)].as_mut_slice()
   1142 ///     ),
   1143 ///     "ycnJycnJycnJycnJyck"
   1144 /// );
   1145 /// // The below will `panic` when uncommented since the supplied output buffer is too small.
   1146 /// // _ = base64url_nopad::encode_buffer([0; 1].as_slice(), [0; 1].as_mut_slice());
   1147 /// ```
   1148 #[expect(clippy::unwrap_used, reason = "comment justifies correctness")]
   1149 #[inline]
   1150 pub const fn encode_buffer<'a>(input: &[u8], output: &'a mut [u8]) -> &'a mut str {
   1151     // A precondition for calling this function is to ensure `encode_buffer_checked` can't return `None`.
   1152     encode_buffer_checked(input, output).unwrap()
   1153 }
   1154 /// Similar to [`encode_buffer`] except a `String` is returned instead using its buffer to write to.
   1155 ///
   1156 /// # Errors
   1157 ///
   1158 /// Errors iff an error occurs from allocating the capacity needed to contain the encoded data.
   1159 ///
   1160 /// # Examples
   1161 ///
   1162 /// ```
   1163 /// # extern crate alloc;
   1164 /// # use alloc::collections::TryReserveError;
   1165 /// assert_eq!(
   1166 ///     base64url_nopad::try_encode([0; 0].as_slice())?,
   1167 ///     ""
   1168 /// );
   1169 /// assert_eq!(
   1170 ///     base64url_nopad::try_encode([0; 1].as_slice())?,
   1171 ///     "AA"
   1172 /// );
   1173 /// assert_eq!(
   1174 ///     base64url_nopad::try_encode([128, 40, 3].as_slice())?,
   1175 ///     "gCgD"
   1176 /// );
   1177 /// assert_eq!(
   1178 ///     base64url_nopad::try_encode([0x7b; 22].as_slice())?,
   1179 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1180 /// );
   1181 /// # Ok::<_, TryReserveError>(())
   1182 /// ```
   1183 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1184 #[cfg(feature = "alloc")]
   1185 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1186 #[inline]
   1187 pub fn try_encode(input: &[u8]) -> Result<String, TryReserveError> {
   1188     let mut output = Vec::new();
   1189     // `encode_len` won't `panic` since Rust guarantees `input.len()` will not return a value greater
   1190     // than `isize::MAX`.
   1191     let len = encode_len(input.len());
   1192     output.try_reserve_exact(len).map(|()| {
   1193         output.resize(len, 0);
   1194         _ = encode_buffer(input, output.as_mut_slice());
   1195         // SAFETY:
   1196         // `output` has the exact length needed to encode `input`, and all of the `u8`s in it
   1197         // are from `Alphabet` which is a subset of UTF-8; thus this is safe.
   1198         // Note the above is vacuously true when `output` is empty.
   1199         unsafe { String::from_utf8_unchecked(output) }
   1200     })
   1201 }
   1202 /// Same as [`try_encode`] except a `panic` occurs on allocation failure.
   1203 ///
   1204 /// # Panics
   1205 ///
   1206 /// `panic`s iff [`try_encode`] errors.
   1207 ///
   1208 /// # Examples
   1209 ///
   1210 /// ```
   1211 /// assert_eq!(
   1212 ///     base64url_nopad::encode([0; 0].as_slice()),
   1213 ///     ""
   1214 /// );
   1215 /// assert_eq!(
   1216 ///     base64url_nopad::encode([0; 1].as_slice()),
   1217 ///     "AA"
   1218 /// );
   1219 /// assert_eq!(
   1220 ///     base64url_nopad::encode([128, 40, 3].as_slice()),
   1221 ///     "gCgD"
   1222 /// );
   1223 /// assert_eq!(
   1224 ///     base64url_nopad::encode([0x7b; 22].as_slice()),
   1225 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1226 /// );
   1227 /// ```
   1228 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1229 #[cfg(feature = "alloc")]
   1230 #[expect(
   1231     clippy::unwrap_used,
   1232     reason = "purpose of function is to panic on allocation failure"
   1233 )]
   1234 #[inline]
   1235 #[must_use]
   1236 pub fn encode(input: &[u8]) -> String {
   1237     try_encode(input).unwrap()
   1238 }
   1239 /// Writes the base64url encoding of `input` using `writer`.
   1240 ///
   1241 /// Internally a buffer of at most 1024 bytes is used to write the encoded data. Smaller buffers may be used
   1242 /// for small inputs.
   1243 ///
   1244 /// # Errors
   1245 ///
   1246 /// Errors iff [`Write::write_str`] does.
   1247 ///
   1248 /// # Panics
   1249 ///
   1250 /// `panic`s iff [`Write::write_str`] does.
   1251 ///
   1252 /// # Examples
   1253 ///
   1254 /// ```
   1255 /// # extern crate alloc;
   1256 /// # use alloc::string::String;
   1257 /// # use core::fmt::Error;
   1258 /// let mut buffer = String::new();
   1259 /// base64url_nopad::encode_write([0; 0].as_slice(), &mut buffer)?;
   1260 /// assert_eq!(buffer, "");
   1261 /// buffer.clear();
   1262 /// base64url_nopad::encode_write([0; 1].as_slice(), &mut buffer)?;
   1263 /// assert_eq!(buffer, "AA");
   1264 /// buffer.clear();
   1265 /// base64url_nopad::encode_write(
   1266 ///     [0xc9; 14].as_slice(),
   1267 ///     &mut buffer,
   1268 /// )?;
   1269 /// assert_eq!(buffer, "ycnJycnJycnJycnJyck");
   1270 /// # Ok::<_, Error>(())
   1271 /// ```
   1272 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1273 #[expect(
   1274     clippy::arithmetic_side_effects,
   1275     clippy::as_conversions,
   1276     clippy::indexing_slicing,
   1277     reason = "comments justify correctness"
   1278 )]
   1279 #[inline]
   1280 pub fn encode_write<W: Write>(mut input: &[u8], writer: &mut W) -> fmt::Result {
   1281     /// The minimum buffer size we use.
   1282     const MIN_BUFFER_LEN: usize = 256;
   1283     /// The medium buffer size we use.
   1284     const MID_BUFFER_LEN: usize = MIN_BUFFER_LEN << 1;
   1285     /// The max buffer size.
   1286     ///
   1287     /// This must be at least 4, no more than `i16::MAX`, and must be a power of 2.
   1288     const MAX_BUFFER_LEN: usize = MID_BUFFER_LEN << 1;
   1289     /// The minimum length of the input until we must chunk encode the data.
   1290     const LOOP_LEN: usize = MAX_BUFFER_LEN + 1;
   1291     /// Want to ensure at compilation time that `MAX_BUFFER_LEN` upholds its invariants. Namely
   1292     /// that it's at least as large as 4, doesn't exceed [`i16::MAX`], and is always a power of 2.
   1293     const _: () = {
   1294         // `i16::MAX <= usize::MAX`, so this is fine.
   1295         /// `i16::MAX`.
   1296         const MAX_LEN: usize = i16::MAX as usize;
   1297         assert!(
   1298             4 <= MAX_BUFFER_LEN && MAX_BUFFER_LEN < MAX_LEN && MAX_BUFFER_LEN.is_power_of_two(),
   1299             "encode_write::MAX_BUFFER_LEN must be a power of two less than i16::MAX but at least as large as 4"
   1300         );
   1301     };
   1302     /// The input size that corresponds to an encoded value of length `MAX_BUFFER_LEN`.
   1303     // This will never `panic` since `MAX_BUFFER_LEN` is a power of two at least as large as 4
   1304     // (i.e., `MAX_BUFFER_LEN` ≢ 1 (mod 4)).
   1305     const INPUT_LEN: usize = decode_len(MAX_BUFFER_LEN).unwrap();
   1306     // This won't `panic` since `input.len()` is guaranteed to be no more than `isize::MAX`.
   1307     let len = encode_len(input.len());
   1308     match len {
   1309         0 => Ok(()),
   1310         1..=MIN_BUFFER_LEN => {
   1311             let mut buffer = [0; MIN_BUFFER_LEN];
   1312             // `buffer.len() == MIN_BUFFER_LEN >= len`, so indexing is fine.
   1313             // `encode_buffer` won't `panic` since `len` is the exact number of bytes needed to encode
   1314             // the data.
   1315             writer.write_str(encode_buffer(input, &mut buffer[..len]))
   1316         }
   1317         257..=MID_BUFFER_LEN => {
   1318             let mut buffer = [0; MID_BUFFER_LEN];
   1319             // `buffer.len() == MID_BUFFER_LEN >= len`, so indexing is fine.
   1320             // `encode_buffer` won't `panic` since `len` is the exact number of bytes needed to encode
   1321             // the data.
   1322             writer.write_str(encode_buffer(input, &mut buffer[..len]))
   1323         }
   1324         513..=MAX_BUFFER_LEN => {
   1325             let mut buffer = [0; MAX_BUFFER_LEN];
   1326             // `buffer.len() == MAX_BUFFER_LEN >= len`, so indexing is fine.
   1327             // `encode_buffer` won't `panic` since `len` is the exact number of bytes needed to encode
   1328             // the data.
   1329             writer.write_str(encode_buffer(input, &mut buffer[..len]))
   1330         }
   1331         LOOP_LEN.. => {
   1332             let mut buffer = [0; MAX_BUFFER_LEN];
   1333             let mut counter = 0;
   1334             // `len / MAX_BUFFER_LEN` is equal to ⌊len / MAX_BUFFER_LEN⌋ since `MAX_BUFFER_LEN` is a power of two.
   1335             // We can safely encode `term` chunks of `INPUT_LEN` length into `buffer`.
   1336             let term = len >> MAX_BUFFER_LEN.trailing_zeros();
   1337             let mut input_buffer;
   1338             while counter < term {
   1339                 // SAFETY:
   1340                 // `input.len() >= INPUT_LEN`.
   1341                 input_buffer = unsafe { input.split_at_unchecked(INPUT_LEN) };
   1342                 // `encode_buffer` won't `panic` since `buffer` has length `MAX_BUFFER_LEN` which
   1343                 // is the exact length needed for `INPUT_LEN` length inputs which `input_buffer.0` is.
   1344                 writer.write_str(encode_buffer(input_buffer.0, buffer.as_mut_slice()))?;
   1345                 input = input_buffer.1;
   1346                 // `counter < term`, so overflow cannot happen.
   1347                 counter += 1;
   1348             }
   1349             // `encode_len` won't `panic` since `input.len() < MAX_ENCODE_INPUT_LEN`.
   1350             // `input.len() < INPUT_LEN`; thus `encode_len(input.len()) < MAX_BUFFER_LEN = buffer.len()` so
   1351             // indexing is fine.
   1352             // `encode_buffer` won't `panic` since the buffer is the exact length needed to encode `input`.
   1353             writer.write_str(encode_buffer(input, &mut buffer[..encode_len(input.len())]))
   1354         }
   1355     }
   1356 }
   1357 /// Appends the base64url encoding of `input` to `s` returning the `str` that was appended.
   1358 ///
   1359 /// # Errors
   1360 ///
   1361 /// Errors iff an error occurs from allocating the capacity needed to append the encoded data.
   1362 ///
   1363 /// # Examples
   1364 ///
   1365 /// ```
   1366 /// # extern crate alloc;
   1367 /// # use alloc::{collections::TryReserveError, string::String};
   1368 /// let mut buffer = String::new();
   1369 /// assert_eq!(
   1370 ///     base64url_nopad::try_encode_append([0; 0].as_slice(), &mut buffer)?,
   1371 ///     ""
   1372 /// );
   1373 /// assert_eq!(
   1374 ///     base64url_nopad::try_encode_append([0; 1].as_slice(), &mut buffer)?,
   1375 ///     "AA"
   1376 /// );
   1377 /// assert_eq!(
   1378 ///     base64url_nopad::try_encode_append([128, 40, 3].as_slice(), &mut buffer)?,
   1379 ///     "gCgD"
   1380 /// );
   1381 /// assert_eq!(buffer, "AAgCgD");
   1382 /// assert_eq!(
   1383 ///     base64url_nopad::try_encode_append([0x7b; 22].as_slice(), &mut buffer)?,
   1384 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1385 /// );
   1386 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1387 /// # Ok::<_, TryReserveError>(())
   1388 /// ```
   1389 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1390 #[cfg(feature = "alloc")]
   1391 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1392 #[expect(
   1393     clippy::arithmetic_side_effects,
   1394     clippy::indexing_slicing,
   1395     reason = "comments justify correctness"
   1396 )]
   1397 #[inline]
   1398 pub fn try_encode_append<'a>(
   1399     input: &[u8],
   1400     s: &'a mut String,
   1401 ) -> Result<&'a mut str, TryReserveError> {
   1402     // `encode_len` won't `panic` since Rust guarantees `input.len()` will return a value no larger
   1403     // than `isize::MAX`.
   1404     let additional_len = encode_len(input.len());
   1405     s.try_reserve_exact(additional_len).map(|()| {
   1406         // SAFETY:
   1407         // We only append base64url ASCII which is a subset of UTF-8, so this will remain valid UTF-8.
   1408         let utf8 = unsafe { s.as_mut_vec() };
   1409         let original_len = utf8.len();
   1410         // Overflow can't happen; otherwise `s.try_reserve_exact` would have erred.
   1411         utf8.resize(original_len + additional_len, 0);
   1412         // `utf8.len() >= original_len`, so indexing is fine.
   1413         // `encode_buffer` won't `panic` since `utf8[original_len..]` has length `additional_len`
   1414         // which is the exact number of bytes needed to encode `input`.
   1415         encode_buffer(input, &mut utf8[original_len..])
   1416     })
   1417 }
   1418 /// Same as [`try_encode_append`] except the encoded `str` is not returned.
   1419 ///
   1420 /// # Errors
   1421 ///
   1422 /// Errors iff [`try_encode_append`] does.
   1423 ///
   1424 /// # Examples
   1425 ///
   1426 /// ```
   1427 /// # extern crate alloc;
   1428 /// # use alloc::{collections::TryReserveError, string::String};
   1429 /// let mut buffer = String::new();
   1430 /// base64url_nopad::try_encode_append_only([0; 0].as_slice(), &mut buffer)?;
   1431 /// assert_eq!(buffer, "");
   1432 /// base64url_nopad::try_encode_append_only([0; 1].as_slice(), &mut buffer)?;
   1433 /// assert_eq!(buffer, "AA");
   1434 /// base64url_nopad::try_encode_append_only([128, 40, 3].as_slice(), &mut buffer)?;
   1435 /// assert_eq!(buffer, "AAgCgD");
   1436 /// base64url_nopad::try_encode_append_only([0x7b; 22].as_slice(), &mut buffer)?;
   1437 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1438 /// # Ok::<_, TryReserveError>(())
   1439 /// ```
   1440 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1441 #[cfg(feature = "alloc")]
   1442 #[inline]
   1443 pub fn try_encode_append_only(input: &[u8], s: &mut String) -> Result<(), TryReserveError> {
   1444     try_encode_append(input, s).map(|_| ())
   1445 }
   1446 /// Same as [`try_encode_append`] except a `panic` occurs on allocation failure.
   1447 ///
   1448 /// # Panics
   1449 ///
   1450 /// `panic`s iff [`try_encode_append`] errors.
   1451 ///
   1452 /// # Examples
   1453 ///
   1454 /// ```
   1455 /// # extern crate alloc;
   1456 /// # use alloc::{collections::TryReserveError, string::String};
   1457 /// let mut buffer = String::new();
   1458 /// assert_eq!(
   1459 ///     base64url_nopad::encode_append([0; 0].as_slice(), &mut buffer),
   1460 ///     ""
   1461 /// );
   1462 /// assert_eq!(
   1463 ///     base64url_nopad::encode_append([0; 1].as_slice(), &mut buffer),
   1464 ///     "AA"
   1465 /// );
   1466 /// assert_eq!(
   1467 ///     base64url_nopad::encode_append([128, 40, 3].as_slice(), &mut buffer),
   1468 ///     "gCgD"
   1469 /// );
   1470 /// assert_eq!(buffer, "AAgCgD");
   1471 /// assert_eq!(
   1472 ///     base64url_nopad::encode_append([0x7b; 22].as_slice(), &mut buffer),
   1473 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1474 /// );
   1475 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1476 /// ```
   1477 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1478 #[cfg(feature = "alloc")]
   1479 #[expect(
   1480     clippy::unwrap_used,
   1481     reason = "purpose of this function is to panic on allocation failure"
   1482 )]
   1483 #[inline]
   1484 pub fn encode_append<'a>(input: &[u8], s: &'a mut String) -> &'a mut str {
   1485     try_encode_append(input, s).unwrap()
   1486 }
   1487 /// Same as [`encode_append`] except the encoded `str` is not returned.
   1488 ///
   1489 /// # Panics
   1490 ///
   1491 /// `panic`s iff [`encode_append`] does.
   1492 ///
   1493 /// # Examples
   1494 ///
   1495 /// ```
   1496 /// # extern crate alloc;
   1497 /// # use alloc::{collections::TryReserveError, string::String};
   1498 /// let mut buffer = String::new();
   1499 /// base64url_nopad::encode_append_only([0; 0].as_slice(), &mut buffer);
   1500 /// assert_eq!(buffer, "");
   1501 /// base64url_nopad::encode_append_only([0; 1].as_slice(), &mut buffer);
   1502 /// assert_eq!(buffer, "AA");
   1503 /// base64url_nopad::encode_append_only([128, 40, 3].as_slice(), &mut buffer);
   1504 /// assert_eq!(buffer, "AAgCgD");
   1505 /// base64url_nopad::encode_append_only([0x7b; 22].as_slice(), &mut buffer);
   1506 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1507 /// # Ok::<_, TryReserveError>(())
   1508 /// ```
   1509 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1510 #[cfg(feature = "alloc")]
   1511 #[inline]
   1512 pub fn encode_append_only(input: &[u8], s: &mut String) {
   1513     _ = encode_append(input, s);
   1514 }
   1515 /// Returns the exact number of bytes needed to decode a base64url without padding input of length `input_length`.
   1516 ///
   1517 /// `Some` is returned iff `input_length` represents a possible length of a base64url without padding input.
   1518 ///
   1519 /// # Examples
   1520 ///
   1521 /// ```
   1522 /// # use base64url_nopad::MAX_ENCODE_INPUT_LEN;
   1523 /// assert!(base64url_nopad::decode_len(1).is_none());
   1524 /// assert!(base64url_nopad::decode_len(usize::MAX).is_some_and(|len| len == MAX_ENCODE_INPUT_LEN));
   1525 /// assert!(base64url_nopad::decode_len(4).is_some_and(|len| len == 3));
   1526 /// assert!(base64url_nopad::decode_len(3).is_some_and(|len| len == 2));
   1527 /// assert!(base64url_nopad::decode_len(2).is_some_and(|len| len == 1));
   1528 /// assert!(base64url_nopad::decode_len(0).is_some_and(|len| len == 0));
   1529 /// ```
   1530 #[expect(
   1531     clippy::arithmetic_side_effects,
   1532     reason = "proof and comment justifies their correctness"
   1533 )]
   1534 #[inline]
   1535 #[must_use]
   1536 pub const fn decode_len(input_length: usize) -> Option<usize> {
   1537     // 64^n is the number of distinct values of the input. Let the decoded output be O.
   1538     // There are 256 possible values each byte in O can be; thus we must find
   1539     // the maximum nonnegative integer m such that:
   1540     // 256^m = (2^8)^m = 2^(8m) <= 64^n = (2^6)^n = 2^(6n)
   1541     // <==>
   1542     // lg(2^(8m)) = 8m <= lg(2^(6n)) = 6n   lg is defined on all positive reals which 2^(8m) and 2^(6n) are
   1543     // <==>
   1544     // m <= 6n/8 = 3n/4
   1545     // Clearly that corresponds to m = ⌊3n/4⌋.
   1546     // From the proof in `encode_len_checked`, we know that n is a valid length
   1547     // iff n ≢ 1 (mod 4).
   1548     // We claim ⌊3n/4⌋ = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋.
   1549     // Proof:
   1550     // There are three partitions for n:
   1551     // (1) 4i = n ≡ 0 (mod 4) for some integer i
   1552     //   <==>
   1553     //   ⌊3n/4⌋ = ⌊3(4i)/4⌋ = ⌊3i⌋ = 3i = 3⌊i⌋ = 3⌊4i/4⌋ = 3⌊n/4⌋ + 0 = 3⌊n/4⌋ + ⌊3(0)/4⌋ = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋
   1554     // (2) 4i + 2 = n ≡ 2 (mod 4) for some integer i
   1555     //   <==>
   1556     //   ⌊3n/4⌋ = ⌊3(4i + 2)/4⌋ = ⌊3i + 6/4⌋ = 3i + ⌊6/4⌋ = 3i + 1 = 3⌊i⌋ + ⌊3(2)/4⌋
   1557     //                                                             = 3⌊(4i + 2)/4⌋ + ⌊3((4i + 2) mod 4)/4⌋
   1558     //                                                             = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋
   1559     // (3) 4i + 3 = n ≡ 3 (mod 4) for some integer i
   1560     //   <==>
   1561     //   ⌊3n/4⌋ = ⌊3(4i + 3)/4⌋ = ⌊3i + 9/4⌋ = 3i + ⌊9/4⌋ = 3i + 2 = 3⌊i⌋ + ⌊3(3)/4⌋
   1562     //                                                             = 3⌊(4i + 3)/4⌋ + ⌊3((4i + 3) mod 4)/4⌋
   1563     //                                                             = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋
   1564     // QED
   1565     // Naively implementing ⌊3n/4⌋ as (3 * n) / 4 can cause overflow due to `3 * n`; thus
   1566     // we implement the equivalent equation 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋ instead:
   1567     // `(3 * (n / 4)) + ((3 * (n % 4)) / 4)` since none of the intermediate calculations suffer
   1568     // from overflow.
   1569     // `input_length % 4`.
   1570     let rem = input_length & 3;
   1571     if rem == 1 {
   1572         None
   1573     } else {
   1574         // 3 * (n >> 2u8) <= m < usize::MAX; thus the left operand of + is fine.
   1575         // rem <= 3
   1576         // <==>
   1577         // 3rem <= 9 < usize::MAX; thus 3 * rem is fine.
   1578         // <==>
   1579         // ⌊3rem/4⌋ <= 3rem, so the right operand of + is fine.
   1580         // The sum is fine since
   1581         // m = ⌊3n/4⌋ = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋ = (3 * (n >> 2u8)) + ((3 * rem) >> 2u8), and m < usize::MAX.
   1582         Some((3 * (input_length >> 2u8)) + ((3 * rem) >> 2u8))
   1583     }
   1584 }
   1585 /// Ordinal numbers from first to fourth inclusively.
   1586 enum FourOrdinal {
   1587     /// First.
   1588     First,
   1589     /// Second.
   1590     Second,
   1591     /// Third.
   1592     Third,
   1593     /// Fourth.
   1594     Fourth,
   1595 }
   1596 /// Error returned from [`decode_buffer`] and [`decode`].
   1597 #[derive(Clone, Debug, Eq, PartialEq)]
   1598 pub enum DecodeErr {
   1599     /// The encoded input had an invalid length.
   1600     EncodedLen,
   1601     /// The buffer supplied had a length that was too small to contain the decoded data.
   1602     BufferLen,
   1603     /// The encoded data contained trailing bits that were not zero.
   1604     TrailingBits,
   1605     /// The encoded data contained an invalid `u8`.
   1606     InvalidByte,
   1607     /// [`decode`] could not allocate enough memory to contain the decoded data.
   1608     #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1609     #[cfg(feature = "alloc")]
   1610     TryReserve(TryReserveError),
   1611 }
   1612 #[cfg_attr(docsrs, doc(cfg(not(feature = "alloc"))))]
   1613 #[cfg(not(feature = "alloc"))]
   1614 impl Copy for DecodeErr {}
   1615 impl Display for DecodeErr {
   1616     #[inline]
   1617     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
   1618         match *self {
   1619             Self::EncodedLen => f.write_str("length of encoded data was invalid"),
   1620             Self::BufferLen => {
   1621                 f.write_str("length of the output buffer is too small to contain the decoded data")
   1622             }
   1623             Self::TrailingBits => {
   1624                 f.write_str("encoded data contained trailing bits that were not zero")
   1625             }
   1626             Self::InvalidByte => f.write_str("encoded data contained an invalid byte"),
   1627             #[cfg(feature = "alloc")]
   1628             Self::TryReserve(ref err) => err.fmt(f),
   1629         }
   1630     }
   1631 }
   1632 impl Error for DecodeErr {}
   1633 /// Decodes `input` into `output` returning the subset of `output` containing the decoded data.
   1634 ///
   1635 /// # Errors
   1636 ///
   1637 /// Errors iff [`decode_len`] of `input.len()` does not return `Some` containing a
   1638 /// `usize` that does not exceed `ouput.len()` or `input` is an invalid base64url-encoded value without padding.
   1639 /// Note [`DecodeErr::TryReserve`] will never be returned.
   1640 ///
   1641 /// # Examples
   1642 ///
   1643 /// ```
   1644 /// # use base64url_nopad::DecodeErr;
   1645 /// assert_eq!(base64url_nopad::decode_buffer([0; 0].as_slice(), [0; 0].as_mut_slice())?, b"");
   1646 /// assert_eq!(
   1647 ///     base64url_nopad::decode_buffer([0; 1].as_slice(), [0; 0].as_mut_slice()).unwrap_err(),
   1648 ///     DecodeErr::EncodedLen
   1649 /// );
   1650 /// assert_eq!(
   1651 ///     base64url_nopad::decode_buffer([0; 2].as_slice(), [0; 3].as_mut_slice()).unwrap_err(),
   1652 ///     DecodeErr::InvalidByte
   1653 /// );
   1654 /// assert_eq!(
   1655 ///     base64url_nopad::decode_buffer([0; 2].as_slice(), [0; 0].as_mut_slice()).unwrap_err(),
   1656 ///     DecodeErr::BufferLen
   1657 /// );
   1658 /// assert_eq!(
   1659 ///     base64url_nopad::decode_buffer(b"-8", [0; 3].as_mut_slice()).unwrap_err(),
   1660 ///     DecodeErr::TrailingBits
   1661 /// );
   1662 /// // A larger output buffer than necessary is OK.
   1663 /// assert_eq!(base64url_nopad::decode_buffer(b"C8Aa_A--91VZbx0", &mut [0; 128])?, [0x0b, 0xc0, 0x1a, 0xfc, 0x0f, 0xbe, 0xf7, b'U', b'Y', b'o', 0x1d]);
   1664 /// # Ok::<_, DecodeErr>(())
   1665 /// ```
   1666 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1667 #[expect(
   1668     clippy::arithmetic_side_effects,
   1669     clippy::indexing_slicing,
   1670     reason = "comments justify correctness"
   1671 )]
   1672 #[inline]
   1673 pub const fn decode_buffer<'a>(
   1674     mut input: &[u8],
   1675     output: &'a mut [u8],
   1676 ) -> Result<&'a mut [u8], DecodeErr> {
   1677     if let Some(output_len) = decode_len(input.len()) {
   1678         if output.len() >= output_len {
   1679             // `input.len() % 4`.
   1680             let len = input.len() & 3;
   1681             // A trailing `Alphabet` is added iff the encode value is not a multiple of 4 (i.e., len % 4 != 0).
   1682             match len {
   1683                 2 => {
   1684                     // We know `input` is not empty; otherwise `len % 3 == 0`.
   1685                     if let Some(val) = Alphabet::from_ascii(input[input.len() - 1]) {
   1686                         if val.to_u8().trailing_zeros() < 4 {
   1687                             return Err(DecodeErr::TrailingBits);
   1688                         }
   1689                     } else {
   1690                         return Err(DecodeErr::InvalidByte);
   1691                     }
   1692                 }
   1693                 3 => {
   1694                     // We know `input` is not empty; otherwise `len % 3 == 0`.
   1695                     if let Some(val) = Alphabet::from_ascii(input[input.len() - 1]) {
   1696                         if val.to_u8().trailing_zeros() < 2 {
   1697                             return Err(DecodeErr::TrailingBits);
   1698                         }
   1699                     } else {
   1700                         return Err(DecodeErr::InvalidByte);
   1701                     }
   1702                 }
   1703                 // The only possible value is `0` since if `len` were `1`, `decode_len` would have failed.
   1704                 _ => {}
   1705             }
   1706             let mut val = 0;
   1707             let mut output_idx = 0;
   1708             let mut counter = FourOrdinal::First;
   1709             while let [mut first, ref rest @ ..] = *input {
   1710                 if let Some(base64) = Alphabet::from_ascii(first) {
   1711                     first = base64.to_u8();
   1712                     match counter {
   1713                         FourOrdinal::First => {
   1714                             val = first << 2;
   1715                             counter = FourOrdinal::Second;
   1716                         }
   1717                         FourOrdinal::Second => {
   1718                             output[output_idx] = val | (first >> 4);
   1719                             val = first << 4;
   1720                             counter = FourOrdinal::Third;
   1721                             output_idx += 1;
   1722                         }
   1723                         FourOrdinal::Third => {
   1724                             output[output_idx] = val | (first >> 2);
   1725                             val = first << 6;
   1726                             counter = FourOrdinal::Fourth;
   1727                             output_idx += 1;
   1728                         }
   1729                         FourOrdinal::Fourth => {
   1730                             output[output_idx] = val | first;
   1731                             counter = FourOrdinal::First;
   1732                             output_idx += 1;
   1733                         }
   1734                     }
   1735                     input = rest;
   1736                 } else {
   1737                     return Err(DecodeErr::InvalidByte);
   1738                 }
   1739             }
   1740             // SAFETY:
   1741             // `output.len() >= output_len`.
   1742             Ok(unsafe { output.split_at_mut_unchecked(output_len) }.0)
   1743         } else {
   1744             Err(DecodeErr::BufferLen)
   1745         }
   1746     } else {
   1747         Err(DecodeErr::EncodedLen)
   1748     }
   1749 }
   1750 /// Similar to [`decode_buffer`] except a `Vec` is returned instead using its buffer to write to.
   1751 ///
   1752 /// # Errors
   1753 ///
   1754 /// Errors iff [`decode_buffer`] errors or an error occurs from allocating the capacity needed to contain
   1755 /// the decoded data. Note [`DecodeErr::BufferLen`] is not possible to be returned.
   1756 ///
   1757 /// # Examples
   1758 ///
   1759 /// ```
   1760 /// # use base64url_nopad::DecodeErr;
   1761 /// assert_eq!(base64url_nopad::decode([0; 0].as_slice())?, b"");
   1762 /// assert_eq!(
   1763 ///     base64url_nopad::decode([0; 1].as_slice()).unwrap_err(),
   1764 ///     DecodeErr::EncodedLen
   1765 /// );
   1766 /// assert_eq!(
   1767 ///     base64url_nopad::decode([0; 2].as_slice()).unwrap_err(),
   1768 ///     DecodeErr::InvalidByte
   1769 /// );
   1770 /// assert_eq!(
   1771 ///     base64url_nopad::decode(b"-8").unwrap_err(),
   1772 ///     DecodeErr::TrailingBits
   1773 /// );
   1774 /// assert_eq!(base64url_nopad::decode(b"C8Aa_A--91VZbx0")?, [0x0b, 0xc0, 0x1a, 0xfc, 0x0f, 0xbe, 0xf7, b'U', b'Y', b'o', 0x1d]);
   1775 /// # Ok::<_, DecodeErr>(())
   1776 /// ```
   1777 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1778 #[cfg(feature = "alloc")]
   1779 #[inline]
   1780 pub fn decode(input: &[u8]) -> Result<Vec<u8>, DecodeErr> {
   1781     decode_len(input.len())
   1782         .ok_or(DecodeErr::EncodedLen)
   1783         .and_then(|capacity| {
   1784             let mut buffer = Vec::new();
   1785             buffer
   1786                 .try_reserve_exact(capacity)
   1787                 .map_err(DecodeErr::TryReserve)
   1788                 .and_then(|()| {
   1789                     buffer.resize(capacity, 0);
   1790                     if let Err(e) = decode_buffer(input, buffer.as_mut_slice()) {
   1791                         Err(e)
   1792                     } else {
   1793                         Ok(buffer)
   1794                     }
   1795                 })
   1796         })
   1797 }
   1798 /// Similar to [`decode_buffer`] except the data is not decoded.
   1799 ///
   1800 /// In some situations, one does not want to actually decode data but merely validate that the encoded data
   1801 /// is valid base64url without padding. Since data is not actually decoded, one avoids the need to allocate
   1802 /// a large-enough buffer first.
   1803 ///
   1804 /// # Errors
   1805 ///
   1806 /// Errors iff `input` is an invalid base64url without padding.
   1807 ///
   1808 /// Note since no buffer is used to decode the data into, neither [`DecodeErr::BufferLen`] nor
   1809 /// [`DecodeErr::TryReserve`] will ever be returned.
   1810 ///
   1811 /// # Examples
   1812 ///
   1813 /// ```
   1814 /// # use base64url_nopad::DecodeErr;
   1815 /// base64url_nopad::validate_encoded_data([0; 0].as_slice())?;
   1816 /// assert_eq!(
   1817 ///     base64url_nopad::validate_encoded_data([0; 1].as_slice()).unwrap_err(),
   1818 ///     DecodeErr::EncodedLen
   1819 /// );
   1820 /// assert_eq!(
   1821 ///     base64url_nopad::validate_encoded_data([0; 2].as_slice()).unwrap_err(),
   1822 ///     DecodeErr::InvalidByte
   1823 /// );
   1824 /// assert_eq!(
   1825 ///     base64url_nopad::validate_encoded_data(b"-8").unwrap_err(),
   1826 ///     DecodeErr::TrailingBits
   1827 /// );
   1828 /// base64url_nopad::validate_encoded_data(b"C8Aa_A--91VZbx0")?;
   1829 /// # Ok::<_, DecodeErr>(())
   1830 /// ```
   1831 #[expect(
   1832     clippy::arithmetic_side_effects,
   1833     clippy::indexing_slicing,
   1834     reason = "comments justify correctness"
   1835 )]
   1836 #[inline]
   1837 pub const fn validate_encoded_data(mut input: &[u8]) -> Result<(), DecodeErr> {
   1838     let len = input.len();
   1839     // `len % 4`.
   1840     match len & 3 {
   1841         // `input.len()` is invalid iff it is equivalent to 1 modulo 4 per the proof in
   1842         // `decode_len`.
   1843         1 => return Err(DecodeErr::EncodedLen),
   1844         2 => {
   1845             // We know `input` is not empty; otherwise `len % 4 == 0`.
   1846             if let Some(val) = Alphabet::from_ascii(input[len - 1]) {
   1847                 if val.to_u8().trailing_zeros() < 4 {
   1848                     return Err(DecodeErr::TrailingBits);
   1849                 }
   1850             } else {
   1851                 return Err(DecodeErr::InvalidByte);
   1852             }
   1853         }
   1854         3 => {
   1855             // We know `input` is not empty; otherwise `len % 4 == 0`.
   1856             if let Some(val) = Alphabet::from_ascii(input[len - 1]) {
   1857                 if val.to_u8().trailing_zeros() < 2 {
   1858                     return Err(DecodeErr::TrailingBits);
   1859                 }
   1860             } else {
   1861                 return Err(DecodeErr::InvalidByte);
   1862             }
   1863         }
   1864         // When the input has length that is a multple of 4, then no trailing bits were added and thus
   1865         // all values are possible.
   1866         _ => {}
   1867     }
   1868     while let [first, ref rest @ ..] = *input {
   1869         if Alphabet::from_ascii(first).is_some() {
   1870             input = rest;
   1871         } else {
   1872             return Err(DecodeErr::InvalidByte);
   1873         }
   1874     }
   1875     Ok(())
   1876 }
   1877 /// Same as [`encode_buffer`] except `output` must have the _exact_ length needed to encode `input`, and the
   1878 /// encoded `str` is not returned.
   1879 ///
   1880 /// # Panics
   1881 ///
   1882 /// `panic`s iff `output` does not have the _exact_ length needed to encode `input`.
   1883 ///
   1884 /// # Examples
   1885 ///
   1886 /// ```
   1887 /// let mut buffer = [0; 256];
   1888 /// base64url_nopad::encode_buffer_exact([0; 0].as_slice(), &mut buffer[..0]);
   1889 /// base64url_nopad::encode_buffer_exact([0; 1].as_slice(), &mut buffer[..2]);
   1890 /// assert_eq!(*b"AA", buffer[..2]);
   1891 /// // Uncommenting below will cause a `panic` since the output buffer must be exact.
   1892 /// // base64url_nopad::encode_buffer_exact([255; 1].as_slice(), &mut buffer);
   1893 /// ```
   1894 #[inline]
   1895 pub const fn encode_buffer_exact(input: &[u8], output: &mut [u8]) {
   1896     assert!(
   1897         // `encode_len` won't `panic` since Rust guarantees `input.len()` is at most `isize::MAX`.
   1898         output.len() == encode_len(input.len()),
   1899         "encode_buffer_exact must be passed an output buffer whose length is exactly the length needed to encode the data"
   1900     );
   1901     _ = encode_buffer(input, output);
   1902 }
   1903 /// Same as [`decode_buffer`] except `output` must have the _exact_ length needed, and the decoded `slice`
   1904 /// is not returned.
   1905 ///
   1906 /// # Errors
   1907 ///
   1908 /// Errors iff [`decode_buffer`] errors. Note that since a `panic` occurs when `output.len()` is not the
   1909 /// exact length needed, [`DecodeErr::BufferLen`] is not possible in addition to [`DecodeErr::TryReserve`].
   1910 ///
   1911 /// # Panics
   1912 ///
   1913 /// `panic`s iff `output` does not have the _exact_ length needed to contain the decoded data. Note when `input`
   1914 /// contains an invalid length, [`DecodeErr::EncodedLen`] is returned _not_ a `panic`.
   1915 ///
   1916 /// # Examples
   1917 ///
   1918 /// ```
   1919 /// # use base64url_nopad::DecodeErr;
   1920 /// assert_eq!(
   1921 ///     base64url_nopad::decode_buffer_exact([0; 1].as_slice(), [0; 0].as_mut_slice()).unwrap_err(),
   1922 ///     DecodeErr::EncodedLen
   1923 /// );
   1924 /// assert_eq!(
   1925 ///     base64url_nopad::decode_buffer_exact([0; 2].as_slice(), [0; 1].as_mut_slice()).unwrap_err(),
   1926 ///     DecodeErr::InvalidByte
   1927 /// );
   1928 /// assert_eq!(
   1929 ///     base64url_nopad::decode_buffer_exact(b"-8", [0; 1].as_mut_slice()).unwrap_err(),
   1930 ///     DecodeErr::TrailingBits
   1931 /// );
   1932 /// let mut buffer = [0; base64url_nopad::decode_len(b"C8Aa_A--91VZbx0".len()).unwrap()];
   1933 /// base64url_nopad::decode_buffer(b"C8Aa_A--91VZbx0", &mut buffer)?;
   1934 /// assert_eq!(buffer, [0x0b, 0xc0, 0x1a, 0xfc, 0x0f, 0xbe, 0xf7, b'U', b'Y', b'o', 0x1d]);
   1935 /// // Uncommenting below will cause a `panic` since a larger output buffer than necessary is _not_ OK.
   1936 /// // base64url_nopad::decode_buffer_exact(b"C8Aa_A--91VZbx0", &mut [0; 128])?;
   1937 /// # Ok::<_, DecodeErr>(())
   1938 /// ```
   1939 #[expect(
   1940     clippy::panic_in_result_fn,
   1941     reason = "purpose of this function is to panic when output does not have the exact length needed"
   1942 )]
   1943 #[inline]
   1944 pub const fn decode_buffer_exact(input: &[u8], output: &mut [u8]) -> Result<(), DecodeErr> {
   1945     if let Some(output_len) = decode_len(input.len()) {
   1946         assert!(
   1947             output.len() == output_len,
   1948             "decode_buffer_exact must be passed an output buffer whose length is exactly the length needed to decode the data"
   1949         );
   1950         if let Err(e) = decode_buffer(input, output) {
   1951             Err(e)
   1952         } else {
   1953             Ok(())
   1954         }
   1955     } else {
   1956         Err(DecodeErr::EncodedLen)
   1957     }
   1958 }
   1959 #[cfg(test)]
   1960 mod test {
   1961     use super::MAX_ENCODE_INPUT_LEN;
   1962     #[cfg(feature = "alloc")]
   1963     use alloc::string::String;
   1964     use rand::{Rng as _, SeedableRng as _, rngs::SmallRng};
   1965     #[expect(
   1966         clippy::as_conversions,
   1967         clippy::cast_possible_truncation,
   1968         reason = "comment justifies correctness"
   1969     )]
   1970     #[cfg(any(
   1971         target_pointer_width = "16",
   1972         target_pointer_width = "32",
   1973         target_pointer_width = "64",
   1974     ))]
   1975     #[ignore = "slow"]
   1976     #[test]
   1977     fn encode_decode_len() {
   1978         assert_eq!(MAX_ENCODE_INPUT_LEN, 3 * (usize::MAX.div_ceil(4)) - 1);
   1979         let mut rng = SmallRng::from_os_rng();
   1980         for _ in 0u32..10_000_000 {
   1981             // `uN as usize` is fine since we `cfg` by pointer width.
   1982             #[cfg(target_pointer_width = "16")]
   1983             let len = rng.random::<u16>() as usize;
   1984             #[cfg(target_pointer_width = "32")]
   1985             let len = rng.random::<u32>() as usize;
   1986             #[cfg(target_pointer_width = "64")]
   1987             let len = rng.random::<u64>() as usize;
   1988             if len <= MAX_ENCODE_INPUT_LEN {
   1989                 assert!(
   1990                     super::encode_len_checked(len)
   1991                         .is_some_and(|l| super::decode_len(l).is_some_and(|orig| orig == len))
   1992                 );
   1993             } else {
   1994                 assert!(super::encode_len_checked(len).is_none());
   1995             }
   1996         }
   1997         for i in 0..1025 {
   1998             assert!(
   1999                 super::encode_len_checked(i)
   2000                     .is_some_and(|l| super::decode_len(l).is_some_and(|orig| orig == i))
   2001             );
   2002         }
   2003         #[cfg(target_pointer_width = "16")]
   2004         for i in MAX_ENCODE_INPUT_LEN + 1.. {
   2005             assert!(super::encode_len_checked(i).is_none());
   2006         }
   2007         #[cfg(not(target_pointer_width = "16"))]
   2008         for i in MAX_ENCODE_INPUT_LEN + 1..MAX_ENCODE_INPUT_LEN + 1_000_000 {
   2009             assert!(super::encode_len_checked(i).is_none());
   2010         }
   2011         assert!(super::encode_len_checked(usize::MAX).is_none());
   2012         assert!(super::encode_len_checked(MAX_ENCODE_INPUT_LEN).is_some_and(|l| l == usize::MAX));
   2013         for _ in 0u32..10_000_000 {
   2014             #[cfg(target_pointer_width = "16")]
   2015             let len = rng.random::<u16>() as usize;
   2016             #[cfg(target_pointer_width = "32")]
   2017             let len = rng.random::<u32>() as usize;
   2018             #[cfg(target_pointer_width = "64")]
   2019             let len = rng.random::<u64>() as usize;
   2020             if len & 3 == 1 {
   2021                 assert!(super::decode_len(len).is_none());
   2022             } else {
   2023                 assert!(
   2024                     super::decode_len(len).is_some_and(
   2025                         |l| super::encode_len_checked(l).is_some_and(|orig| orig == len)
   2026                     )
   2027                 );
   2028             }
   2029         }
   2030         for i in 0..1025 {
   2031             if i & 3 == 1 {
   2032                 assert!(super::decode_len(i).is_none());
   2033             } else {
   2034                 assert!(
   2035                     super::decode_len(i).is_some_and(
   2036                         |l| super::encode_len_checked(l).is_some_and(|orig| orig == i)
   2037                     )
   2038                 );
   2039             }
   2040         }
   2041         #[cfg(target_pointer_width = "16")]
   2042         for i in 0..=usize::MAX {
   2043             if i & 3 == 1 {
   2044                 assert!(super::decode_len(i).is_none());
   2045             } else {
   2046                 assert!(super::decode_len(i).is_some_and(|l| super::encode_len_checked(l).is_some_and(|orig| orig == i)));
   2047             }
   2048         }
   2049         #[cfg(not(target_pointer_width = "16"))]
   2050         for i in usize::MAX - 1_000_000..=usize::MAX {
   2051             if i & 3 == 1 {
   2052                 assert!(super::decode_len(i).is_none());
   2053             } else {
   2054                 assert!(super::decode_len(i).is_some_and(|l| super::encode_len_checked(l).is_some_and(|orig| orig == i)));
   2055             }
   2056         }
   2057         assert!(super::decode_len(usize::MAX).is_some_and(|l| l == MAX_ENCODE_INPUT_LEN));
   2058     }
   2059     #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
   2060     #[cfg(feature = "alloc")]
   2061     #[test]
   2062     fn encode_write() {
   2063         let input = [9; 8192];
   2064         let mut buffer = String::with_capacity(super::encode_len(input.len()));
   2065         let cap = buffer.capacity();
   2066         let mut write_len;
   2067         for len in 0..input.len() {
   2068             write_len = super::encode_len(len);
   2069             match write_len.checked_add(buffer.len()) {
   2070                 None => {
   2071                     buffer.clear();
   2072                     // Indexing is fine since `len <= input.len()`.
   2073                     assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
   2074                     assert_eq!(buffer.len(), write_len);
   2075                 }
   2076                 Some(l) => {
   2077                     if l > cap {
   2078                         buffer.clear();
   2079                         // Indexing is fine since `len <= input.len()`.
   2080                         assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
   2081                         assert_eq!(buffer.len(), write_len);
   2082                     } else {
   2083                         // Indexing is fine since `len <= input.len()`.
   2084                         assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
   2085                         assert_eq!(buffer.len(), l);
   2086                     }
   2087                 }
   2088             }
   2089             assert!(
   2090                 buffer
   2091                     .as_bytes()
   2092                     .iter()
   2093                     .all(|b| { matches!(*b, b'C' | b'J' | b'Q' | b'k') })
   2094             );
   2095         }
   2096     }
   2097 }