base64url_nopad

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

lib.rs (73490B)


      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     // usize::MAX >= u16::MAX
    948     // usize::MAX = 2^i - 1 where i is any integer >= 16 (due to above)
    949     // Suppose n < 3 * 2^(i-2), then:
    950     // ⌈4n/3⌉ < ⌈4*(3*2^(i-2))/3⌉ = ⌈2^i⌉
    951     //                            = 2^i
    952     //                            = usize::MAX + 1
    953     // thus ignoring intermediate calcuations, the maximum possible value is usize::MAX thus overflow is not
    954     // possible.
    955     // QED
    956     // Naively implementing ⌈4n/3⌉ as (4 * n).div_ceil(3) can cause overflow due to `4 * n`; thus
    957     // we implement the equivalent equation 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉ instead:
    958     // `(4 * (n / 3)) + (4 * (n % 3)).div_ceil(3)` since none of the intermediate calculations suffer
    959     // from overflow.
    960     // `MAX_ENCODE_INPUT_LEN` = 3 * 2^(i-2) - 1.
    961     if input_length <= MAX_ENCODE_INPUT_LEN {
    962         // (n / 3) << 2u8 <= m <= usize::MAX; thus the left operand of + is fine.
    963         // n % 3 <= 2
    964         // <==>
    965         // 4(n % 3) <= 8 < usize::MAX; thus (n % 3) << 2u8 is fine.
    966         // <==>
    967         // ⌈4(n % 3)/3⌉ <= 4(n % 3), so the right operand of + is fine.
    968         // The sum is fine since
    969         // m = ⌈4n/3⌉ = 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉ = ((n / 3) << 2u8) + ((n % 3) << 2u8).div_ceil(3), and m <= usize::MAX.
    970         Some(((input_length / 3) << 2u8) + ((input_length % 3) << 2u8).div_ceil(3))
    971     } else {
    972         None
    973     }
    974 }
    975 /// Same as [`encode_len_checked`] except a `panic` occurs instead of `None` being returned.
    976 ///
    977 /// One should prefer this function over `encode_len_checked` when passing the length of a memory allocation
    978 /// since such a length is guaranteed to succeed.
    979 ///
    980 /// # Panics
    981 ///
    982 /// `panic`s iff [`encode_len_checked`] returns `None`.
    983 ///
    984 /// # Examples
    985 ///
    986 /// ```
    987 /// # use base64url_nopad::MAX_ENCODE_INPUT_LEN;
    988 /// // Uncommenting below will cause a `panic`.
    989 /// // base64url_nopad::encode_len(usize::MAX - 4);
    990 /// // Uncommenting below will cause a `panic`.
    991 /// // base64url_nopad::encode_len(MAX_ENCODE_INPUT_LEN + 1);
    992 /// assert_eq!(base64url_nopad::encode_len(MAX_ENCODE_INPUT_LEN), usize::MAX);
    993 /// assert!(base64url_nopad::encode_len(isize::MAX as usize) > isize::MAX as usize);
    994 /// assert_eq!(base64url_nopad::encode_len(3), 4);
    995 /// assert_eq!(base64url_nopad::encode_len(2), 3);
    996 /// assert_eq!(base64url_nopad::encode_len(1), 2);
    997 /// assert_eq!(base64url_nopad::encode_len(0), 0);
    998 /// ```
    999 #[expect(clippy::unwrap_used, reason = "comment justifies correctness")]
   1000 #[inline]
   1001 #[must_use]
   1002 pub const fn encode_len(input_length: usize) -> usize {
   1003     // A precondition for calling this function is to ensure `encode_len_checked` can't return `None`.
   1004     encode_len_checked(input_length).unwrap()
   1005 }
   1006 /// Encodes `input` into `output` re-interpreting the encoded subset of `output` as a `str` before returning it.
   1007 ///
   1008 /// `Some` is returned iff `output.len()` is large enough to write the encoded data into.
   1009 ///
   1010 /// Note since Rust guarantees all memory allocations don't exceed [`isize::MAX`] bytes, one can
   1011 /// instead call [`encode_buffer`] using a buffer whose length is at least as large as the value returned from
   1012 /// [`encode_len`] without fear of a `panic` and the benefit of getting a `str` instead of an `Option`.
   1013 ///
   1014 /// # Examples
   1015 ///
   1016 /// ```
   1017 /// assert!(
   1018 ///     base64url_nopad::encode_buffer_checked([0; 0].as_slice(), [0; 0].as_mut_slice()).is_some_and(|val| val.is_empty())
   1019 /// );
   1020 /// assert!(
   1021 ///     base64url_nopad::encode_buffer_checked([0; 1].as_slice(), [0; 2].as_mut_slice()).is_some_and(|val| val == "AA")
   1022 /// );
   1023 /// // A larger output buffer than necessary is OK.
   1024 /// assert!(
   1025 ///     base64url_nopad::encode_buffer_checked([1; 1].as_slice(), [0; 128].as_mut_slice()).is_some_and(|val| val == "AQ")
   1026 /// );
   1027 /// assert!(
   1028 ///     base64url_nopad::encode_buffer_checked(
   1029 ///         [0xc9; 14].as_slice(),
   1030 ///         [0; base64url_nopad::encode_len(14)].as_mut_slice()
   1031 ///     ).is_some_and(|val| val == "ycnJycnJycnJycnJyck")
   1032 /// );
   1033 /// assert!(base64url_nopad::encode_buffer_checked([0; 1].as_slice(), [0; 1].as_mut_slice()).is_none());
   1034 /// ```
   1035 #[expect(unsafe_code, reason = "comments justify correctness")]
   1036 #[expect(
   1037     clippy::arithmetic_side_effects,
   1038     clippy::indexing_slicing,
   1039     reason = "comments justify correctness"
   1040 )]
   1041 #[inline]
   1042 pub const fn encode_buffer_checked<'a>(
   1043     mut input: &[u8],
   1044     output: &'a mut [u8],
   1045 ) -> Option<&'a mut str> {
   1046     // This won't `panic` since Rust guarantees that all memory allocations won't exceed `isize::MAX`.
   1047     let final_len = encode_len(input.len());
   1048     if output.len() >= final_len {
   1049         // We increment this by `1` for each `u8` in `input`. On every third `u8`, we increment it an extra
   1050         // time since we use 4 base64url `u8`s for each `u8`.
   1051         // We also verified that `output.len()` large enough; thus all indexing operations
   1052         // using it are correct, and incrementing it never results in overflow.
   1053         let mut output_idx = 0;
   1054         let mut counter = ThreeOrdinal::First;
   1055         let mut trailing = 0;
   1056         let mut shift;
   1057         while let [first, ref rest @ ..] = *input {
   1058             match counter {
   1059                 ThreeOrdinal::First => {
   1060                     // We trim the last two bits and interpret `first` as a 6-bit integer.
   1061                     shift = first >> 2;
   1062                     // SAFETY:
   1063                     // `shift <= 63` since we shifted at least two bits to the right.
   1064                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1065                     // The two bits we trimmed are the first two bits of the next 6-bit integer.
   1066                     trailing = (first & 3) << 4;
   1067                     counter = ThreeOrdinal::Second;
   1068                 }
   1069                 ThreeOrdinal::Second => {
   1070                     // We trim the last four bits and interpret `first` as a 6-bit integer.
   1071                     // The first two bits are the trailing 2 bits from the previous value.
   1072                     shift = trailing | (first >> 4);
   1073                     // SAFETY:
   1074                     // `shift <= 63` since `first` was shifted at least two bits to the right, and
   1075                     // `trailing = (first & 3) << 4` which means its high two bits are 0 as well.
   1076                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1077                     // The four bits we trimmed are the first four bits of the next 6-bit integer.
   1078                     trailing = (first & 15) << 2;
   1079                     counter = ThreeOrdinal::Third;
   1080                 }
   1081                 ThreeOrdinal::Third => {
   1082                     // We trim the last six bits and interpret `first` as a 6-bit integer.
   1083                     // The first four bits are the trailing 4 bits from the previous value.
   1084                     shift = trailing | (first >> 6);
   1085                     // SAFETY:
   1086                     // `shift <= 63` since `first` was shifted at least two bits to the right, and
   1087                     // `trailing = (first & 15) << 2` which means its high two bits are 0 as well.
   1088                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1089                     // Every third `u8` corresponds to a fourth base64url `u8`.
   1090                     output_idx += 1;
   1091                     // We use the 6 bits we just trimmed.
   1092                     shift = first & 63;
   1093                     // SAFETY:
   1094                     // `shift <= 63` since `first & 63` is.
   1095                     output[output_idx] = unsafe { Alphabet::from_u8_unchecked(shift) }.to_ascii();
   1096                     counter = ThreeOrdinal::First;
   1097                 }
   1098             }
   1099             input = rest;
   1100             output_idx += 1;
   1101         }
   1102         if !matches!(counter, ThreeOrdinal::First) {
   1103             // `input.len()` is not a multiple of 3; thus we have to append a trailing base64url `u8` that
   1104             // is simply the current value of `trailing`.
   1105             // SAFETY:
   1106             // `trailing <= 63` since `trailing` is either `(first & 3) << 4` or `(first & 15) << 2` where
   1107             // `first` is any `u8`. This means the high two bits are guaranteed to be 0.
   1108             output[output_idx] = unsafe { Alphabet::from_u8_unchecked(trailing) }.to_ascii();
   1109         }
   1110         // SAFETY:
   1111         // We verified `output.len() >= final_len`.
   1112         let val = unsafe { output.split_at_mut_unchecked(final_len) }.0;
   1113         // SAFETY:
   1114         // `val` has the exact length needed to encode `input`, and all of the `u8`s in it
   1115         // are from `Alphabet::to_ascii` which is a subset of UTF-8; thus this is safe.
   1116         // Note the above is vacuously true when `val` is empty.
   1117         Some(unsafe { str::from_utf8_unchecked_mut(val) })
   1118     } else {
   1119         None
   1120     }
   1121 }
   1122 /// Same as [`encode_buffer_checked`] except a `panic` occurs instead of `None` being returned.
   1123 ///
   1124 /// # Panics
   1125 ///
   1126 /// `panic`s iff [`encode_buffer_checked`] returns `None` (i.e., the length of the output buffer is too small).
   1127 ///
   1128 /// # Examples
   1129 ///
   1130 /// ```
   1131 /// assert_eq!(
   1132 ///     base64url_nopad::encode_buffer([0; 0].as_slice(), [0; 0].as_mut_slice()),
   1133 ///     ""
   1134 /// );
   1135 /// assert_eq!(
   1136 ///     base64url_nopad::encode_buffer([0; 1].as_slice(), [0; 2].as_mut_slice()),
   1137 ///     "AA"
   1138 /// );
   1139 /// // A larger output buffer than necessary is OK.
   1140 /// assert_eq!(
   1141 ///     base64url_nopad::encode_buffer([255; 1].as_slice(), [0; 256].as_mut_slice()),
   1142 ///     "_w"
   1143 /// );
   1144 /// assert_eq!(
   1145 ///     base64url_nopad::encode_buffer(
   1146 ///         [0xc9; 14].as_slice(),
   1147 ///         [0; base64url_nopad::encode_len(14)].as_mut_slice()
   1148 ///     ),
   1149 ///     "ycnJycnJycnJycnJyck"
   1150 /// );
   1151 /// // The below will `panic` when uncommented since the supplied output buffer is too small.
   1152 /// // _ = base64url_nopad::encode_buffer([0; 1].as_slice(), [0; 1].as_mut_slice());
   1153 /// ```
   1154 #[expect(clippy::unwrap_used, reason = "comment justifies correctness")]
   1155 #[inline]
   1156 pub const fn encode_buffer<'a>(input: &[u8], output: &'a mut [u8]) -> &'a mut str {
   1157     // A precondition for calling this function is to ensure `encode_buffer_checked` can't return `None`.
   1158     encode_buffer_checked(input, output).unwrap()
   1159 }
   1160 /// Similar to [`encode_buffer`] except a `String` is returned instead using its buffer to write to.
   1161 ///
   1162 /// # Errors
   1163 ///
   1164 /// Errors iff an error occurs from allocating the capacity needed to contain the encoded data.
   1165 ///
   1166 /// # Examples
   1167 ///
   1168 /// ```
   1169 /// # extern crate alloc;
   1170 /// # use alloc::collections::TryReserveError;
   1171 /// assert_eq!(
   1172 ///     base64url_nopad::try_encode([0; 0].as_slice())?,
   1173 ///     ""
   1174 /// );
   1175 /// assert_eq!(
   1176 ///     base64url_nopad::try_encode([0; 1].as_slice())?,
   1177 ///     "AA"
   1178 /// );
   1179 /// assert_eq!(
   1180 ///     base64url_nopad::try_encode([128, 40, 3].as_slice())?,
   1181 ///     "gCgD"
   1182 /// );
   1183 /// assert_eq!(
   1184 ///     base64url_nopad::try_encode([0x7b; 22].as_slice())?,
   1185 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1186 /// );
   1187 /// # Ok::<_, TryReserveError>(())
   1188 /// ```
   1189 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1190 #[cfg(feature = "alloc")]
   1191 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1192 #[inline]
   1193 pub fn try_encode(input: &[u8]) -> Result<String, TryReserveError> {
   1194     let mut output = Vec::new();
   1195     // `encode_len` won't `panic` since Rust guarantees `input.len()` will not return a value greater
   1196     // than `isize::MAX`.
   1197     let len = encode_len(input.len());
   1198     output.try_reserve_exact(len).map(|()| {
   1199         output.resize(len, 0);
   1200         _ = encode_buffer(input, output.as_mut_slice());
   1201         // SAFETY:
   1202         // `output` has the exact length needed to encode `input`, and all of the `u8`s in it
   1203         // are from `Alphabet` which is a subset of UTF-8; thus this is safe.
   1204         // Note the above is vacuously true when `output` is empty.
   1205         unsafe { String::from_utf8_unchecked(output) }
   1206     })
   1207 }
   1208 /// Same as [`try_encode`] except a `panic` occurs on allocation failure.
   1209 ///
   1210 /// # Panics
   1211 ///
   1212 /// `panic`s iff [`try_encode`] errors.
   1213 ///
   1214 /// # Examples
   1215 ///
   1216 /// ```
   1217 /// assert_eq!(
   1218 ///     base64url_nopad::encode([0; 0].as_slice()),
   1219 ///     ""
   1220 /// );
   1221 /// assert_eq!(
   1222 ///     base64url_nopad::encode([0; 1].as_slice()),
   1223 ///     "AA"
   1224 /// );
   1225 /// assert_eq!(
   1226 ///     base64url_nopad::encode([128, 40, 3].as_slice()),
   1227 ///     "gCgD"
   1228 /// );
   1229 /// assert_eq!(
   1230 ///     base64url_nopad::encode([0x7b; 22].as_slice()),
   1231 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1232 /// );
   1233 /// ```
   1234 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1235 #[cfg(feature = "alloc")]
   1236 #[expect(
   1237     clippy::unwrap_used,
   1238     reason = "purpose of function is to panic on allocation failure"
   1239 )]
   1240 #[inline]
   1241 #[must_use]
   1242 pub fn encode(input: &[u8]) -> String {
   1243     try_encode(input).unwrap()
   1244 }
   1245 /// Writes the base64url encoding of `input` using `writer`.
   1246 ///
   1247 /// Internally a buffer of at most 1024 bytes is used to write the encoded data. Smaller buffers may be used
   1248 /// for small inputs.
   1249 ///
   1250 /// # Errors
   1251 ///
   1252 /// Errors iff [`Write::write_str`] does.
   1253 ///
   1254 /// # Panics
   1255 ///
   1256 /// `panic`s iff [`Write::write_str`] does.
   1257 ///
   1258 /// # Examples
   1259 ///
   1260 /// ```
   1261 /// # extern crate alloc;
   1262 /// # use alloc::string::String;
   1263 /// # use core::fmt::Error;
   1264 /// let mut buffer = String::new();
   1265 /// base64url_nopad::encode_write([0; 0].as_slice(), &mut buffer)?;
   1266 /// assert_eq!(buffer, "");
   1267 /// buffer.clear();
   1268 /// base64url_nopad::encode_write([0; 1].as_slice(), &mut buffer)?;
   1269 /// assert_eq!(buffer, "AA");
   1270 /// buffer.clear();
   1271 /// base64url_nopad::encode_write(
   1272 ///     [0xc9; 14].as_slice(),
   1273 ///     &mut buffer,
   1274 /// )?;
   1275 /// assert_eq!(buffer, "ycnJycnJycnJycnJyck");
   1276 /// # Ok::<_, Error>(())
   1277 /// ```
   1278 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1279 #[expect(
   1280     clippy::arithmetic_side_effects,
   1281     clippy::as_conversions,
   1282     clippy::indexing_slicing,
   1283     reason = "comments justify correctness"
   1284 )]
   1285 #[inline]
   1286 pub fn encode_write<W: Write>(mut input: &[u8], writer: &mut W) -> fmt::Result {
   1287     /// The minimum buffer size we use.
   1288     const MIN_BUFFER_LEN: usize = 256;
   1289     /// The medium buffer size we use.
   1290     const MID_BUFFER_LEN: usize = MIN_BUFFER_LEN << 1;
   1291     /// The max buffer size.
   1292     ///
   1293     /// This must be at least 4, no more than `i16::MAX`, and must be a power of 2.
   1294     const MAX_BUFFER_LEN: usize = MID_BUFFER_LEN << 1;
   1295     /// The minimum length of the input until we must chunk encode the data.
   1296     const LOOP_LEN: usize = MAX_BUFFER_LEN + 1;
   1297     /// Want to ensure at compilation time that `MAX_BUFFER_LEN` upholds its invariants. Namely
   1298     /// that it's at least as large as 4, doesn't exceed [`i16::MAX`], and is always a power of 2.
   1299     const _: () = {
   1300         // `i16::MAX <= usize::MAX`, so this is fine.
   1301         /// `i16::MAX`.
   1302         const MAX_LEN: usize = i16::MAX as usize;
   1303         assert!(
   1304             4 <= MAX_BUFFER_LEN && MAX_BUFFER_LEN < MAX_LEN && MAX_BUFFER_LEN.is_power_of_two(),
   1305             "encode_write::MAX_BUFFER_LEN must be a power of two less than i16::MAX but at least as large as 4"
   1306         );
   1307     };
   1308     /// The input size that corresponds to an encoded value of length `MAX_BUFFER_LEN`.
   1309     // This will never `panic` since `MAX_BUFFER_LEN` is a power of two at least as large as 4
   1310     // (i.e., `MAX_BUFFER_LEN` ≢ 1 (mod 4)).
   1311     const INPUT_LEN: usize = decode_len(MAX_BUFFER_LEN).unwrap();
   1312     // This won't `panic` since `input.len()` is guaranteed to be no more than `isize::MAX`.
   1313     let len = encode_len(input.len());
   1314     match len {
   1315         0 => Ok(()),
   1316         1..=MIN_BUFFER_LEN => {
   1317             let mut buffer = [0; MIN_BUFFER_LEN];
   1318             // `buffer.len() == MIN_BUFFER_LEN >= len`, so indexing is fine.
   1319             // `encode_buffer` won't `panic` since `len` is the exact number of bytes needed to encode
   1320             // the data.
   1321             writer.write_str(encode_buffer(input, &mut buffer[..len]))
   1322         }
   1323         257..=MID_BUFFER_LEN => {
   1324             let mut buffer = [0; MID_BUFFER_LEN];
   1325             // `buffer.len() == MID_BUFFER_LEN >= len`, so indexing is fine.
   1326             // `encode_buffer` won't `panic` since `len` is the exact number of bytes needed to encode
   1327             // the data.
   1328             writer.write_str(encode_buffer(input, &mut buffer[..len]))
   1329         }
   1330         513..=MAX_BUFFER_LEN => {
   1331             let mut buffer = [0; MAX_BUFFER_LEN];
   1332             // `buffer.len() == MAX_BUFFER_LEN >= len`, so indexing is fine.
   1333             // `encode_buffer` won't `panic` since `len` is the exact number of bytes needed to encode
   1334             // the data.
   1335             writer.write_str(encode_buffer(input, &mut buffer[..len]))
   1336         }
   1337         LOOP_LEN.. => {
   1338             let mut buffer = [0; MAX_BUFFER_LEN];
   1339             let mut counter = 0;
   1340             // `len / MAX_BUFFER_LEN` is equal to ⌊len / MAX_BUFFER_LEN⌋ since `MAX_BUFFER_LEN` is a power of two.
   1341             // We can safely encode `term` chunks of `INPUT_LEN` length into `buffer`.
   1342             let term = len >> MAX_BUFFER_LEN.trailing_zeros();
   1343             let mut input_buffer;
   1344             while counter < term {
   1345                 // SAFETY:
   1346                 // `input.len() >= INPUT_LEN`.
   1347                 input_buffer = unsafe { input.split_at_unchecked(INPUT_LEN) };
   1348                 // `encode_buffer` won't `panic` since `buffer` has length `MAX_BUFFER_LEN` which
   1349                 // is the exact length needed for `INPUT_LEN` length inputs which `input_buffer.0` is.
   1350                 writer.write_str(encode_buffer(input_buffer.0, buffer.as_mut_slice()))?;
   1351                 input = input_buffer.1;
   1352                 // `counter < term`, so overflow cannot happen.
   1353                 counter += 1;
   1354             }
   1355             // `encode_len` won't `panic` since `input.len() < MAX_ENCODE_INPUT_LEN`.
   1356             // `input.len() < INPUT_LEN`; thus `encode_len(input.len()) < MAX_BUFFER_LEN = buffer.len()` so
   1357             // indexing is fine.
   1358             // `encode_buffer` won't `panic` since the buffer is the exact length needed to encode `input`.
   1359             writer.write_str(encode_buffer(input, &mut buffer[..encode_len(input.len())]))
   1360         }
   1361     }
   1362 }
   1363 /// Appends the base64url encoding of `input` to `s` returning the `str` that was appended.
   1364 ///
   1365 /// # Errors
   1366 ///
   1367 /// Errors iff an error occurs from allocating the capacity needed to append the encoded data.
   1368 ///
   1369 /// # Examples
   1370 ///
   1371 /// ```
   1372 /// # extern crate alloc;
   1373 /// # use alloc::{collections::TryReserveError, string::String};
   1374 /// let mut buffer = String::new();
   1375 /// assert_eq!(
   1376 ///     base64url_nopad::try_encode_append([0; 0].as_slice(), &mut buffer)?,
   1377 ///     ""
   1378 /// );
   1379 /// assert_eq!(
   1380 ///     base64url_nopad::try_encode_append([0; 1].as_slice(), &mut buffer)?,
   1381 ///     "AA"
   1382 /// );
   1383 /// assert_eq!(
   1384 ///     base64url_nopad::try_encode_append([128, 40, 3].as_slice(), &mut buffer)?,
   1385 ///     "gCgD"
   1386 /// );
   1387 /// assert_eq!(buffer, "AAgCgD");
   1388 /// assert_eq!(
   1389 ///     base64url_nopad::try_encode_append([0x7b; 22].as_slice(), &mut buffer)?,
   1390 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1391 /// );
   1392 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1393 /// # Ok::<_, TryReserveError>(())
   1394 /// ```
   1395 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1396 #[cfg(feature = "alloc")]
   1397 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1398 #[expect(
   1399     clippy::arithmetic_side_effects,
   1400     clippy::indexing_slicing,
   1401     reason = "comments justify correctness"
   1402 )]
   1403 #[inline]
   1404 pub fn try_encode_append<'a>(
   1405     input: &[u8],
   1406     s: &'a mut String,
   1407 ) -> Result<&'a mut str, TryReserveError> {
   1408     // `encode_len` won't `panic` since Rust guarantees `input.len()` will return a value no larger
   1409     // than `isize::MAX`.
   1410     let additional_len = encode_len(input.len());
   1411     s.try_reserve_exact(additional_len).map(|()| {
   1412         // SAFETY:
   1413         // We only append base64url ASCII which is a subset of UTF-8, so this will remain valid UTF-8.
   1414         let utf8 = unsafe { s.as_mut_vec() };
   1415         let original_len = utf8.len();
   1416         // Overflow can't happen; otherwise `s.try_reserve_exact` would have erred.
   1417         utf8.resize(original_len + additional_len, 0);
   1418         // `utf8.len() >= original_len`, so indexing is fine.
   1419         // `encode_buffer` won't `panic` since `utf8[original_len..]` has length `additional_len`
   1420         // which is the exact number of bytes needed to encode `input`.
   1421         encode_buffer(input, &mut utf8[original_len..])
   1422     })
   1423 }
   1424 /// Same as [`try_encode_append`] except the encoded `str` is not returned.
   1425 ///
   1426 /// # Errors
   1427 ///
   1428 /// Errors iff [`try_encode_append`] does.
   1429 ///
   1430 /// # Examples
   1431 ///
   1432 /// ```
   1433 /// # extern crate alloc;
   1434 /// # use alloc::{collections::TryReserveError, string::String};
   1435 /// let mut buffer = String::new();
   1436 /// base64url_nopad::try_encode_append_only([0; 0].as_slice(), &mut buffer)?;
   1437 /// assert_eq!(buffer, "");
   1438 /// base64url_nopad::try_encode_append_only([0; 1].as_slice(), &mut buffer)?;
   1439 /// assert_eq!(buffer, "AA");
   1440 /// base64url_nopad::try_encode_append_only([128, 40, 3].as_slice(), &mut buffer)?;
   1441 /// assert_eq!(buffer, "AAgCgD");
   1442 /// base64url_nopad::try_encode_append_only([0x7b; 22].as_slice(), &mut buffer)?;
   1443 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1444 /// # Ok::<_, TryReserveError>(())
   1445 /// ```
   1446 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1447 #[cfg(feature = "alloc")]
   1448 #[inline]
   1449 pub fn try_encode_append_only(input: &[u8], s: &mut String) -> Result<(), TryReserveError> {
   1450     try_encode_append(input, s).map(|_| ())
   1451 }
   1452 /// Same as [`try_encode_append`] except a `panic` occurs on allocation failure.
   1453 ///
   1454 /// # Panics
   1455 ///
   1456 /// `panic`s iff [`try_encode_append`] errors.
   1457 ///
   1458 /// # Examples
   1459 ///
   1460 /// ```
   1461 /// # extern crate alloc;
   1462 /// # use alloc::{collections::TryReserveError, string::String};
   1463 /// let mut buffer = String::new();
   1464 /// assert_eq!(
   1465 ///     base64url_nopad::encode_append([0; 0].as_slice(), &mut buffer),
   1466 ///     ""
   1467 /// );
   1468 /// assert_eq!(
   1469 ///     base64url_nopad::encode_append([0; 1].as_slice(), &mut buffer),
   1470 ///     "AA"
   1471 /// );
   1472 /// assert_eq!(
   1473 ///     base64url_nopad::encode_append([128, 40, 3].as_slice(), &mut buffer),
   1474 ///     "gCgD"
   1475 /// );
   1476 /// assert_eq!(buffer, "AAgCgD");
   1477 /// assert_eq!(
   1478 ///     base64url_nopad::encode_append([0x7b; 22].as_slice(), &mut buffer),
   1479 ///     "e3t7e3t7e3t7e3t7e3t7e3t7e3t7ew"
   1480 /// );
   1481 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1482 /// ```
   1483 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1484 #[cfg(feature = "alloc")]
   1485 #[expect(
   1486     clippy::unwrap_used,
   1487     reason = "purpose of this function is to panic on allocation failure"
   1488 )]
   1489 #[inline]
   1490 pub fn encode_append<'a>(input: &[u8], s: &'a mut String) -> &'a mut str {
   1491     try_encode_append(input, s).unwrap()
   1492 }
   1493 /// Same as [`encode_append`] except the encoded `str` is not returned.
   1494 ///
   1495 /// # Panics
   1496 ///
   1497 /// `panic`s iff [`encode_append`] does.
   1498 ///
   1499 /// # Examples
   1500 ///
   1501 /// ```
   1502 /// # extern crate alloc;
   1503 /// # use alloc::{collections::TryReserveError, string::String};
   1504 /// let mut buffer = String::new();
   1505 /// base64url_nopad::encode_append_only([0; 0].as_slice(), &mut buffer);
   1506 /// assert_eq!(buffer, "");
   1507 /// base64url_nopad::encode_append_only([0; 1].as_slice(), &mut buffer);
   1508 /// assert_eq!(buffer, "AA");
   1509 /// base64url_nopad::encode_append_only([128, 40, 3].as_slice(), &mut buffer);
   1510 /// assert_eq!(buffer, "AAgCgD");
   1511 /// base64url_nopad::encode_append_only([0x7b; 22].as_slice(), &mut buffer);
   1512 /// assert_eq!(buffer, "AAgCgDe3t7e3t7e3t7e3t7e3t7e3t7e3t7ew");
   1513 /// # Ok::<_, TryReserveError>(())
   1514 /// ```
   1515 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1516 #[cfg(feature = "alloc")]
   1517 #[inline]
   1518 pub fn encode_append_only(input: &[u8], s: &mut String) {
   1519     _ = encode_append(input, s);
   1520 }
   1521 /// Returns the exact number of bytes needed to decode a base64url without padding input of length `input_length`.
   1522 ///
   1523 /// `Some` is returned iff `input_length` represents a possible length of a base64url without padding input.
   1524 ///
   1525 /// # Examples
   1526 ///
   1527 /// ```
   1528 /// # use base64url_nopad::MAX_ENCODE_INPUT_LEN;
   1529 /// assert!(base64url_nopad::decode_len(1).is_none());
   1530 /// assert!(base64url_nopad::decode_len(usize::MAX).is_some_and(|len| len == MAX_ENCODE_INPUT_LEN));
   1531 /// assert!(base64url_nopad::decode_len(4).is_some_and(|len| len == 3));
   1532 /// assert!(base64url_nopad::decode_len(3).is_some_and(|len| len == 2));
   1533 /// assert!(base64url_nopad::decode_len(2).is_some_and(|len| len == 1));
   1534 /// assert!(base64url_nopad::decode_len(0).is_some_and(|len| len == 0));
   1535 /// ```
   1536 #[expect(
   1537     clippy::arithmetic_side_effects,
   1538     reason = "proof and comment justifies their correctness"
   1539 )]
   1540 #[inline]
   1541 #[must_use]
   1542 pub const fn decode_len(input_length: usize) -> Option<usize> {
   1543     // 64^n is the number of distinct values of the input. Let the decoded output be O.
   1544     // There are 256 possible values each byte in O can be; thus we must find
   1545     // the maximum nonnegative integer m such that:
   1546     // 256^m = (2^8)^m = 2^(8m) <= 64^n = (2^6)^n = 2^(6n)
   1547     // <==>
   1548     // lg(2^(8m)) = 8m <= lg(2^(6n)) = 6n   lg is defined on all positive reals which 2^(8m) and 2^(6n) are
   1549     // <==>
   1550     // m <= 6n/8 = 3n/4
   1551     // Clearly that corresponds to m = ⌊3n/4⌋.
   1552     // From the proof in `encode_len_checked`, we know that n is a valid length
   1553     // iff n ≢ 1 (mod 4).
   1554     // We claim ⌊3n/4⌋ = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋.
   1555     // Proof:
   1556     // There are three partitions for n:
   1557     // (1) 4i = n ≡ 0 (mod 4) for some integer i
   1558     //   <==>
   1559     //   ⌊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⌋
   1560     // (2) 4i + 2 = n ≡ 2 (mod 4) for some integer i
   1561     //   <==>
   1562     //   ⌊3n/4⌋ = ⌊3(4i + 2)/4⌋ = ⌊3i + 6/4⌋ = 3i + ⌊6/4⌋ = 3i + 1 = 3⌊i⌋ + ⌊3(2)/4⌋
   1563     //                                                             = 3⌊(4i + 2)/4⌋ + ⌊3((4i + 2) mod 4)/4⌋
   1564     //                                                             = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋
   1565     // (3) 4i + 3 = n ≡ 3 (mod 4) for some integer i
   1566     //   <==>
   1567     //   ⌊3n/4⌋ = ⌊3(4i + 3)/4⌋ = ⌊3i + 9/4⌋ = 3i + ⌊9/4⌋ = 3i + 2 = 3⌊i⌋ + ⌊3(3)/4⌋
   1568     //                                                             = 3⌊(4i + 3)/4⌋ + ⌊3((4i + 3) mod 4)/4⌋
   1569     //                                                             = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋
   1570     // QED
   1571     // Naively implementing ⌊3n/4⌋ as (3 * n) / 4 can cause overflow due to `3 * n`; thus
   1572     // we implement the equivalent equation 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋ instead:
   1573     // `(3 * (n / 4)) + ((3 * (n % 4)) / 4)` since none of the intermediate calculations suffer
   1574     // from overflow.
   1575     // `input_length % 4`.
   1576     let rem = input_length & 3;
   1577     if rem == 1 {
   1578         None
   1579     } else {
   1580         // 3 * (n >> 2u8) <= m < usize::MAX; thus the left operand of + is fine.
   1581         // rem <= 3
   1582         // <==>
   1583         // 3rem <= 9 < usize::MAX; thus 3 * rem is fine.
   1584         // <==>
   1585         // ⌊3rem/4⌋ <= 3rem, so the right operand of + is fine.
   1586         // The sum is fine since
   1587         // m = ⌊3n/4⌋ = 3⌊n/4⌋ + ⌊3(n mod 4)/4⌋ = (3 * (n >> 2u8)) + ((3 * rem) >> 2u8), and m < usize::MAX.
   1588         Some((3 * (input_length >> 2u8)) + ((3 * rem) >> 2u8))
   1589     }
   1590 }
   1591 /// Ordinal numbers from first to fourth inclusively.
   1592 enum FourOrdinal {
   1593     /// First.
   1594     First,
   1595     /// Second.
   1596     Second,
   1597     /// Third.
   1598     Third,
   1599     /// Fourth.
   1600     Fourth,
   1601 }
   1602 /// Error returned from [`decode_buffer`] and [`decode`].
   1603 #[derive(Clone, Debug, Eq, PartialEq)]
   1604 pub enum DecodeErr {
   1605     /// The encoded input had an invalid length.
   1606     EncodedLen,
   1607     /// The buffer supplied had a length that was too small to contain the decoded data.
   1608     BufferLen,
   1609     /// The encoded data contained trailing bits that were not zero.
   1610     TrailingBits,
   1611     /// The encoded data contained an invalid `u8`.
   1612     InvalidByte,
   1613     /// [`decode`] could not allocate enough memory to contain the decoded data.
   1614     #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1615     #[cfg(feature = "alloc")]
   1616     TryReserve(TryReserveError),
   1617 }
   1618 #[cfg_attr(docsrs, doc(cfg(not(feature = "alloc"))))]
   1619 #[cfg(not(feature = "alloc"))]
   1620 impl Copy for DecodeErr {}
   1621 impl Display for DecodeErr {
   1622     #[inline]
   1623     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
   1624         match *self {
   1625             Self::EncodedLen => f.write_str("length of encoded data was invalid"),
   1626             Self::BufferLen => {
   1627                 f.write_str("length of the output buffer is too small to contain the decoded data")
   1628             }
   1629             Self::TrailingBits => {
   1630                 f.write_str("encoded data contained trailing bits that were not zero")
   1631             }
   1632             Self::InvalidByte => f.write_str("encoded data contained an invalid byte"),
   1633             #[cfg(feature = "alloc")]
   1634             Self::TryReserve(ref err) => err.fmt(f),
   1635         }
   1636     }
   1637 }
   1638 impl Error for DecodeErr {}
   1639 /// Decodes `input` into `output` returning the subset of `output` containing the decoded data.
   1640 ///
   1641 /// # Errors
   1642 ///
   1643 /// Errors iff [`decode_len`] of `input.len()` does not return `Some` containing a
   1644 /// `usize` that does not exceed `ouput.len()` or `input` is an invalid base64url-encoded value without padding.
   1645 /// Note [`DecodeErr::TryReserve`] will never be returned.
   1646 ///
   1647 /// # Examples
   1648 ///
   1649 /// ```
   1650 /// # use base64url_nopad::DecodeErr;
   1651 /// assert_eq!(base64url_nopad::decode_buffer([0; 0].as_slice(), [0; 0].as_mut_slice())?, b"");
   1652 /// assert_eq!(
   1653 ///     base64url_nopad::decode_buffer([0; 1].as_slice(), [0; 0].as_mut_slice()).unwrap_err(),
   1654 ///     DecodeErr::EncodedLen
   1655 /// );
   1656 /// assert_eq!(
   1657 ///     base64url_nopad::decode_buffer([0; 2].as_slice(), [0; 3].as_mut_slice()).unwrap_err(),
   1658 ///     DecodeErr::InvalidByte
   1659 /// );
   1660 /// assert_eq!(
   1661 ///     base64url_nopad::decode_buffer([0; 2].as_slice(), [0; 0].as_mut_slice()).unwrap_err(),
   1662 ///     DecodeErr::BufferLen
   1663 /// );
   1664 /// assert_eq!(
   1665 ///     base64url_nopad::decode_buffer(b"-8", [0; 3].as_mut_slice()).unwrap_err(),
   1666 ///     DecodeErr::TrailingBits
   1667 /// );
   1668 /// // A larger output buffer than necessary is OK.
   1669 /// 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]);
   1670 /// # Ok::<_, DecodeErr>(())
   1671 /// ```
   1672 #[expect(unsafe_code, reason = "comment justifies correctness")]
   1673 #[expect(
   1674     clippy::arithmetic_side_effects,
   1675     clippy::indexing_slicing,
   1676     reason = "comments justify correctness"
   1677 )]
   1678 #[inline]
   1679 pub const fn decode_buffer<'a>(
   1680     mut input: &[u8],
   1681     output: &'a mut [u8],
   1682 ) -> Result<&'a mut [u8], DecodeErr> {
   1683     if let Some(output_len) = decode_len(input.len()) {
   1684         if output.len() >= output_len {
   1685             // `input.len() % 4`.
   1686             let len = input.len() & 3;
   1687             // A trailing `Alphabet` is added iff the encode value is not a multiple of 4 (i.e., len % 4 != 0).
   1688             match len {
   1689                 2 => {
   1690                     // We know `input` is not empty; otherwise `len % 3 == 0`.
   1691                     if let Some(val) = Alphabet::from_ascii(input[input.len() - 1]) {
   1692                         if val.to_u8().trailing_zeros() < 4 {
   1693                             return Err(DecodeErr::TrailingBits);
   1694                         }
   1695                     } else {
   1696                         return Err(DecodeErr::InvalidByte);
   1697                     }
   1698                 }
   1699                 3 => {
   1700                     // We know `input` is not empty; otherwise `len % 3 == 0`.
   1701                     if let Some(val) = Alphabet::from_ascii(input[input.len() - 1]) {
   1702                         if val.to_u8().trailing_zeros() < 2 {
   1703                             return Err(DecodeErr::TrailingBits);
   1704                         }
   1705                     } else {
   1706                         return Err(DecodeErr::InvalidByte);
   1707                     }
   1708                 }
   1709                 // The only possible value is `0` since if `len` were `1`, `decode_len` would have failed.
   1710                 _ => {}
   1711             }
   1712             let mut val = 0;
   1713             let mut output_idx = 0;
   1714             let mut counter = FourOrdinal::First;
   1715             while let [mut first, ref rest @ ..] = *input {
   1716                 if let Some(base64) = Alphabet::from_ascii(first) {
   1717                     first = base64.to_u8();
   1718                     match counter {
   1719                         FourOrdinal::First => {
   1720                             val = first << 2;
   1721                             counter = FourOrdinal::Second;
   1722                         }
   1723                         FourOrdinal::Second => {
   1724                             output[output_idx] = val | (first >> 4);
   1725                             val = first << 4;
   1726                             counter = FourOrdinal::Third;
   1727                             output_idx += 1;
   1728                         }
   1729                         FourOrdinal::Third => {
   1730                             output[output_idx] = val | (first >> 2);
   1731                             val = first << 6;
   1732                             counter = FourOrdinal::Fourth;
   1733                             output_idx += 1;
   1734                         }
   1735                         FourOrdinal::Fourth => {
   1736                             output[output_idx] = val | first;
   1737                             counter = FourOrdinal::First;
   1738                             output_idx += 1;
   1739                         }
   1740                     }
   1741                     input = rest;
   1742                 } else {
   1743                     return Err(DecodeErr::InvalidByte);
   1744                 }
   1745             }
   1746             // SAFETY:
   1747             // `output.len() >= output_len`.
   1748             Ok(unsafe { output.split_at_mut_unchecked(output_len) }.0)
   1749         } else {
   1750             Err(DecodeErr::BufferLen)
   1751         }
   1752     } else {
   1753         Err(DecodeErr::EncodedLen)
   1754     }
   1755 }
   1756 /// Similar to [`decode_buffer`] except a `Vec` is returned instead using its buffer to write to.
   1757 ///
   1758 /// # Errors
   1759 ///
   1760 /// Errors iff [`decode_buffer`] errors or an error occurs from allocating the capacity needed to contain
   1761 /// the decoded data. Note [`DecodeErr::BufferLen`] is not possible to be returned.
   1762 ///
   1763 /// # Examples
   1764 ///
   1765 /// ```
   1766 /// # use base64url_nopad::DecodeErr;
   1767 /// assert_eq!(base64url_nopad::decode([0; 0].as_slice())?, b"");
   1768 /// assert_eq!(
   1769 ///     base64url_nopad::decode([0; 1].as_slice()).unwrap_err(),
   1770 ///     DecodeErr::EncodedLen
   1771 /// );
   1772 /// assert_eq!(
   1773 ///     base64url_nopad::decode([0; 2].as_slice()).unwrap_err(),
   1774 ///     DecodeErr::InvalidByte
   1775 /// );
   1776 /// assert_eq!(
   1777 ///     base64url_nopad::decode(b"-8").unwrap_err(),
   1778 ///     DecodeErr::TrailingBits
   1779 /// );
   1780 /// assert_eq!(base64url_nopad::decode(b"C8Aa_A--91VZbx0")?, [0x0b, 0xc0, 0x1a, 0xfc, 0x0f, 0xbe, 0xf7, b'U', b'Y', b'o', 0x1d]);
   1781 /// # Ok::<_, DecodeErr>(())
   1782 /// ```
   1783 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
   1784 #[cfg(feature = "alloc")]
   1785 #[inline]
   1786 pub fn decode(input: &[u8]) -> Result<Vec<u8>, DecodeErr> {
   1787     decode_len(input.len())
   1788         .ok_or(DecodeErr::EncodedLen)
   1789         .and_then(|capacity| {
   1790             let mut buffer = Vec::new();
   1791             buffer
   1792                 .try_reserve_exact(capacity)
   1793                 .map_err(DecodeErr::TryReserve)
   1794                 .and_then(|()| {
   1795                     buffer.resize(capacity, 0);
   1796                     if let Err(e) = decode_buffer(input, buffer.as_mut_slice()) {
   1797                         Err(e)
   1798                     } else {
   1799                         Ok(buffer)
   1800                     }
   1801                 })
   1802         })
   1803 }
   1804 /// Similar to [`decode_buffer`] except the data is not decoded.
   1805 ///
   1806 /// In some situations, one does not want to actually decode data but merely validate that the encoded data
   1807 /// is valid base64url without padding. Since data is not actually decoded, one avoids the need to allocate
   1808 /// a large-enough buffer first.
   1809 ///
   1810 /// # Errors
   1811 ///
   1812 /// Errors iff `input` is an invalid base64url without padding.
   1813 ///
   1814 /// Note since no buffer is used to decode the data into, neither [`DecodeErr::BufferLen`] nor
   1815 /// [`DecodeErr::TryReserve`] will ever be returned.
   1816 ///
   1817 /// # Examples
   1818 ///
   1819 /// ```
   1820 /// # use base64url_nopad::DecodeErr;
   1821 /// base64url_nopad::validate_encoded_data([0; 0].as_slice())?;
   1822 /// assert_eq!(
   1823 ///     base64url_nopad::validate_encoded_data([0; 1].as_slice()).unwrap_err(),
   1824 ///     DecodeErr::EncodedLen
   1825 /// );
   1826 /// assert_eq!(
   1827 ///     base64url_nopad::validate_encoded_data([0; 2].as_slice()).unwrap_err(),
   1828 ///     DecodeErr::InvalidByte
   1829 /// );
   1830 /// assert_eq!(
   1831 ///     base64url_nopad::validate_encoded_data(b"-8").unwrap_err(),
   1832 ///     DecodeErr::TrailingBits
   1833 /// );
   1834 /// base64url_nopad::validate_encoded_data(b"C8Aa_A--91VZbx0")?;
   1835 /// # Ok::<_, DecodeErr>(())
   1836 /// ```
   1837 #[expect(
   1838     clippy::arithmetic_side_effects,
   1839     clippy::indexing_slicing,
   1840     reason = "comments justify correctness"
   1841 )]
   1842 #[inline]
   1843 pub const fn validate_encoded_data(mut input: &[u8]) -> Result<(), DecodeErr> {
   1844     let len = input.len();
   1845     // `len % 4`.
   1846     match len & 3 {
   1847         // `input.len()` is invalid iff it is equivalent to 1 modulo 4 per the proof in
   1848         // `decode_len`.
   1849         1 => return Err(DecodeErr::EncodedLen),
   1850         2 => {
   1851             // We know `input` is not empty; otherwise `len % 4 == 0`.
   1852             if let Some(val) = Alphabet::from_ascii(input[len - 1]) {
   1853                 if val.to_u8().trailing_zeros() < 4 {
   1854                     return Err(DecodeErr::TrailingBits);
   1855                 }
   1856             } else {
   1857                 return Err(DecodeErr::InvalidByte);
   1858             }
   1859         }
   1860         3 => {
   1861             // We know `input` is not empty; otherwise `len % 4 == 0`.
   1862             if let Some(val) = Alphabet::from_ascii(input[len - 1]) {
   1863                 if val.to_u8().trailing_zeros() < 2 {
   1864                     return Err(DecodeErr::TrailingBits);
   1865                 }
   1866             } else {
   1867                 return Err(DecodeErr::InvalidByte);
   1868             }
   1869         }
   1870         // When the input has length that is a multple of 4, then no trailing bits were added and thus
   1871         // all values are possible.
   1872         _ => {}
   1873     }
   1874     while let [first, ref rest @ ..] = *input {
   1875         if Alphabet::from_ascii(first).is_some() {
   1876             input = rest;
   1877         } else {
   1878             return Err(DecodeErr::InvalidByte);
   1879         }
   1880     }
   1881     Ok(())
   1882 }
   1883 /// Same as [`encode_buffer`] except `output` must have the _exact_ length needed to encode `input`, and the
   1884 /// encoded `str` is not returned.
   1885 ///
   1886 /// # Panics
   1887 ///
   1888 /// `panic`s iff `output` does not have the _exact_ length needed to encode `input`.
   1889 ///
   1890 /// # Examples
   1891 ///
   1892 /// ```
   1893 /// let mut buffer = [0; 256];
   1894 /// base64url_nopad::encode_buffer_exact([0; 0].as_slice(), &mut buffer[..0]);
   1895 /// base64url_nopad::encode_buffer_exact([0; 1].as_slice(), &mut buffer[..2]);
   1896 /// assert_eq!(*b"AA", buffer[..2]);
   1897 /// // Uncommenting below will cause a `panic` since the output buffer must be exact.
   1898 /// // base64url_nopad::encode_buffer_exact([255; 1].as_slice(), &mut buffer);
   1899 /// ```
   1900 #[inline]
   1901 pub const fn encode_buffer_exact(input: &[u8], output: &mut [u8]) {
   1902     assert!(
   1903         // `encode_len` won't `panic` since Rust guarantees `input.len()` is at most `isize::MAX`.
   1904         output.len() == encode_len(input.len()),
   1905         "encode_buffer_exact must be passed an output buffer whose length is exactly the length needed to encode the data"
   1906     );
   1907     _ = encode_buffer(input, output);
   1908 }
   1909 /// Same as [`decode_buffer`] except `output` must have the _exact_ length needed, and the decoded `slice`
   1910 /// is not returned.
   1911 ///
   1912 /// # Errors
   1913 ///
   1914 /// Errors iff [`decode_buffer`] errors. Note that since a `panic` occurs when `output.len()` is not the
   1915 /// exact length needed, [`DecodeErr::BufferLen`] is not possible in addition to [`DecodeErr::TryReserve`].
   1916 ///
   1917 /// # Panics
   1918 ///
   1919 /// `panic`s iff `output` does not have the _exact_ length needed to contain the decoded data. Note when `input`
   1920 /// contains an invalid length, [`DecodeErr::EncodedLen`] is returned _not_ a `panic`.
   1921 ///
   1922 /// # Examples
   1923 ///
   1924 /// ```
   1925 /// # use base64url_nopad::DecodeErr;
   1926 /// assert_eq!(
   1927 ///     base64url_nopad::decode_buffer_exact([0; 1].as_slice(), [0; 0].as_mut_slice()).unwrap_err(),
   1928 ///     DecodeErr::EncodedLen
   1929 /// );
   1930 /// assert_eq!(
   1931 ///     base64url_nopad::decode_buffer_exact([0; 2].as_slice(), [0; 1].as_mut_slice()).unwrap_err(),
   1932 ///     DecodeErr::InvalidByte
   1933 /// );
   1934 /// assert_eq!(
   1935 ///     base64url_nopad::decode_buffer_exact(b"-8", [0; 1].as_mut_slice()).unwrap_err(),
   1936 ///     DecodeErr::TrailingBits
   1937 /// );
   1938 /// let mut buffer = [0; base64url_nopad::decode_len(b"C8Aa_A--91VZbx0".len()).unwrap()];
   1939 /// base64url_nopad::decode_buffer(b"C8Aa_A--91VZbx0", &mut buffer)?;
   1940 /// assert_eq!(buffer, [0x0b, 0xc0, 0x1a, 0xfc, 0x0f, 0xbe, 0xf7, b'U', b'Y', b'o', 0x1d]);
   1941 /// // Uncommenting below will cause a `panic` since a larger output buffer than necessary is _not_ OK.
   1942 /// // base64url_nopad::decode_buffer_exact(b"C8Aa_A--91VZbx0", &mut [0; 128])?;
   1943 /// # Ok::<_, DecodeErr>(())
   1944 /// ```
   1945 #[expect(
   1946     clippy::panic_in_result_fn,
   1947     reason = "purpose of this function is to panic when output does not have the exact length needed"
   1948 )]
   1949 #[inline]
   1950 pub const fn decode_buffer_exact(input: &[u8], output: &mut [u8]) -> Result<(), DecodeErr> {
   1951     if let Some(output_len) = decode_len(input.len()) {
   1952         assert!(
   1953             output.len() == output_len,
   1954             "decode_buffer_exact must be passed an output buffer whose length is exactly the length needed to decode the data"
   1955         );
   1956         if let Err(e) = decode_buffer(input, output) {
   1957             Err(e)
   1958         } else {
   1959             Ok(())
   1960         }
   1961     } else {
   1962         Err(DecodeErr::EncodedLen)
   1963     }
   1964 }
   1965 #[cfg(test)]
   1966 mod test {
   1967     use super::MAX_ENCODE_INPUT_LEN;
   1968     #[cfg(feature = "alloc")]
   1969     use alloc::string::String;
   1970     #[cfg(feature = "alloc")]
   1971     use core::fmt;
   1972     use rand::{Rng as _, SeedableRng as _, rngs::SmallRng};
   1973     #[cfg(any(
   1974         target_pointer_width = "16",
   1975         target_pointer_width = "32",
   1976         target_pointer_width = "64",
   1977     ))]
   1978     #[ignore]
   1979     #[test]
   1980     fn encode_decode_len() {
   1981         assert_eq!(MAX_ENCODE_INPUT_LEN, 3 * (usize::MAX.div_ceil(4)) - 1);
   1982         let mut rng = SmallRng::from_os_rng();
   1983         for _ in 0..10_000_000 {
   1984             #[cfg(target_pointer_width = "16")]
   1985             let len = rng.random::<u16>() as usize;
   1986             #[cfg(target_pointer_width = "32")]
   1987             let len = rng.random::<u32>() as usize;
   1988             #[cfg(target_pointer_width = "64")]
   1989             let len = rng.random::<u64>() as usize;
   1990             if len <= MAX_ENCODE_INPUT_LEN {
   1991                 assert!(
   1992                     super::encode_len_checked(len)
   1993                         .is_some_and(|l| super::decode_len(l).is_some_and(|orig| orig == len))
   1994                 );
   1995             } else {
   1996                 assert!(super::encode_len_checked(len).is_none());
   1997             }
   1998         }
   1999         for i in 0..1025 {
   2000             assert!(
   2001                 super::encode_len_checked(i)
   2002                     .is_some_and(|l| super::decode_len(l).is_some_and(|orig| orig == i))
   2003             );
   2004         }
   2005         #[cfg(target_pointer_width = "16")]
   2006         for i in MAX_ENCODE_INPUT_LEN + 1.. {
   2007             assert!(super::encode_len_checked(i).is_none());
   2008         }
   2009         #[cfg(not(target_pointer_width = "16"))]
   2010         for i in MAX_ENCODE_INPUT_LEN + 1..MAX_ENCODE_INPUT_LEN + 1_000_000 {
   2011             assert!(super::encode_len_checked(i).is_none());
   2012         }
   2013         assert!(super::encode_len_checked(usize::MAX).is_none());
   2014         assert!(super::encode_len_checked(MAX_ENCODE_INPUT_LEN).is_some_and(|l| l == usize::MAX));
   2015         for _ in 0..10_000_000 {
   2016             #[cfg(target_pointer_width = "16")]
   2017             let len = rng.random::<u16>() as usize;
   2018             #[cfg(target_pointer_width = "32")]
   2019             let len = rng.random::<u32>() as usize;
   2020             #[cfg(target_pointer_width = "64")]
   2021             let len = rng.random::<u64>() as usize;
   2022             if len % 4 == 1 {
   2023                 assert!(super::decode_len(len).is_none());
   2024             } else {
   2025                 assert!(
   2026                     super::decode_len(len).is_some_and(
   2027                         |l| super::encode_len_checked(l).is_some_and(|orig| orig == len)
   2028                     )
   2029                 );
   2030             }
   2031         }
   2032         for i in 0..1025 {
   2033             if i % 4 == 1 {
   2034                 assert!(super::decode_len(i).is_none());
   2035             } else {
   2036                 assert!(
   2037                     super::decode_len(i).is_some_and(
   2038                         |l| super::encode_len_checked(l).is_some_and(|orig| orig == i)
   2039                     )
   2040                 );
   2041             }
   2042         }
   2043         #[cfg(target_pointer_width = "16")]
   2044         for i in 0..=usize::MAX {
   2045             if i % 4 == 1 {
   2046                 assert!(super::decode_len(i).is_none());
   2047             } else {
   2048                 assert!(super::decode_len(i).is_some_and(|l| super::encode_len_checked(l).is_some_and(|orig| orig == i)));
   2049             }
   2050         }
   2051         #[cfg(not(target_pointer_width = "16"))]
   2052         for i in usize::MAX - 1_000_000..=usize::MAX {
   2053             if i % 4 == 1 {
   2054                 assert!(super::decode_len(i).is_none());
   2055             } else {
   2056                 assert!(super::decode_len(i).is_some_and(|l| super::encode_len_checked(l).is_some_and(|orig| orig == i)));
   2057             }
   2058         }
   2059         assert!(super::decode_len(usize::MAX).is_some_and(|l| l == MAX_ENCODE_INPUT_LEN));
   2060     }
   2061     #[cfg(feature = "alloc")]
   2062     #[test]
   2063     fn encode_write() -> fmt::Result {
   2064         let input = [9; 8192];
   2065         let mut buffer = String::with_capacity(super::encode_len(input.len()));
   2066         let cap = buffer.capacity() as isize;
   2067         let mut write_len;
   2068         for len in 0..input.len() {
   2069             write_len = super::encode_len(len) as isize;
   2070             match write_len.checked_add(buffer.len() as isize) {
   2071                 None => {
   2072                     buffer.clear();
   2073                     super::encode_write(&input[..len], &mut buffer)?;
   2074                     assert_eq!(buffer.len() as isize, write_len);
   2075                 }
   2076                 Some(l) => {
   2077                     if l > cap {
   2078                         buffer.clear();
   2079                         super::encode_write(&input[..len], &mut buffer)?;
   2080                         assert_eq!(buffer.len() as isize, write_len);
   2081                     } else {
   2082                         super::encode_write(&input[..len], &mut buffer)?;
   2083                         assert_eq!(buffer.len() as isize, l);
   2084                     }
   2085                 }
   2086             }
   2087             assert!(
   2088                 buffer
   2089                     .as_bytes()
   2090                     .iter()
   2091                     .all(|b| { matches!(*b, b'C' | b'J' | b'Q' | b'k') })
   2092             );
   2093         }
   2094         Ok(())
   2095     }
   2096 }