base64url_nopad

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

lib.rs (74085B)


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